/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 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/constfold.c */ #include "root/dsystem.h" // mem{cpy|set|cmp}() #ifndef IN_GCC #include #endif #include "root/rmem.h" #include "root/root.h" #include "root/port.h" #include "errors.h" #include "mtype.h" #include "expression.h" #include "aggregate.h" #include "declaration.h" #include "utf.h" #include "ctfe.h" #include "target.h" int RealEquals(real_t x1, real_t x2); Expression *expType(Type *type, Expression *e) { if (type != e->type) { e = e->copy(); e->type = type; } return e; } /* ================================== isConst() ============================== */ int isConst(Expression *e) { //printf("Expression::isConst(): %s\n", e->toChars()); switch (e->op) { case TOKint64: case TOKfloat64: case TOKcomplex80: return 1; case TOKnull: return 0; case TOKsymoff: return 2; default: return 0; } assert(0); return 0; } /* =============================== constFold() ============================== */ /* The constFold() functions were redundant with the optimize() ones, * and so have been folded in with them. */ /* ========================================================================== */ UnionExp Neg(Type *type, Expression *e1) { UnionExp ue; Loc loc = e1->loc; if (e1->type->isreal()) { new(&ue) RealExp(loc, -e1->toReal(), type); } else if (e1->type->isimaginary()) { new(&ue) RealExp(loc, -e1->toImaginary(), type); } else if (e1->type->iscomplex()) { new(&ue) ComplexExp(loc, -e1->toComplex(), type); } else { new(&ue) IntegerExp(loc, -e1->toInteger(), type); } return ue; } UnionExp Com(Type *type, Expression *e1) { UnionExp ue; Loc loc = e1->loc; new(&ue) IntegerExp(loc, ~e1->toInteger(), type); return ue; } UnionExp Not(Type *type, Expression *e1) { UnionExp ue; Loc loc = e1->loc; new(&ue) IntegerExp(loc, e1->isBool(false) ? 1 : 0, type); return ue; } UnionExp Bool(Type *type, Expression *e1) { UnionExp ue; Loc loc = e1->loc; new(&ue) IntegerExp(loc, e1->isBool(true) ? 1 : 0, type); return ue; } UnionExp Add(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; if (type->isreal()) { new(&ue) RealExp(loc, e1->toReal() + e2->toReal(), type); } else if (type->isimaginary()) { new(&ue) RealExp(loc, e1->toImaginary() + e2->toImaginary(), type); } else if (type->iscomplex()) { // This rigamarole is necessary so that -0.0 doesn't get // converted to +0.0 by doing an extraneous add with +0.0 complex_t c1 = complex_t(CTFloat::zero); real_t r1 = CTFloat::zero; real_t i1 = CTFloat::zero; complex_t c2 = complex_t(CTFloat::zero); real_t r2 = CTFloat::zero; real_t i2 = CTFloat::zero; complex_t v = complex_t(CTFloat::zero); int x; if (e1->type->isreal()) { r1 = e1->toReal(); x = 0; } else if (e1->type->isimaginary()) { i1 = e1->toImaginary(); x = 3; } else { c1 = e1->toComplex(); x = 6; } if (e2->type->isreal()) { r2 = e2->toReal(); } else if (e2->type->isimaginary()) { i2 = e2->toImaginary(); x += 1; } else { c2 = e2->toComplex(); x += 2; } switch (x) { case 0 + 0: v = complex_t(r1 + r2); break; case 0 + 1: v = complex_t(r1, i2); break; case 0 + 2: v = complex_t(r1 + creall(c2), cimagl(c2)); break; case 3 + 0: v = complex_t(r2, i1); break; case 3 + 1: v = complex_t(CTFloat::zero, i1 + i2); break; case 3 + 2: v = complex_t(creall(c2), i1 + cimagl(c2)); break; case 6 + 0: v = complex_t(creall(c1) + r2, cimagl(c2)); break; case 6 + 1: v = complex_t(creall(c1), cimagl(c1) + i2); break; case 6 + 2: v = c1 + c2; break; default: assert(0); } new(&ue) ComplexExp(loc, v, type); } else if (e1->op == TOKsymoff) { SymOffExp *soe = (SymOffExp *)e1; new(&ue) SymOffExp(loc, soe->var, soe->offset + e2->toInteger()); ue.exp()->type = type; } else if (e2->op == TOKsymoff) { SymOffExp *soe = (SymOffExp *)e2; new(&ue) SymOffExp(loc, soe->var, soe->offset + e1->toInteger()); ue.exp()->type = type; } else new(&ue) IntegerExp(loc, e1->toInteger() + e2->toInteger(), type); return ue; } UnionExp Min(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; if (type->isreal()) { new(&ue) RealExp(loc, e1->toReal() - e2->toReal(), type); } else if (type->isimaginary()) { new(&ue) RealExp(loc, e1->toImaginary() - e2->toImaginary(), type); } else if (type->iscomplex()) { // This rigamarole is necessary so that -0.0 doesn't get // converted to +0.0 by doing an extraneous add with +0.0 complex_t c1 = complex_t(CTFloat::zero); real_t r1 = CTFloat::zero; real_t i1 = CTFloat::zero; complex_t c2 = complex_t(CTFloat::zero); real_t r2 = CTFloat::zero; real_t i2 = CTFloat::zero; complex_t v = complex_t(CTFloat::zero); int x; if (e1->type->isreal()) { r1 = e1->toReal(); x = 0; } else if (e1->type->isimaginary()) { i1 = e1->toImaginary(); x = 3; } else { c1 = e1->toComplex(); x = 6; } if (e2->type->isreal()) { r2 = e2->toReal(); } else if (e2->type->isimaginary()) { i2 = e2->toImaginary(); x += 1; } else { c2 = e2->toComplex(); x += 2; } switch (x) { case 0 + 0: v = complex_t(r1 - r2); break; case 0 + 1: v = complex_t(r1, -i2); break; case 0 + 2: v = complex_t(r1 - creall(c2), -cimagl(c2)); break; case 3 + 0: v = complex_t(-r2, i1); break; case 3 + 1: v = complex_t(CTFloat::zero, i1 - i2); break; case 3 + 2: v = complex_t(-creall(c2), i1 - cimagl(c2)); break; case 6 + 0: v = complex_t(creall(c1) - r2, cimagl(c1)); break; case 6 + 1: v = complex_t(creall(c1), cimagl(c1) - i2); break; case 6 + 2: v = c1 - c2; break; default: assert(0); } new(&ue) ComplexExp(loc, v, type); } else if (e1->op == TOKsymoff) { SymOffExp *soe = (SymOffExp *)e1; new(&ue) SymOffExp(loc, soe->var, soe->offset - e2->toInteger()); ue.exp()->type = type; } else { new(&ue) IntegerExp(loc, e1->toInteger() - e2->toInteger(), type); } return ue; } UnionExp Mul(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; if (type->isfloating()) { complex_t c = complex_t(CTFloat::zero); real_t r; if (e1->type->isreal()) { r = e1->toReal(); c = e2->toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } else if (e1->type->isimaginary()) { r = e1->toImaginary(); c = e2->toComplex(); c = complex_t(-r * cimagl(c), r * creall(c)); } else if (e2->type->isreal()) { r = e2->toReal(); c = e1->toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } else if (e2->type->isimaginary()) { r = e2->toImaginary(); c = e1->toComplex(); c = complex_t(-r * cimagl(c), r * creall(c)); } else c = e1->toComplex() * e2->toComplex(); if (type->isreal()) new(&ue) RealExp(loc, creall(c), type); else if (type->isimaginary()) new(&ue) RealExp(loc, cimagl(c), type); else if (type->iscomplex()) new(&ue) ComplexExp(loc, c, type); else assert(0); } else { new(&ue) IntegerExp(loc, e1->toInteger() * e2->toInteger(), type); } return ue; } UnionExp Div(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; if (type->isfloating()) { complex_t c = complex_t(CTFloat::zero); real_t r; //e1->type->print(); //e2->type->print(); if (e2->type->isreal()) { if (e1->type->isreal()) { new(&ue) RealExp(loc, e1->toReal() / e2->toReal(), type); return ue; } r = e2->toReal(); c = e1->toComplex(); c = complex_t(creall(c) / r, cimagl(c) / r); } else if (e2->type->isimaginary()) { r = e2->toImaginary(); c = e1->toComplex(); c = complex_t(cimagl(c) / r, -creall(c) / r); } else { c = e1->toComplex() / e2->toComplex(); } if (type->isreal()) new(&ue) RealExp(loc, creall(c), type); else if (type->isimaginary()) new(&ue) RealExp(loc, cimagl(c), type); else if (type->iscomplex()) new(&ue) ComplexExp(loc, c, type); else assert(0); } else { sinteger_t n1; sinteger_t n2; sinteger_t n; n1 = e1->toInteger(); n2 = e2->toInteger(); if (n2 == 0) { e2->error("divide by 0"); new(&ue) ErrorExp(); return ue; } if (n2 == -1 && !type->isunsigned()) { // Check for int.min / -1 if ((dinteger_t)n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64) { e2->error("integer overflow: int.min / -1"); new(&ue) ErrorExp(); return ue; } else if ((dinteger_t)n1 == 0x8000000000000000LL) // long.min / -1 { e2->error("integer overflow: long.min / -1"); new(&ue) ErrorExp(); return ue; } } if (e1->type->isunsigned() || e2->type->isunsigned()) n = ((dinteger_t) n1) / ((dinteger_t) n2); else n = n1 / n2; new(&ue) IntegerExp(loc, n, type); } return ue; } UnionExp Mod(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; if (type->isfloating()) { complex_t c = complex_t(CTFloat::zero); if (e2->type->isreal()) { real_t r2 = e2->toReal(); #ifdef IN_GCC c = complex_t(e1->toReal() % r2, e1->toImaginary() % r2); #else c = complex_t(::fmodl(e1->toReal(), r2), ::fmodl(e1->toImaginary(), r2)); #endif } else if (e2->type->isimaginary()) { real_t i2 = e2->toImaginary(); #ifdef IN_GCC c = complex_t(e1->toReal() % i2, e1->toImaginary() % i2); #else c = complex_t(::fmodl(e1->toReal(), i2), ::fmodl(e1->toImaginary(), i2)); #endif } else assert(0); if (type->isreal()) new(&ue) RealExp(loc, creall(c), type); else if (type->isimaginary()) new(&ue) RealExp(loc, cimagl(c), type); else if (type->iscomplex()) new(&ue) ComplexExp(loc, c, type); else assert(0); } else { sinteger_t n1; sinteger_t n2; sinteger_t n; n1 = e1->toInteger(); n2 = e2->toInteger(); if (n2 == 0) { e2->error("divide by 0"); new(&ue) ErrorExp(); return ue; } if (n2 == -1 && !type->isunsigned()) { // Check for int.min % -1 if ((dinteger_t)n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64) { e2->error("integer overflow: int.min %% -1"); new(&ue) ErrorExp(); return ue; } else if ((dinteger_t)n1 == 0x8000000000000000LL) // long.min % -1 { e2->error("integer overflow: long.min %% -1"); new(&ue) ErrorExp(); return ue; } } if (e1->type->isunsigned() || e2->type->isunsigned()) n = ((dinteger_t) n1) % ((dinteger_t) n2); else n = n1 % n2; new(&ue) IntegerExp(loc, n, type); } return ue; } UnionExp Pow(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; // Handle integer power operations. if (e2->type->isintegral()) { dinteger_t n = e2->toInteger(); bool neg; if (!e2->type->isunsigned() && (sinteger_t)n < 0) { if (e1->type->isintegral()) { new(&ue) CTFEExp(TOKcantexp); return ue; } // Don't worry about overflow, from now on n is unsigned. neg = true; n = -n; } else neg = false; UnionExp ur, uv; if (e1->type->iscomplex()) { new(&ur) ComplexExp(loc, e1->toComplex(), e1->type); new(&uv) ComplexExp(loc, complex_t(CTFloat::one), e1->type); } else if (e1->type->isfloating()) { new(&ur) RealExp(loc, e1->toReal(), e1->type); new(&uv) RealExp(loc, CTFloat::one, e1->type); } else { new(&ur) IntegerExp(loc, e1->toInteger(), e1->type); new(&uv) IntegerExp(loc, 1, e1->type); } Expression* r = ur.exp(); Expression* v = uv.exp(); while (n != 0) { if (n & 1) { // v = v * r; uv = Mul(loc, v->type, v, r); } n >>= 1; // r = r * r ur = Mul(loc, r->type, r, r); } if (neg) { // ue = 1.0 / v UnionExp one; new(&one) RealExp(loc, CTFloat::one, v->type); uv = Div(loc, v->type, one.exp(), v); } if (type->iscomplex()) new(&ue) ComplexExp(loc, v->toComplex(), type); else if (type->isintegral()) new(&ue) IntegerExp(loc, v->toInteger(), type); else new(&ue) RealExp(loc, v->toReal(), type); } else if (e2->type->isfloating()) { // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN if (e1->toReal() < CTFloat::zero) { new(&ue) RealExp(loc, Target::RealProperties::nan, type); } else new(&ue) CTFEExp(TOKcantexp); } else new(&ue) CTFEExp(TOKcantexp); return ue; } UnionExp Shl(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; new(&ue) IntegerExp(loc, e1->toInteger() << e2->toInteger(), type); return ue; } UnionExp Shr(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; dinteger_t value = e1->toInteger(); dinteger_t dcount = e2->toInteger(); assert(dcount <= 0xFFFFFFFF); unsigned count = (unsigned)dcount; switch (e1->type->toBasetype()->ty) { case Tint8: value = (d_int8)(value) >> count; break; case Tuns8: case Tchar: value = (d_uns8)(value) >> count; break; case Tint16: value = (d_int16)(value) >> count; break; case Tuns16: case Twchar: value = (d_uns16)(value) >> count; break; case Tint32: value = (d_int32)(value) >> count; break; case Tuns32: case Tdchar: value = (d_uns32)(value) >> count; break; case Tint64: value = (d_int64)(value) >> count; break; case Tuns64: value = (d_uns64)(value) >> count; break; case Terror: new(&ue) ErrorExp(); return ue; default: assert(0); } new(&ue) IntegerExp(loc, value, type); return ue; } UnionExp Ushr(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; dinteger_t value = e1->toInteger(); dinteger_t dcount = e2->toInteger(); assert(dcount <= 0xFFFFFFFF); unsigned count = (unsigned)dcount; switch (e1->type->toBasetype()->ty) { case Tint8: case Tuns8: case Tchar: // Possible only with >>>=. >>> always gets promoted to int. value = (value & 0xFF) >> count; break; case Tint16: case Tuns16: case Twchar: // Possible only with >>>=. >>> always gets promoted to int. value = (value & 0xFFFF) >> count; break; case Tint32: case Tuns32: case Tdchar: value = (value & 0xFFFFFFFF) >> count; break; case Tint64: case Tuns64: value = (d_uns64)(value) >> count; break; case Terror: new(&ue) ErrorExp(); return ue; default: assert(0); } new(&ue) IntegerExp(loc, value, type); return ue; } UnionExp And(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; new(&ue) IntegerExp(loc, e1->toInteger() & e2->toInteger(), type); return ue; } UnionExp Or(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; new(&ue) IntegerExp(loc, e1->toInteger() | e2->toInteger(), type); return ue; } UnionExp Xor(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; new(&ue) IntegerExp(loc, e1->toInteger() ^ e2->toInteger(), type); return ue; } /* Also returns TOKcantexp if cannot be computed. */ UnionExp Equal(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; int cmp = 0; real_t r1; real_t r2; //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); assert(op == TOKequal || op == TOKnotequal); if (e1->op == TOKnull) { if (e2->op == TOKnull) cmp = 1; else if (e2->op == TOKstring) { StringExp *es2 = (StringExp *)e2; cmp = (0 == es2->len); } else if (e2->op == TOKarrayliteral) { ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; cmp = !es2->elements || (0 == es2->elements->dim); } else { new(&ue) CTFEExp(TOKcantexp); return ue; } } else if (e2->op == TOKnull) { if (e1->op == TOKstring) { StringExp *es1 = (StringExp *)e1; cmp = (0 == es1->len); } else if (e1->op == TOKarrayliteral) { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; cmp = !es1->elements || (0 == es1->elements->dim); } else { new(&ue) CTFEExp(TOKcantexp); return ue; } } else if (e1->op == TOKstring && e2->op == TOKstring) { StringExp *es1 = (StringExp *)e1; StringExp *es2 = (StringExp *)e2; if (es1->sz != es2->sz) { assert(global.errors); new(&ue) CTFEExp(TOKcantexp); return ue; } if (es1->len == es2->len && memcmp(es1->string, es2->string, es1->sz * es1->len) == 0) cmp = 1; else cmp = 0; } else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral) { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; if ((!es1->elements || !es1->elements->dim) && (!es2->elements || !es2->elements->dim)) cmp = 1; // both arrays are empty else if (!es1->elements || !es2->elements) cmp = 0; else if (es1->elements->dim != es2->elements->dim) cmp = 0; else { for (size_t i = 0; i < es1->elements->dim; i++) { Expression *ee1 = es1->getElement(i); Expression *ee2 = es2->getElement(i); ue = Equal(TOKequal, loc, Type::tint32, ee1, ee2); if (CTFEExp::isCantExp(ue.exp())) return ue; cmp = (int)ue.exp()->toInteger(); if (cmp == 0) break; } } } else if (e1->op == TOKarrayliteral && e2->op == TOKstring) { // Swap operands and use common code Expression *etmp = e1; e1 = e2; e2 = etmp; goto Lsa; } else if (e1->op == TOKstring && e2->op == TOKarrayliteral) { Lsa: StringExp *es1 = (StringExp *)e1; ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; size_t dim1 = es1->len; size_t dim2 = es2->elements ? es2->elements->dim : 0; if (dim1 != dim2) cmp = 0; else { cmp = 1; // if dim1 winds up being 0 for (size_t i = 0; i < dim1; i++) { uinteger_t c = es1->charAt(i); Expression *ee2 = es2->getElement(i); if (ee2->isConst() != 1) { new(&ue) CTFEExp(TOKcantexp); return ue; } cmp = (c == ee2->toInteger()); if (cmp == 0) break; } } } else if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) { StructLiteralExp *es1 = (StructLiteralExp *)e1; StructLiteralExp *es2 = (StructLiteralExp *)e2; if (es1->sd != es2->sd) cmp = 0; else if ((!es1->elements || !es1->elements->dim) && (!es2->elements || !es2->elements->dim)) cmp = 1; // both arrays are empty else if (!es1->elements || !es2->elements) cmp = 0; else if (es1->elements->dim != es2->elements->dim) cmp = 0; else { cmp = 1; for (size_t i = 0; i < es1->elements->dim; i++) { Expression *ee1 = (*es1->elements)[i]; Expression *ee2 = (*es2->elements)[i]; if (ee1 == ee2) continue; if (!ee1 || !ee2) { cmp = 0; break; } ue = Equal(TOKequal, loc, Type::tint32, ee1, ee2); if (ue.exp()->op == TOKcantexp) return ue; cmp = (int)ue.exp()->toInteger(); if (cmp == 0) break; } } } else if (e1->isConst() != 1 || e2->isConst() != 1) { new(&ue) CTFEExp(TOKcantexp); return ue; } else if (e1->type->isreal()) { r1 = e1->toReal(); r2 = e2->toReal(); goto L1; } else if (e1->type->isimaginary()) { r1 = e1->toImaginary(); r2 = e2->toImaginary(); L1: if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered { cmp = 0; } else { cmp = (r1 == r2); } } else if (e1->type->iscomplex()) { cmp = e1->toComplex() == e2->toComplex(); } else if (e1->type->isintegral() || e1->type->toBasetype()->ty == Tpointer) { cmp = (e1->toInteger() == e2->toInteger()); } else { new(&ue) CTFEExp(TOKcantexp); return ue; } if (op == TOKnotequal) cmp ^= 1; new(&ue) IntegerExp(loc, cmp, type); return ue; } UnionExp Identity(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; int cmp; if (e1->op == TOKnull) { cmp = (e2->op == TOKnull); } else if (e2->op == TOKnull) { cmp = 0; } else if (e1->op == TOKsymoff && e2->op == TOKsymoff) { SymOffExp *es1 = (SymOffExp *)e1; SymOffExp *es2 = (SymOffExp *)e2; cmp = (es1->var == es2->var && es1->offset == es2->offset); } else { if (e1->type->isreal()) { cmp = RealEquals(e1->toReal(), e2->toReal()); } else if (e1->type->isimaginary()) { cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); } else if (e1->type->iscomplex()) { complex_t v1 = e1->toComplex(); complex_t v2 = e2->toComplex(); cmp = RealEquals(creall(v1), creall(v2)) && RealEquals(cimagl(v1), cimagl(v1)); } else { ue = Equal((op == TOKidentity) ? TOKequal : TOKnotequal, loc, type, e1, e2); return ue; } } if (op == TOKnotidentity) cmp ^= 1; new(&ue) IntegerExp(loc, cmp, type); return ue; } UnionExp Cmp(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; dinteger_t n; real_t r1; real_t r2; //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); if (e1->op == TOKstring && e2->op == TOKstring) { StringExp *es1 = (StringExp *)e1; StringExp *es2 = (StringExp *)e2; size_t sz = es1->sz; assert(sz == es2->sz); size_t len = es1->len; if (es2->len < len) len = es2->len; int rawCmp = memcmp(es1->string, es2->string, sz * len); if (rawCmp == 0) rawCmp = (int)(es1->len - es2->len); n = specificCmp(op, rawCmp); } else if (e1->isConst() != 1 || e2->isConst() != 1) { new(&ue) CTFEExp(TOKcantexp); return ue; } else if (e1->type->isreal()) { r1 = e1->toReal(); r2 = e2->toReal(); goto L1; } else if (e1->type->isimaginary()) { r1 = e1->toImaginary(); r2 = e2->toImaginary(); L1: n = realCmp(op, r1, r2); } else if (e1->type->iscomplex()) { assert(0); } else { sinteger_t n1; sinteger_t n2; n1 = e1->toInteger(); n2 = e2->toInteger(); if (e1->type->isunsigned() || e2->type->isunsigned()) n = intUnsignedCmp(op, n1, n2); else n = intSignedCmp(op, n1, n2); } new(&ue) IntegerExp(loc, n, type); return ue; } /* Also returns TOKcantexp if cannot be computed. * to: type to cast to * type: type to paint the result */ UnionExp Cast(Loc loc, Type *type, Type *to, Expression *e1) { UnionExp ue; Type *tb = to->toBasetype(); Type *typeb = type->toBasetype(); //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars()); //printf("\te1->type = %s\n", e1->type->toChars()); if (e1->type->equals(type) && type->equals(to)) { new(&ue) UnionExp(e1); return ue; } if (e1->op == TOKvector && ((TypeVector *)e1->type)->basetype->equals(type) && type->equals(to)) { Expression *ex = ((VectorExp *)e1)->e1; new(&ue) UnionExp(ex); return ue; } if (e1->type->implicitConvTo(to) >= MATCHconst || to->implicitConvTo(e1->type) >= MATCHconst) { goto L1; } // Allow covariant converions of delegates // (Perhaps implicit conversion from pure to impure should be a MATCHconst, // then we wouldn't need this extra check.) if (e1->type->toBasetype()->ty == Tdelegate && e1->type->implicitConvTo(to) == MATCHconvert) { goto L1; } /* Allow casting from one string type to another */ if (e1->op == TOKstring) { if (tb->ty == Tarray && typeb->ty == Tarray && tb->nextOf()->size() == typeb->nextOf()->size()) { goto L1; } } if (e1->op == TOKarrayliteral && typeb == tb) { L1: Expression *ex = expType(to, e1); new(&ue) UnionExp(ex); return ue; } if (e1->isConst() != 1) { new(&ue) CTFEExp(TOKcantexp); } else if (tb->ty == Tbool) { new(&ue) IntegerExp(loc, e1->toInteger() != 0, type); } else if (type->isintegral()) { if (e1->type->isfloating()) { dinteger_t result; real_t r = e1->toReal(); switch (typeb->ty) { case Tint8: result = (d_int8)(sinteger_t)r; break; case Tchar: case Tuns8: result = (d_uns8)(dinteger_t)r; break; case Tint16: result = (d_int16)(sinteger_t)r; break; case Twchar: case Tuns16: result = (d_uns16)(dinteger_t)r; break; case Tint32: result = (d_int32)r; break; case Tdchar: case Tuns32: result = (d_uns32)r; break; case Tint64: result = (d_int64)r; break; case Tuns64: result = (d_uns64)r; break; default: assert(0); } new(&ue) IntegerExp(loc, result, type); } else if (type->isunsigned()) new(&ue) IntegerExp(loc, e1->toUInteger(), type); else new(&ue) IntegerExp(loc, e1->toInteger(), type); } else if (tb->isreal()) { real_t value = e1->toReal(); new(&ue) RealExp(loc, value, type); } else if (tb->isimaginary()) { real_t value = e1->toImaginary(); new(&ue) RealExp(loc, value, type); } else if (tb->iscomplex()) { complex_t value = e1->toComplex(); new(&ue) ComplexExp(loc, value, type); } else if (tb->isscalar()) { new(&ue) IntegerExp(loc, e1->toInteger(), type); } else if (tb->ty == Tvoid) { new(&ue) CTFEExp(TOKcantexp); } else if (tb->ty == Tstruct && e1->op == TOKint64) { // Struct = 0; StructDeclaration *sd = tb->toDsymbol(NULL)->isStructDeclaration(); assert(sd); Expressions *elements = new Expressions; for (size_t i = 0; i < sd->fields.dim; i++) { VarDeclaration *v = sd->fields[i]; UnionExp zero; new(&zero) IntegerExp(0); ue = Cast(loc, v->type, v->type, zero.exp()); if (ue.exp()->op == TOKcantexp) return ue; elements->push(ue.exp()->copy()); } new(&ue) StructLiteralExp(loc, sd, elements); ue.exp()->type = type; } else { if (type != Type::terror) { // have to change to Internal Compiler Error // all invalid casts should be handled already in Expression::castTo(). error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars()); } new(&ue) ErrorExp(); } return ue; } UnionExp ArrayLength(Type *type, Expression *e1) { UnionExp ue; Loc loc = e1->loc; if (e1->op == TOKstring) { StringExp *es1 = (StringExp *)e1; new(&ue) IntegerExp(loc, es1->len, type); } else if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; size_t dim = ale->elements ? ale->elements->dim : 0; new(&ue) IntegerExp(loc, dim, type); } else if (e1->op == TOKassocarrayliteral) { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e1; size_t dim = ale->keys->dim; new(&ue) IntegerExp(loc, dim, type); } else if (e1->type->toBasetype()->ty == Tsarray) { Expression *e = ((TypeSArray *)e1->type->toBasetype())->dim; new(&ue) UnionExp(e); } else new(&ue) CTFEExp(TOKcantexp); return ue; } /* Also return TOKcantexp if this fails */ UnionExp Index(Type *type, Expression *e1, Expression *e2) { UnionExp ue; Loc loc = e1->loc; //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); assert(e1->type); if (e1->op == TOKstring && e2->op == TOKint64) { StringExp *es1 = (StringExp *)e1; uinteger_t i = e2->toInteger(); if (i >= es1->len) { e1->error("string index %llu is out of bounds [0 .. %llu]", i, (ulonglong)es1->len); new(&ue) ErrorExp(); } else { new(&ue) IntegerExp(loc, es1->charAt(i), type); } } else if (e1->type->toBasetype()->ty == Tsarray && e2->op == TOKint64) { TypeSArray *tsa = (TypeSArray *)e1->type->toBasetype(); uinteger_t length = tsa->dim->toInteger(); uinteger_t i = e2->toInteger(); if (i >= length) { e1->error("array index %llu is out of bounds %s[0 .. %llu]", i, e1->toChars(), length); new(&ue) ErrorExp(); } else if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; Expression *e = ale->getElement((size_t)i); e->type = type; e->loc = loc; if (hasSideEffect(e)) new(&ue) CTFEExp(TOKcantexp); else new(&ue) UnionExp(e); } else new(&ue) CTFEExp(TOKcantexp); } else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64) { uinteger_t i = e2->toInteger(); if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; if (i >= ale->elements->dim) { e1->error("array index %llu is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); new(&ue) ErrorExp(); } else { Expression *e = ale->getElement((size_t)i); e->type = type; e->loc = loc; if (hasSideEffect(e)) new(&ue) CTFEExp(TOKcantexp); else new(&ue) UnionExp(e); } } else new(&ue) CTFEExp(TOKcantexp); } else if (e1->op == TOKassocarrayliteral) { AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1; /* Search the keys backwards, in case there are duplicate keys */ for (size_t i = ae->keys->dim; i;) { i--; Expression *ekey = (*ae->keys)[i]; ue = Equal(TOKequal, loc, Type::tbool, ekey, e2); if (CTFEExp::isCantExp(ue.exp())) return ue; if (ue.exp()->isBool(true)) { Expression *e = (*ae->values)[i]; e->type = type; e->loc = loc; if (hasSideEffect(e)) new(&ue) CTFEExp(TOKcantexp); else new(&ue) UnionExp(e); return ue; } } new(&ue) CTFEExp(TOKcantexp); } else new(&ue) CTFEExp(TOKcantexp); return ue; } /* Also return TOKcantexp if this fails */ UnionExp Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) { UnionExp ue; Loc loc = e1->loc; if (e1->op == TOKstring && lwr->op == TOKint64 && upr->op == TOKint64) { StringExp *es1 = (StringExp *)e1; uinteger_t ilwr = lwr->toInteger(); uinteger_t iupr = upr->toInteger(); if (iupr > es1->len || ilwr > iupr) { e1->error("string slice [%llu .. %llu] is out of bounds", ilwr, iupr); new(&ue) ErrorExp(); } else { size_t len = (size_t)(iupr - ilwr); unsigned char sz = es1->sz; void *s = mem.xmalloc((len + 1) * sz); memcpy((char *)s, (char *)es1->string + ilwr * sz, len * sz); memset((char *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len, es1->postfix); StringExp *es = (StringExp *)ue.exp(); es->sz = sz; es->committed = es1->committed; es->type = type; } } else if (e1->op == TOKarrayliteral && lwr->op == TOKint64 && upr->op == TOKint64 && !hasSideEffect(e1)) { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; uinteger_t ilwr = lwr->toInteger(); uinteger_t iupr = upr->toInteger(); if (iupr > es1->elements->dim || ilwr > iupr) { e1->error("array slice [%llu .. %llu] is out of bounds", ilwr, iupr); new(&ue) ErrorExp(); } else { Expressions *elements = new Expressions(); elements->setDim((size_t)(iupr - ilwr)); memcpy(elements->tdata(), es1->elements->tdata() + ilwr, (size_t)(iupr - ilwr) * sizeof((*es1->elements)[0])); new(&ue) ArrayLiteralExp(e1->loc, type, elements); } } else new(&ue) CTFEExp(TOKcantexp); assert(ue.exp()->type); return ue; } /* Set a slice of char/integer array literal 'existingAE' from a string 'newval'. * existingAE[firstIndex..firstIndex+newval.length] = newval. */ void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex) { size_t newlen = newval->len; size_t sz = newval->sz; void *s = newval->string; Type *elemType = existingAE->type->nextOf(); for (size_t j = 0; j < newlen; j++) { dinteger_t val; switch (sz) { case 1: val = (( utf8_t *)s)[j]; break; case 2: val = ((utf16_t *)s)[j]; break; case 4: val = ((utf32_t *)s)[j]; break; default: assert(0); break; } (*existingAE->elements)[j + firstIndex] = new IntegerExp(newval->loc, val, elemType); } } /* Set a slice of string 'existingSE' from a char array literal 'newae'. * existingSE[firstIndex..firstIndex+newae.length] = newae. */ void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex) { void *s = existingSE->string; for (size_t j = 0; j < newae->elements->dim; j++) { unsigned val = (unsigned)newae->getElement(j)->toInteger(); switch (existingSE->sz) { case 1: (( utf8_t *)s)[j + firstIndex] = ( utf8_t)val; break; case 2: ((utf16_t *)s)[j + firstIndex] = (utf16_t)val; break; case 4: ((utf32_t *)s)[j + firstIndex] = (utf32_t)val; break; default: assert(0); break; } } } /* Set a slice of string 'existingSE' from a string 'newstr'. * existingSE[firstIndex..firstIndex+newstr.length] = newstr. */ void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex) { void *s = existingSE->string; size_t sz = existingSE->sz; assert(sz == newstr->sz); memcpy((char *)s + firstIndex * sz, newstr->string, sz * newstr->len); } /* Compare a string slice with another string slice. * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len]) */ int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len) { void *s1 = se1->string; void *s2 = se2->string; size_t sz = se1->sz; assert(sz == se2->sz); return memcmp((char *)s1 + sz * lo1, (char *)s2 + sz * lo2, sz * len); } /* Compare a string slice with an array literal slice * Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len]) */ int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len) { void *s = se1->string; size_t sz = se1->sz; for (size_t j = 0; j < len; j++) { unsigned val2 = (unsigned)ae2->getElement(j + lo2)->toInteger(); unsigned val1; switch (sz) { case 1: val1 = (( utf8_t *)s)[j + lo1]; break; case 2: val1 = ((utf16_t *)s)[j + lo1]; break; case 4: val1 = ((utf32_t *)s)[j + lo1]; break; default: assert(0); break; } int c = val1 - val2; if (c) return c; } return 0; } /* Also return TOKcantexp if this fails */ UnionExp Cat(Type *type, Expression *e1, Expression *e2) { UnionExp ue; Expression *e = CTFEExp::cantexp; Loc loc = e1->loc; Type *t; Type *t1 = e1->type->toBasetype(); Type *t2 = e2->type->toBasetype(); //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars()); //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars()); if (e1->op == TOKnull && (e2->op == TOKint64 || e2->op == TOKstructliteral)) { e = e2; t = t1; goto L2; } else if ((e1->op == TOKint64 || e1->op == TOKstructliteral) && e2->op == TOKnull) { e = e1; t = t2; L2: Type *tn = e->type->toBasetype(); if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) { // Create a StringExp if (t->nextOf()) t = t->nextOf()->toBasetype(); unsigned char sz = (unsigned char)t->size(); dinteger_t v = e->toInteger(); size_t len = (t->ty == tn->ty) ? 1 : utf_codeLength(sz, (dchar_t)v); void *s = mem.xmalloc((len + 1) * sz); if (t->ty == tn->ty) Port::valcpy(s, v, sz); else utf_encode(sz, s, (dchar_t)v); // Add terminating 0 memset((char *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); StringExp *es = (StringExp *)ue.exp(); es->type = type; es->sz = sz; es->committed = 1; } else { // Create an ArrayLiteralExp Expressions *elements = new Expressions(); elements->push(e); new(&ue) ArrayLiteralExp(e->loc, type, elements); } assert(ue.exp()->type); return ue; } else if (e1->op == TOKnull && e2->op == TOKnull) { if (type == e1->type) { // Handle null ~= null if (t1->ty == Tarray && t2 == t1->nextOf()) { new(&ue) ArrayLiteralExp(e1->loc, type, e2); assert(ue.exp()->type); return ue; } else { new(&ue) UnionExp(e1); assert(ue.exp()->type); return ue; } } if (type == e2->type) { new(&ue) UnionExp(e2); assert(ue.exp()->type); return ue; } new(&ue) NullExp(e1->loc, type); assert(ue.exp()->type); return ue; } else if (e1->op == TOKstring && e2->op == TOKstring) { // Concatenate the strings StringExp *es1 = (StringExp *)e1; StringExp *es2 = (StringExp *)e2; size_t len = es1->len + es2->len; unsigned char sz = es1->sz; if (sz != es2->sz) { /* Can happen with: * auto s = "foo"d ~ "bar"c; */ assert(global.errors); new(&ue) CTFEExp(TOKcantexp); assert(ue.exp()->type); return ue; } void *s = mem.xmalloc((len + 1) * sz); memcpy((char *)s, es1->string, es1->len * sz); memcpy((char *)s + es1->len * sz, es2->string, es2->len * sz); // Add terminating 0 memset((char *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); StringExp *es = (StringExp *)ue.exp(); es->sz = sz; es->committed = es1->committed | es2->committed; es->type = type; assert(ue.exp()->type); return ue; } else if (e2->op == TOKstring && e1->op == TOKarrayliteral && t1->nextOf()->isintegral()) { // [chars] ~ string --> [chars] StringExp *es = (StringExp *)e2; ArrayLiteralExp *ea = (ArrayLiteralExp *)e1; size_t len = es->len + ea->elements->dim; Expressions * elems = new Expressions; elems->setDim(len); for (size_t i= 0; i < ea->elements->dim; ++i) { (*elems)[i] = ea->getElement(i); } new(&ue) ArrayLiteralExp(e1->loc, type, elems); ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp(); sliceAssignArrayLiteralFromString(dest, es, ea->elements->dim); assert(ue.exp()->type); return ue; } else if (e1->op == TOKstring && e2->op == TOKarrayliteral && t2->nextOf()->isintegral()) { // string ~ [chars] --> [chars] StringExp *es = (StringExp *)e1; ArrayLiteralExp *ea = (ArrayLiteralExp *)e2; size_t len = es->len + ea->elements->dim; Expressions * elems = new Expressions; elems->setDim(len); for (size_t i= 0; i < ea->elements->dim; ++i) { (*elems)[es->len + i] = ea->getElement(i); } new(&ue) ArrayLiteralExp(e1->loc, type, elems); ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp(); sliceAssignArrayLiteralFromString(dest, es, 0); assert(ue.exp()->type); return ue; } else if (e1->op == TOKstring && e2->op == TOKint64) { // string ~ char --> string StringExp *es1 = (StringExp *)e1; StringExp *es; unsigned char sz = es1->sz; dinteger_t v = e2->toInteger(); // Is it a concatentation of homogenous types? // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar) bool homoConcat = (sz == t2->size()); size_t len = es1->len; len += homoConcat ? 1 : utf_codeLength(sz, (dchar_t)v); void *s = mem.xmalloc((len + 1) * sz); memcpy(s, es1->string, es1->len * sz); if (homoConcat) Port::valcpy((char *)s + (sz * es1->len), v, sz); else utf_encode(sz, (char *)s + (sz * es1->len), (dchar_t)v); // Add terminating 0 memset((char *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); es = (StringExp *)ue.exp(); es->sz = sz; es->committed = es1->committed; es->type = type; assert(ue.exp()->type); return ue; } else if (e1->op == TOKint64 && e2->op == TOKstring) { // [w|d]?char ~ string --> string // We assume that we only ever prepend one char of the same type // (wchar,dchar) as the string's characters. StringExp *es2 = (StringExp *)e2; size_t len = 1 + es2->len; unsigned char sz = es2->sz; dinteger_t v = e1->toInteger(); void *s = mem.xmalloc((len + 1) * sz); Port::valcpy((char *)s, v, sz); memcpy((char *)s + sz, es2->string, es2->len * sz); // Add terminating 0 memset((char *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); StringExp *es = (StringExp *)ue.exp(); es->sz = sz; es->committed = es2->committed; es->type = type; assert(ue.exp()->type); return ue; } else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral && t1->nextOf()->equals(t2->nextOf())) { // Concatenate the arrays Expressions *elems = ArrayLiteralExp::copyElements(e1, e2); new(&ue) ArrayLiteralExp(e1->loc, NULL, elems); e = ue.exp(); if (type->toBasetype()->ty == Tsarray) { e->type = t1->nextOf()->sarrayOf(elems->dim); } else e->type = type; assert(ue.exp()->type); return ue; } else if (e1->op == TOKarrayliteral && e2->op == TOKnull && t1->nextOf()->equals(t2->nextOf())) { e = e1; goto L3; } else if (e1->op == TOKnull && e2->op == TOKarrayliteral && t1->nextOf()->equals(t2->nextOf())) { e = e2; L3: // Concatenate the array with null Expressions *elems = ArrayLiteralExp::copyElements(e); new(&ue) ArrayLiteralExp(e->loc, NULL, elems); e = ue.exp(); if (type->toBasetype()->ty == Tsarray) { e->type = t1->nextOf()->sarrayOf(elems->dim); } else e->type = type; assert(ue.exp()->type); return ue; } else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) && e1->type->toBasetype()->nextOf() && e1->type->toBasetype()->nextOf()->equals(e2->type)) { Expressions *elems = (e1->op == TOKarrayliteral) ? ArrayLiteralExp::copyElements(e1) : new Expressions(); elems->push(e2); new(&ue) ArrayLiteralExp(e1->loc, NULL, elems); e = ue.exp(); if (type->toBasetype()->ty == Tsarray) { e->type = e2->type->sarrayOf(elems->dim); } else e->type = type; assert(ue.exp()->type); return ue; } else if (e2->op == TOKarrayliteral && e2->type->toBasetype()->nextOf()->equals(e1->type)) { Expressions *elems = ArrayLiteralExp::copyElements(e1, e2); new(&ue) ArrayLiteralExp(e2->loc, NULL, elems); e = ue.exp(); if (type->toBasetype()->ty == Tsarray) { e->type = e1->type->sarrayOf(elems->dim); } else e->type = type; assert(ue.exp()->type); return ue; } else if (e1->op == TOKnull && e2->op == TOKstring) { t = e1->type; e = e2; goto L1; } else if (e1->op == TOKstring && e2->op == TOKnull) { e = e1; t = e2->type; L1: Type *tb = t->toBasetype(); if (tb->ty == Tarray && tb->nextOf()->equivalent(e->type)) { Expressions *expressions = new Expressions(); expressions->push(e); new(&ue) ArrayLiteralExp(loc, t, expressions); e = ue.exp(); } else { new(&ue) UnionExp(e); e = ue.exp(); } if (!e->type->equals(type)) { StringExp *se = (StringExp *)e->copy(); e = se->castTo(NULL, type); new(&ue) UnionExp(e); e = ue.exp(); } } else new(&ue) CTFEExp(TOKcantexp); assert(ue.exp()->type); return ue; } UnionExp Ptr(Type *type, Expression *e1) { //printf("Ptr(e1 = %s)\n", e1->toChars()); UnionExp ue; if (e1->op == TOKadd) { AddExp *ae = (AddExp *)e1; if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) { AddrExp *ade = (AddrExp *)ae->e1; if (ade->e1->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)ade->e1; unsigned offset = (unsigned)ae->e2->toInteger(); Expression *e = se->getField(type, offset); if (e) { new(&ue) UnionExp(e); return ue; } } } } new(&ue) CTFEExp(TOKcantexp); return ue; }