| /** |
| * Perform constant folding of arithmetic expressions. |
| * |
| * The routines in this module are called from `optimize.d`. |
| * |
| * Specification: $(LINK2 https://dlang.org/spec/float.html#fp_const_folding, Floating Point Constant Folding) |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d, _constfold.d) |
| * Documentation: https://dlang.org/phobos/dmd_constfold.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/constfold.d |
| */ |
| |
| module dmd.constfold; |
| |
| import core.stdc.string; |
| import core.stdc.stdio; |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.ctfeexpr; |
| import dmd.declaration; |
| import dmd.dstruct; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.globals; |
| import dmd.mtype; |
| import dmd.root.complex; |
| import dmd.root.ctfloat; |
| import dmd.root.port; |
| import dmd.root.rmem; |
| import dmd.root.utf; |
| import dmd.sideeffect; |
| import dmd.target; |
| import dmd.tokens; |
| |
| private enum LOG = false; |
| |
| private Expression expType(Type type, Expression e) |
| { |
| if (type != e.type) |
| { |
| e = e.copy(); |
| e.type = type; |
| } |
| return e; |
| } |
| |
| /************************************ |
| * Returns: |
| * true if e is a constant |
| */ |
| int isConst(Expression e) |
| { |
| //printf("Expression::isConst(): %s\n", e.toChars()); |
| switch (e.op) |
| { |
| case EXP.int64: |
| case EXP.float64: |
| case EXP.complex80: |
| return 1; |
| case EXP.null_: |
| return 0; |
| case EXP.symbolOffset: |
| return 2; |
| default: |
| return 0; |
| } |
| assert(0); |
| } |
| |
| /********************************** |
| * Initialize a EXP.cantExpression Expression. |
| * Params: |
| * ue = where to write it |
| */ |
| void cantExp(out UnionExp ue) |
| { |
| emplaceExp!(CTFEExp)(&ue, EXP.cantExpression); |
| } |
| |
| /* =============================== 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 = void; |
| Loc loc = e1.loc; |
| if (e1.type.isreal()) |
| { |
| emplaceExp!(RealExp)(&ue, loc, -e1.toReal(), type); |
| } |
| else if (e1.type.isimaginary()) |
| { |
| emplaceExp!(RealExp)(&ue, loc, -e1.toImaginary(), type); |
| } |
| else if (e1.type.iscomplex()) |
| { |
| emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type); |
| } |
| else |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, -e1.toInteger(), type); |
| } |
| return ue; |
| } |
| |
| UnionExp Com(Type type, Expression e1) |
| { |
| UnionExp ue = void; |
| Loc loc = e1.loc; |
| emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type); |
| return ue; |
| } |
| |
| UnionExp Not(Type type, Expression e1) |
| { |
| UnionExp ue = void; |
| Loc loc = e1.loc; |
| // BUG: Should be replaced with e1.toBool().get(), but this is apparently |
| // executed for some expressions that cannot be const-folded |
| // To be fixed in another PR |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toBool().hasValue(false) ? 1 : 0, type); |
| return ue; |
| } |
| |
| UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| static if (LOG) |
| { |
| printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); |
| } |
| if (type.isreal()) |
| { |
| emplaceExp!(RealExp)(&ue, loc, e1.toReal() + e2.toReal(), type); |
| } |
| else if (type.isimaginary()) |
| { |
| emplaceExp!(RealExp)(&ue, 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 |
| auto c1 = complex_t(CTFloat.zero); |
| real_t r1 = CTFloat.zero; |
| real_t i1 = CTFloat.zero; |
| auto c2 = complex_t(CTFloat.zero); |
| real_t r2 = CTFloat.zero; |
| real_t i2 = CTFloat.zero; |
| auto 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); |
| } |
| emplaceExp!(ComplexExp)(&ue, loc, v, type); |
| } |
| else if (SymOffExp soe = e1.isSymOffExp()) |
| { |
| emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e2.toInteger()); |
| ue.exp().type = type; |
| } |
| else if (SymOffExp soe = e2.isSymOffExp()) |
| { |
| emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e1.toInteger()); |
| ue.exp().type = type; |
| } |
| else |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() + e2.toInteger(), type); |
| return ue; |
| } |
| |
| UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| // Compute e1-e2 as e1+(-e2) |
| UnionExp neg = Neg(e2.type, e2); |
| UnionExp ue = Add(loc, type, e1, neg.exp()); |
| return ue; |
| } |
| |
| UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| if (type.isfloating()) |
| { |
| auto c = complex_t(CTFloat.zero); |
| real_t r = CTFloat.zero; |
| 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()) |
| emplaceExp!(RealExp)(&ue, loc, creall(c), type); |
| else if (type.isimaginary()) |
| emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); |
| else if (type.iscomplex()) |
| emplaceExp!(ComplexExp)(&ue, loc, c, type); |
| else |
| assert(0); |
| } |
| else |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() * e2.toInteger(), type); |
| } |
| return ue; |
| } |
| |
| UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| if (type.isfloating()) |
| { |
| auto c = complex_t(CTFloat.zero); |
| if (e2.type.isreal()) |
| { |
| if (e1.type.isreal()) |
| { |
| emplaceExp!(RealExp)(&ue, loc, e1.toReal() / e2.toReal(), type); |
| return ue; |
| } |
| const r = e2.toReal(); |
| c = e1.toComplex(); |
| c = complex_t(creall(c) / r, cimagl(c) / r); |
| } |
| else if (e2.type.isimaginary()) |
| { |
| const r = e2.toImaginary(); |
| c = e1.toComplex(); |
| c = complex_t(cimagl(c) / r, -creall(c) / r); |
| } |
| else |
| { |
| c = e1.toComplex() / e2.toComplex(); |
| } |
| |
| if (type.isreal()) |
| emplaceExp!(RealExp)(&ue, loc, creall(c), type); |
| else if (type.isimaginary()) |
| emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); |
| else if (type.iscomplex()) |
| emplaceExp!(ComplexExp)(&ue, 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"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| if (n2 == -1 && !type.isunsigned()) |
| { |
| // Check for int.min / -1 |
| if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) |
| { |
| e2.error("integer overflow: `int.min / -1`"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| else if (n1 == 0x8000000000000000L) // long.min / -1 |
| { |
| e2.error("integer overflow: `long.min / -1L`"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| } |
| if (e1.type.isunsigned() || e2.type.isunsigned()) |
| n = (cast(dinteger_t)n1) / (cast(dinteger_t)n2); |
| else |
| n = n1 / n2; |
| emplaceExp!(IntegerExp)(&ue, loc, n, type); |
| } |
| return ue; |
| } |
| |
| UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| if (type.isfloating()) |
| { |
| auto c = complex_t(CTFloat.zero); |
| if (e2.type.isreal()) |
| { |
| const r2 = e2.toReal(); |
| c = complex_t(e1.toReal() % r2, e1.toImaginary() % r2); |
| } |
| else if (e2.type.isimaginary()) |
| { |
| const i2 = e2.toImaginary(); |
| c = complex_t(e1.toReal() % i2, e1.toImaginary() % i2); |
| } |
| else |
| assert(0); |
| if (type.isreal()) |
| emplaceExp!(RealExp)(&ue, loc, creall(c), type); |
| else if (type.isimaginary()) |
| emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); |
| else if (type.iscomplex()) |
| emplaceExp!(ComplexExp)(&ue, 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"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| if (n2 == -1 && !type.isunsigned()) |
| { |
| // Check for int.min % -1 |
| if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) |
| { |
| e2.error("integer overflow: `int.min %% -1`"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| else if (n1 == 0x8000000000000000L) // long.min % -1 |
| { |
| e2.error("integer overflow: `long.min %% -1L`"); |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| } |
| } |
| if (e1.type.isunsigned() || e2.type.isunsigned()) |
| n = (cast(dinteger_t)n1) % (cast(dinteger_t)n2); |
| else |
| n = n1 % n2; |
| emplaceExp!(IntegerExp)(&ue, loc, n, type); |
| } |
| return ue; |
| } |
| |
| UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| //printf("Pow()\n"); |
| UnionExp ue; |
| // Handle integer power operations. |
| if (e2.type.isintegral()) |
| { |
| dinteger_t n = e2.toInteger(); |
| bool neg; |
| if (!e2.type.isunsigned() && cast(sinteger_t)n < 0) |
| { |
| if (e1.type.isintegral()) |
| { |
| cantExp(ue); |
| 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()) |
| { |
| emplaceExp!(ComplexExp)(&ur, loc, e1.toComplex(), e1.type); |
| emplaceExp!(ComplexExp)(&uv, loc, complex_t(CTFloat.one), e1.type); |
| } |
| else if (e1.type.isfloating()) |
| { |
| emplaceExp!(RealExp)(&ur, loc, e1.toReal(), e1.type); |
| emplaceExp!(RealExp)(&uv, loc, CTFloat.one, e1.type); |
| } |
| else |
| { |
| emplaceExp!(IntegerExp)(&ur, loc, e1.toInteger(), e1.type); |
| emplaceExp!(IntegerExp)(&uv, 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; |
| emplaceExp!(RealExp)(&one, loc, CTFloat.one, v.type); |
| uv = Div(loc, v.type, one.exp(), v); |
| } |
| if (type.iscomplex()) |
| emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type); |
| else if (type.isintegral()) |
| emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type); |
| else |
| emplaceExp!(RealExp)(&ue, 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) |
| { |
| emplaceExp!(RealExp)(&ue, loc, target.RealProperties.nan, type); |
| } |
| else |
| cantExp(ue); |
| } |
| else |
| cantExp(ue); |
| return ue; |
| } |
| |
| UnionExp Shl(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); |
| return ue; |
| } |
| |
| UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| dinteger_t value = e1.toInteger(); |
| dinteger_t dcount = e2.toInteger(); |
| assert(dcount <= 0xFFFFFFFF); |
| uint count = cast(uint)dcount; |
| switch (e1.type.toBasetype().ty) |
| { |
| case Tint8: |
| value = cast(byte)value >> count; |
| break; |
| case Tuns8: |
| case Tchar: |
| value = cast(ubyte)value >> count; |
| break; |
| case Tint16: |
| value = cast(short)value >> count; |
| break; |
| case Tuns16: |
| case Twchar: |
| value = cast(ushort)value >> count; |
| break; |
| case Tint32: |
| value = cast(int)value >> count; |
| break; |
| case Tuns32: |
| case Tdchar: |
| value = cast(uint)value >> count; |
| break; |
| case Tint64: |
| value = cast(long)value >> count; |
| break; |
| case Tuns64: |
| value = cast(ulong)value >> count; |
| break; |
| case Terror: |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| default: |
| assert(0); |
| } |
| emplaceExp!(IntegerExp)(&ue, loc, value, type); |
| return ue; |
| } |
| |
| UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| dinteger_t value = e1.toInteger(); |
| dinteger_t dcount = e2.toInteger(); |
| assert(dcount <= 0xFFFFFFFF); |
| uint count = cast(uint)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 = value >>> count; |
| break; |
| case Terror: |
| emplaceExp!(ErrorExp)(&ue); |
| return ue; |
| default: |
| assert(0); |
| } |
| emplaceExp!(IntegerExp)(&ue, loc, value, type); |
| return ue; |
| } |
| |
| UnionExp And(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); |
| return ue; |
| } |
| |
| UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); |
| return ue; |
| } |
| |
| UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars()); |
| UnionExp ue = void; |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() ^ e2.toInteger(), type); |
| return ue; |
| } |
| |
| /* Also returns EXP.cantExpression if cannot be computed. |
| */ |
| UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| int cmp = 0; |
| real_t r1 = CTFloat.zero; |
| real_t r2 = CTFloat.zero; |
| //printf("Equal(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); |
| assert(op == EXP.equal || op == EXP.notEqual); |
| if (e1.op == EXP.null_) |
| { |
| if (e2.op == EXP.null_) |
| cmp = 1; |
| else if (StringExp es2 = e2.isStringExp()) |
| { |
| cmp = (0 == es2.len); |
| } |
| else if (ArrayLiteralExp es2 = e2.isArrayLiteralExp()) |
| { |
| cmp = !es2.elements || (0 == es2.elements.dim); |
| } |
| else |
| { |
| cantExp(ue); |
| return ue; |
| } |
| } |
| else if (e2.op == EXP.null_) |
| { |
| if (StringExp es1 = e1.isStringExp()) |
| { |
| cmp = (0 == es1.len); |
| } |
| else if (ArrayLiteralExp es1 = e1.isArrayLiteralExp()) |
| { |
| cmp = !es1.elements || (0 == es1.elements.dim); |
| } |
| else |
| { |
| cantExp(ue); |
| return ue; |
| } |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.string_) |
| { |
| StringExp es1 = e1.isStringExp(); |
| StringExp es2 = e2.isStringExp(); |
| if (es1.sz != es2.sz) |
| { |
| assert(global.errors); |
| cantExp(ue); |
| return ue; |
| } |
| const data1 = es1.peekData(); |
| const data2 = es2.peekData(); |
| if (es1.len == es2.len && memcmp(data1.ptr, data2.ptr, es1.sz * es1.len) == 0) |
| cmp = 1; |
| else |
| cmp = 0; |
| } |
| else if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral) |
| { |
| ArrayLiteralExp es1 = e1.isArrayLiteralExp(); |
| ArrayLiteralExp es2 = e2.isArrayLiteralExp(); |
| 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++) |
| { |
| auto ee1 = es1[i]; |
| auto ee2 = es2[i]; |
| ue = Equal(EXP.equal, loc, Type.tint32, ee1, ee2); |
| if (CTFEExp.isCantExp(ue.exp())) |
| return ue; |
| cmp = cast(int)ue.exp().toInteger(); |
| if (cmp == 0) |
| break; |
| } |
| } |
| } |
| else if (e1.op == EXP.arrayLiteral && e2.op == EXP.string_) |
| { |
| // Swap operands and use common code |
| Expression etmp = e1; |
| e1 = e2; |
| e2 = etmp; |
| goto Lsa; |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral) |
| { |
| Lsa: |
| StringExp es1 = e1.isStringExp(); |
| ArrayLiteralExp es2 = e2.isArrayLiteralExp(); |
| 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 |
| foreach (i; 0 .. dim1) |
| { |
| uinteger_t c = es1.getCodeUnit(i); |
| auto ee2 = es2[i]; |
| if (ee2.isConst() != 1) |
| { |
| cantExp(ue); |
| return ue; |
| } |
| cmp = (c == ee2.toInteger()); |
| if (cmp == 0) |
| break; |
| } |
| } |
| } |
| else if (e1.op == EXP.structLiteral && e2.op == EXP.structLiteral) |
| { |
| StructLiteralExp es1 = e1.isStructLiteralExp(); |
| StructLiteralExp es2 = e2.isStructLiteralExp(); |
| 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(EXP.equal, loc, Type.tint32, ee1, ee2); |
| if (ue.exp().op == EXP.cantExpression) |
| return ue; |
| cmp = cast(int)ue.exp().toInteger(); |
| if (cmp == 0) |
| break; |
| } |
| } |
| } |
| else if (e1.isConst() != 1 || e2.isConst() != 1) |
| { |
| cantExp(ue); |
| 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 |
| { |
| cantExp(ue); |
| return ue; |
| } |
| if (op == EXP.notEqual) |
| cmp ^= 1; |
| emplaceExp!(IntegerExp)(&ue, loc, cmp, type); |
| return ue; |
| } |
| |
| UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| int cmp; |
| if (e1.op == EXP.null_) |
| { |
| cmp = (e2.op == EXP.null_); |
| } |
| else if (e2.op == EXP.null_) |
| { |
| cmp = 0; |
| } |
| else if (e1.op == EXP.symbolOffset && e2.op == EXP.symbolOffset) |
| { |
| SymOffExp es1 = e1.isSymOffExp(); |
| SymOffExp es2 = e2.isSymOffExp(); |
| cmp = (es1.var == es2.var && es1.offset == es2.offset); |
| } |
| else |
| { |
| if (e1.type.isreal()) |
| { |
| cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal()); |
| } |
| else if (e1.type.isimaginary()) |
| { |
| cmp = RealIdentical(e1.toImaginary(), e2.toImaginary()); |
| } |
| else if (e1.type.iscomplex()) |
| { |
| complex_t v1 = e1.toComplex(); |
| complex_t v2 = e2.toComplex(); |
| cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1)); |
| } |
| else |
| { |
| ue = Equal((op == EXP.identity) ? EXP.equal : EXP.notEqual, loc, type, e1, e2); |
| return ue; |
| } |
| } |
| if (op == EXP.notIdentity) |
| cmp ^= 1; |
| emplaceExp!(IntegerExp)(&ue, loc, cmp, type); |
| return ue; |
| } |
| |
| UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| dinteger_t n; |
| real_t r1 = CTFloat.zero; |
| real_t r2 = CTFloat.zero; |
| //printf("Cmp(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); |
| if (e1.op == EXP.string_ && e2.op == EXP.string_) |
| { |
| StringExp es1 = e1.isStringExp(); |
| StringExp es2 = e2.isStringExp(); |
| size_t sz = es1.sz; |
| assert(sz == es2.sz); |
| size_t len = es1.len; |
| if (es2.len < len) |
| len = es2.len; |
| const data1 = es1.peekData(); |
| const data2 = es1.peekData(); |
| int rawCmp = memcmp(data1.ptr, data2.ptr, sz * len); |
| if (rawCmp == 0) |
| rawCmp = cast(int)(es1.len - es2.len); |
| n = specificCmp(op, rawCmp); |
| } |
| else if (e1.isConst() != 1 || e2.isConst() != 1) |
| { |
| cantExp(ue); |
| 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); |
| } |
| emplaceExp!(IntegerExp)(&ue, loc, n, type); |
| return ue; |
| } |
| |
| /* Also returns EXP.cantExpression if cannot be computed. |
| * to: type to cast to |
| * type: type to paint the result |
| */ |
| UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) |
| { |
| UnionExp ue = void; |
| 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)) |
| { |
| emplaceExp!(UnionExp)(&ue, e1); |
| return ue; |
| } |
| if (e1.op == EXP.vector && (cast(TypeVector)e1.type).basetype.equals(type) && type.equals(to)) |
| { |
| Expression ex = e1.isVectorExp().e1; |
| emplaceExp!(UnionExp)(&ue, ex); |
| return ue; |
| } |
| if (e1.type.toBasetype.equals(type) && type.equals(to)) |
| { |
| emplaceExp!(UnionExp)(&ue, e1); |
| ue.exp().type = type; |
| return ue; |
| } |
| if (e1.type.implicitConvTo(to) >= MATCH.constant || to.implicitConvTo(e1.type) >= MATCH.constant) |
| { |
| goto L1; |
| } |
| // Allow covariant converions of delegates |
| // (Perhaps implicit conversion from pure to impure should be a MATCH.constant, |
| // then we wouldn't need this extra check.) |
| if (e1.type.toBasetype().ty == Tdelegate && e1.type.implicitConvTo(to) == MATCH.convert) |
| { |
| goto L1; |
| } |
| /* Allow casting from one string type to another |
| */ |
| if (e1.op == EXP.string_) |
| { |
| if (tb.ty == Tarray && typeb.ty == Tarray && tb.nextOf().size() == typeb.nextOf().size()) |
| { |
| goto L1; |
| } |
| } |
| if (e1.op == EXP.arrayLiteral && typeb == tb) |
| { |
| L1: |
| Expression ex = expType(to, e1); |
| emplaceExp!(UnionExp)(&ue, ex); |
| return ue; |
| } |
| if (e1.isConst() != 1) |
| { |
| cantExp(ue); |
| } |
| else if (tb.ty == Tbool) |
| { |
| const opt = e1.toBool(); |
| if (opt.isEmpty()) |
| { |
| cantExp(ue); |
| return ue; |
| } |
| |
| emplaceExp!(IntegerExp)(&ue, loc, opt.get(), type); |
| } |
| else if (type.isintegral()) |
| { |
| if (e1.type.isfloating()) |
| { |
| dinteger_t result; |
| real_t r = e1.toReal(); |
| switch (typeb.ty) |
| { |
| case Tint8: |
| result = cast(byte)cast(sinteger_t)r; |
| break; |
| case Tchar: |
| case Tuns8: |
| result = cast(ubyte)cast(dinteger_t)r; |
| break; |
| case Tint16: |
| result = cast(short)cast(sinteger_t)r; |
| break; |
| case Twchar: |
| case Tuns16: |
| result = cast(ushort)cast(dinteger_t)r; |
| break; |
| case Tint32: |
| result = cast(int)r; |
| break; |
| case Tdchar: |
| case Tuns32: |
| result = cast(uint)r; |
| break; |
| case Tint64: |
| result = cast(long)r; |
| break; |
| case Tuns64: |
| result = cast(ulong)r; |
| break; |
| default: |
| assert(0); |
| } |
| emplaceExp!(IntegerExp)(&ue, loc, result, type); |
| } |
| else if (type.isunsigned()) |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toUInteger(), type); |
| else |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); |
| } |
| else if (tb.isreal()) |
| { |
| real_t value = e1.toReal(); |
| emplaceExp!(RealExp)(&ue, loc, value, type); |
| } |
| else if (tb.isimaginary()) |
| { |
| real_t value = e1.toImaginary(); |
| emplaceExp!(RealExp)(&ue, loc, value, type); |
| } |
| else if (tb.iscomplex()) |
| { |
| complex_t value = e1.toComplex(); |
| emplaceExp!(ComplexExp)(&ue, loc, value, type); |
| } |
| else if (tb.isscalar()) |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); |
| } |
| else if (tb.ty == Tvoid) |
| { |
| cantExp(ue); |
| } |
| else if (tb.ty == Tstruct && e1.op == EXP.int64) |
| { |
| // Struct = 0; |
| StructDeclaration sd = tb.toDsymbol(null).isStructDeclaration(); |
| assert(sd); |
| auto elements = new Expressions(); |
| for (size_t i = 0; i < sd.fields.dim; i++) |
| { |
| VarDeclaration v = sd.fields[i]; |
| UnionExp zero; |
| emplaceExp!(IntegerExp)(&zero, 0); |
| ue = Cast(loc, v.type, v.type, zero.exp()); |
| if (ue.exp().op == EXP.cantExpression) |
| return ue; |
| elements.push(ue.exp().copy()); |
| } |
| emplaceExp!(StructLiteralExp)(&ue, 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()); |
| } |
| emplaceExp!(ErrorExp)(&ue); |
| } |
| return ue; |
| } |
| |
| UnionExp ArrayLength(Type type, Expression e1) |
| { |
| UnionExp ue = void; |
| Loc loc = e1.loc; |
| if (StringExp es1 = e1.isStringExp()) |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, es1.len, type); |
| } |
| else if (ArrayLiteralExp ale = e1.isArrayLiteralExp()) |
| { |
| size_t dim = ale.elements ? ale.elements.dim : 0; |
| emplaceExp!(IntegerExp)(&ue, loc, dim, type); |
| } |
| else if (AssocArrayLiteralExp ale = e1.isAssocArrayLiteralExp) |
| { |
| size_t dim = ale.keys.dim; |
| emplaceExp!(IntegerExp)(&ue, loc, dim, type); |
| } |
| else if (e1.type.toBasetype().ty == Tsarray) |
| { |
| Expression e = (cast(TypeSArray)e1.type.toBasetype()).dim; |
| emplaceExp!(UnionExp)(&ue, e); |
| } |
| else if (e1.isNullExp()) |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, 0, type); |
| } |
| else |
| cantExp(ue); |
| return ue; |
| } |
| |
| /* Also return EXP.cantExpression if this fails |
| */ |
| UnionExp Index(Type type, Expression e1, Expression e2, bool indexIsInBounds) |
| { |
| UnionExp ue = void; |
| Loc loc = e1.loc; |
| //printf("Index(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); |
| assert(e1.type); |
| if (e1.op == EXP.string_ && e2.op == EXP.int64) |
| { |
| StringExp es1 = e1.isStringExp(); |
| uinteger_t i = e2.toInteger(); |
| if (i >= es1.len) |
| { |
| e1.error("string index %llu is out of bounds `[0 .. %llu]`", i, cast(ulong)es1.len); |
| emplaceExp!(ErrorExp)(&ue); |
| } |
| else |
| { |
| emplaceExp!(IntegerExp)(&ue, loc, es1.getCodeUnit(cast(size_t) i), type); |
| } |
| } |
| else if (e1.type.toBasetype().ty == Tsarray && e2.op == EXP.int64) |
| { |
| TypeSArray tsa = cast(TypeSArray)e1.type.toBasetype(); |
| uinteger_t length = tsa.dim.toInteger(); |
| uinteger_t i = e2.toInteger(); |
| if (i >= length && (e1.op == EXP.arrayLiteral || !indexIsInBounds)) |
| { |
| // C code only checks bounds if an ArrayLiteralExp |
| e1.error("array index %llu is out of bounds `%s[0 .. %llu]`", i, e1.toChars(), length); |
| emplaceExp!(ErrorExp)(&ue); |
| } |
| else if (ArrayLiteralExp ale = e1.isArrayLiteralExp()) |
| { |
| auto e = ale[cast(size_t)i]; |
| e.type = type; |
| e.loc = loc; |
| if (hasSideEffect(e)) |
| cantExp(ue); |
| else |
| emplaceExp!(UnionExp)(&ue, e); |
| } |
| else |
| cantExp(ue); |
| } |
| else if (e1.type.toBasetype().ty == Tarray && e2.op == EXP.int64) |
| { |
| uinteger_t i = e2.toInteger(); |
| if (ArrayLiteralExp ale = e1.isArrayLiteralExp()) |
| { |
| if (i >= ale.elements.dim) |
| { |
| e1.error("array index %llu is out of bounds `%s[0 .. %llu]`", i, e1.toChars(), cast(ulong) ale.elements.dim); |
| emplaceExp!(ErrorExp)(&ue); |
| } |
| else |
| { |
| auto e = ale[cast(size_t)i]; |
| e.type = type; |
| e.loc = loc; |
| if (hasSideEffect(e)) |
| cantExp(ue); |
| else |
| emplaceExp!(UnionExp)(&ue, e); |
| } |
| } |
| else |
| cantExp(ue); |
| } |
| else if (AssocArrayLiteralExp ae = e1.isAssocArrayLiteralExp()) |
| { |
| /* 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(EXP.equal, loc, Type.tbool, ekey, e2); |
| if (CTFEExp.isCantExp(ue.exp())) |
| return ue; |
| if (ue.exp().toBool().hasValue(true)) |
| { |
| Expression e = (*ae.values)[i]; |
| e.type = type; |
| e.loc = loc; |
| if (hasSideEffect(e)) |
| cantExp(ue); |
| else |
| emplaceExp!(UnionExp)(&ue, e); |
| return ue; |
| } |
| } |
| cantExp(ue); |
| } |
| else |
| cantExp(ue); |
| return ue; |
| } |
| |
| /* Also return EXP.cantExpression if this fails |
| */ |
| UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr) |
| { |
| UnionExp ue = void; |
| Loc loc = e1.loc; |
| static if (LOG) |
| { |
| printf("Slice()\n"); |
| if (lwr) |
| { |
| printf("\te1 = %s\n", e1.toChars()); |
| printf("\tlwr = %s\n", lwr.toChars()); |
| printf("\tupr = %s\n", upr.toChars()); |
| } |
| } |
| |
| if (e1.op == EXP.string_ && lwr.op == EXP.int64 && upr.op == EXP.int64) |
| { |
| StringExp es1 = e1.isStringExp(); |
| const uinteger_t ilwr = lwr.toInteger(); |
| const uinteger_t iupr = upr.toInteger(); |
| if (sliceBoundsCheck(0, es1.len, ilwr, iupr)) |
| cantExp(ue); // https://issues.dlang.org/show_bug.cgi?id=18115 |
| else |
| { |
| const len = cast(size_t)(iupr - ilwr); |
| const sz = es1.sz; |
| void* s = mem.xmalloc(len * sz); |
| const data1 = es1.peekData(); |
| memcpy(s, data1.ptr + ilwr * sz, len * sz); |
| emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz, es1.postfix); |
| StringExp es = ue.exp().isStringExp(); |
| es.committed = es1.committed; |
| es.type = type; |
| } |
| } |
| else if (e1.op == EXP.arrayLiteral && lwr.op == EXP.int64 && upr.op == EXP.int64 && !hasSideEffect(e1)) |
| { |
| ArrayLiteralExp es1 = e1.isArrayLiteralExp(); |
| const uinteger_t ilwr = lwr.toInteger(); |
| const uinteger_t iupr = upr.toInteger(); |
| if (sliceBoundsCheck(0, es1.elements.dim, ilwr, iupr)) |
| cantExp(ue); |
| else |
| { |
| auto elements = new Expressions(cast(size_t)(iupr - ilwr)); |
| memcpy(elements.tdata(), es1.elements.tdata() + ilwr, cast(size_t)(iupr - ilwr) * ((*es1.elements)[0]).sizeof); |
| emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elements); |
| } |
| } |
| else |
| cantExp(ue); |
| return ue; |
| } |
| |
| /* Check whether slice `[newlwr .. newupr]` is in the range `[lwr .. upr]` |
| */ |
| bool sliceBoundsCheck(uinteger_t lwr, uinteger_t upr, uinteger_t newlwr, uinteger_t newupr) pure |
| { |
| assert(lwr <= upr); |
| return !(newlwr <= newupr && |
| lwr <= newlwr && |
| newupr <= upr); |
| } |
| |
| /* Set a slice of char/integer array literal 'existingAE' from a string 'newval'. |
| * existingAE[firstIndex..firstIndex+newval.length] = newval. |
| */ |
| void sliceAssignArrayLiteralFromString(ArrayLiteralExp existingAE, const StringExp newval, size_t firstIndex) |
| { |
| const len = newval.len; |
| Type elemType = existingAE.type.nextOf(); |
| foreach (j; 0 .. len) |
| { |
| const val = newval.getCodeUnit(j); |
| (*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) |
| { |
| assert(existingSE.ownedByCtfe != OwnedBy.code); |
| foreach (j; 0 .. newae.elements.dim) |
| { |
| existingSE.setCodeUnit(firstIndex + j, cast(dchar)newae[j].toInteger()); |
| } |
| } |
| |
| /* Set a slice of string 'existingSE' from a string 'newstr'. |
| * existingSE[firstIndex..firstIndex+newstr.length] = newstr. |
| */ |
| void sliceAssignStringFromString(StringExp existingSE, const StringExp newstr, size_t firstIndex) |
| { |
| assert(existingSE.ownedByCtfe != OwnedBy.code); |
| size_t sz = existingSE.sz; |
| assert(sz == newstr.sz); |
| auto data1 = existingSE.borrowData(); |
| const data2 = newstr.peekData(); |
| memcpy(data1.ptr + firstIndex * sz, data2.ptr, data2.length); |
| } |
| |
| /* Compare a string slice with another string slice. |
| * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len]) |
| */ |
| int sliceCmpStringWithString(const StringExp se1, const StringExp se2, size_t lo1, size_t lo2, size_t len) |
| { |
| size_t sz = se1.sz; |
| assert(sz == se2.sz); |
| const data1 = se1.peekData(); |
| const data2 = se2.peekData(); |
| return memcmp(data1.ptr + sz * lo1, data2.ptr + 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(const StringExp se1, ArrayLiteralExp ae2, size_t lo1, size_t lo2, size_t len) |
| { |
| foreach (j; 0 .. len) |
| { |
| const val2 = cast(dchar)ae2[j + lo2].toInteger(); |
| const val1 = se1.getCodeUnit(j + lo1); |
| const int c = val1 - val2; |
| if (c) |
| return c; |
| } |
| return 0; |
| } |
| |
| /** Copy element `Expressions` in the parameters when they're `ArrayLiteralExp`s. |
| * Params: |
| * e1 = If it's ArrayLiteralExp, its `elements` will be copied. |
| * Otherwise, `e1` itself will be pushed into the new `Expressions`. |
| * e2 = If it's not `null`, it will be pushed/appended to the new |
| * `Expressions` by the same way with `e1`. |
| * Returns: |
| * Newly allocated `Expressions`. Note that it points to the original |
| * `Expression` values in e1 and e2. |
| */ |
| private Expressions* copyElements(Expression e1, Expression e2 = null) |
| { |
| auto elems = new Expressions(); |
| |
| void append(ArrayLiteralExp ale) |
| { |
| if (!ale.elements) |
| return; |
| auto d = elems.dim; |
| elems.append(ale.elements); |
| foreach (ref el; (*elems)[d .. elems.dim]) |
| { |
| if (!el) |
| el = ale.basis; |
| } |
| } |
| |
| if (auto ale = e1.isArrayLiteralExp()) |
| append(ale); |
| else |
| elems.push(e1); |
| |
| if (e2) |
| { |
| if (auto ale = e2.isArrayLiteralExp()) |
| append(ale); |
| else |
| elems.push(e2); |
| } |
| |
| return elems; |
| } |
| |
| /* Also return EXP.cantExpression if this fails |
| */ |
| UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) |
| { |
| UnionExp ue = void; |
| Expression e = CTFEExp.cantexp; |
| 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()); |
| |
| /* e is the non-null operand, t is the type of the null operand |
| */ |
| UnionExp catNull(Expression e, Type t) |
| { |
| Type tn = e.type.toBasetype(); |
| if (tn.ty.isSomeChar) |
| { |
| // Create a StringExp |
| if (t.nextOf()) |
| t = t.nextOf().toBasetype(); |
| const sz = cast(ubyte)t.size(); |
| dinteger_t v = e.toInteger(); |
| const len = (t.ty == tn.ty) ? 1 : utf_codeLength(sz, cast(dchar)v); |
| void* s = mem.xmalloc(len * sz); |
| if (t.ty == tn.ty) |
| Port.valcpy(s, v, sz); |
| else |
| utf_encode(sz, s, cast(dchar)v); |
| emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
| StringExp es = ue.exp().isStringExp(); |
| es.type = type; |
| es.committed = 1; |
| } |
| else |
| { |
| // Create an ArrayLiteralExp |
| auto elements = new Expressions(); |
| elements.push(e); |
| emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements); |
| } |
| assert(ue.exp().type); |
| return ue; |
| } |
| |
| if (e1.op == EXP.null_ && (e2.op == EXP.int64 || e2.op == EXP.structLiteral)) |
| { |
| return catNull(e2, t1); |
| } |
| else if ((e1.op == EXP.int64 || e1.op == EXP.structLiteral) && e2.op == EXP.null_) |
| { |
| return catNull(e1, t2); |
| } |
| else if (e1.op == EXP.null_ && e2.op == EXP.null_) |
| { |
| if (type == e1.type) |
| { |
| // Handle null ~= null |
| if (t1.ty == Tarray && t2 == t1.nextOf()) |
| { |
| emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, e2); |
| assert(ue.exp().type); |
| return ue; |
| } |
| else |
| { |
| emplaceExp!(UnionExp)(&ue, e1); |
| assert(ue.exp().type); |
| return ue; |
| } |
| } |
| if (type == e2.type) |
| { |
| emplaceExp!(UnionExp)(&ue, e2); |
| assert(ue.exp().type); |
| return ue; |
| } |
| emplaceExp!(NullExp)(&ue, e1.loc, type); |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.string_) |
| { |
| // Concatenate the strings |
| StringExp es1 = e1.isStringExp(); |
| StringExp es2 = e2.isStringExp(); |
| size_t len = es1.len + es2.len; |
| ubyte sz = es1.sz; |
| if (sz != es2.sz) |
| { |
| /* Can happen with: |
| * auto s = "foo"d ~ "bar"c; |
| */ |
| assert(global.errors); |
| cantExp(ue); |
| assert(ue.exp().type); |
| return ue; |
| } |
| void* s = mem.xmalloc(len * sz); |
| const data1 = es1.peekData(); |
| const data2 = es2.peekData(); |
| memcpy(cast(char*)s, data1.ptr, es1.len * sz); |
| memcpy(cast(char*)s + es1.len * sz, data2.ptr, es2.len * sz); |
| emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
| StringExp es = ue.exp().isStringExp(); |
| es.committed = es1.committed | es2.committed; |
| es.type = type; |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral()) |
| { |
| // [chars] ~ string --> [chars] |
| StringExp es = e2.isStringExp(); |
| ArrayLiteralExp ea = e1.isArrayLiteralExp(); |
| size_t len = es.len + ea.elements.dim; |
| auto elems = new Expressions(len); |
| for (size_t i = 0; i < ea.elements.dim; ++i) |
| { |
| (*elems)[i] = ea[i]; |
| } |
| emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems); |
| ArrayLiteralExp dest = ue.exp().isArrayLiteralExp(); |
| sliceAssignArrayLiteralFromString(dest, es, ea.elements.dim); |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral()) |
| { |
| // string ~ [chars] --> [chars] |
| StringExp es = e1.isStringExp(); |
| ArrayLiteralExp ea = e2.isArrayLiteralExp(); |
| size_t len = es.len + ea.elements.dim; |
| auto elems = new Expressions(len); |
| for (size_t i = 0; i < ea.elements.dim; ++i) |
| { |
| (*elems)[es.len + i] = ea[i]; |
| } |
| emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems); |
| ArrayLiteralExp dest = ue.exp().isArrayLiteralExp(); |
| sliceAssignArrayLiteralFromString(dest, es, 0); |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.int64) |
| { |
| // string ~ char --> string |
| StringExp es1 = e1.isStringExp(); |
| StringExp es; |
| const sz = es1.sz; |
| dinteger_t v = e2.toInteger(); |
| // Is it a concatenation of homogenous types? |
| // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar) |
| bool homoConcat = (sz == t2.size()); |
| const len = es1.len + (homoConcat ? 1 : utf_codeLength(sz, cast(dchar)v)); |
| void* s = mem.xmalloc(len * sz); |
| const data1 = es1.peekData(); |
| memcpy(s, data1.ptr, data1.length); |
| if (homoConcat) |
| Port.valcpy(cast(char*)s + (sz * es1.len), v, sz); |
| else |
| utf_encode(sz, cast(char*)s + (sz * es1.len), cast(dchar)v); |
| emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
| es = ue.exp().isStringExp(); |
| es.committed = es1.committed; |
| es.type = type; |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e1.op == EXP.int64 && e2.op == EXP.string_) |
| { |
| // [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 = e2.isStringExp(); |
| const len = 1 + es2.len; |
| const sz = es2.sz; |
| dinteger_t v = e1.toInteger(); |
| void* s = mem.xmalloc(len * sz); |
| Port.valcpy(cast(char*)s, v, sz); |
| const data2 = es2.peekData(); |
| memcpy(cast(char*)s + sz, data2.ptr, data2.length); |
| emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
| StringExp es = ue.exp().isStringExp(); |
| es.sz = sz; |
| es.committed = es2.committed; |
| es.type = type; |
| assert(ue.exp().type); |
| return ue; |
| } |
| else if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf())) |
| { |
| // Concatenate the arrays |
| auto elems = copyElements(e1, e2); |
| |
| emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, cast(Type)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 == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf())) |
| { |
| e = e1; |
| goto L3; |
| } |
| else if (e1.op == EXP.null_ && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf())) |
| { |
| e = e2; |
| L3: |
| // Concatenate the array with null |
| auto elems = copyElements(e); |
| |
| emplaceExp!(ArrayLiteralExp)(&ue, e.loc, cast(Type)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 == EXP.arrayLiteral || e1.op == EXP.null_) && e1.type.toBasetype().nextOf() && e1.type.toBasetype().nextOf().equals(e2.type)) |
| { |
| auto elems = (e1.op == EXP.arrayLiteral) |
| ? copyElements(e1) : new Expressions(); |
| elems.push(e2); |
| |
| emplaceExp!(ArrayLiteralExp)(&ue, loc, cast(Type)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 == EXP.arrayLiteral && e2.type.toBasetype().nextOf().equals(e1.type)) |
| { |
| auto elems = copyElements(e1, e2); |
| |
| emplaceExp!(ArrayLiteralExp)(&ue, loc, cast(Type)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 == EXP.null_ && e2.op == EXP.string_) |
| { |
| t = e1.type; |
| e = e2; |
| goto L1; |
| } |
| else if (e1.op == EXP.string_ && e2.op == EXP.null_) |
| { |
| e = e1; |
| t = e2.type; |
| L1: |
| Type tb = t.toBasetype(); |
| if (tb.ty == Tarray && tb.nextOf().equivalent(e.type)) |
| { |
| auto expressions = new Expressions(); |
| expressions.push(e); |
| emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions); |
| e = ue.exp(); |
| } |
| else |
| { |
| emplaceExp!(UnionExp)(&ue, e); |
| e = ue.exp(); |
| } |
| if (!e.type.equals(type)) |
| { |
| StringExp se = e.copy().isStringExp(); |
| e = se.castTo(null, type); |
| emplaceExp!(UnionExp)(&ue, e); |
| e = ue.exp(); |
| } |
| } |
| else |
| cantExp(ue); |
| assert(ue.exp().type); |
| return ue; |
| } |
| |
| UnionExp Ptr(Type type, Expression e1) |
| { |
| //printf("Ptr(e1 = %s)\n", e1.toChars()); |
| UnionExp ue = void; |
| if (AddExp ae = e1.isAddExp()) |
| { |
| if (AddrExp ade = ae.e1.isAddrExp()) |
| { |
| if (ae.e2.op == EXP.int64) |
| if (StructLiteralExp se = ade.e1.isStructLiteralExp()) |
| { |
| uint offset = cast(uint)ae.e2.toInteger(); |
| Expression e = se.getField(type, offset); |
| if (e) |
| { |
| emplaceExp!(UnionExp)(&ue, e); |
| return ue; |
| } |
| } |
| } |
| } |
| cantExp(ue); |
| return ue; |
| } |