diff --git a/dmd/mtype.c b/dmd/mtype.c index 4f38a0fd..b8dda54b 100644 --- a/dmd/mtype.c +++ b/dmd/mtype.c @@ -1743,12 +1743,12 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) if (dup) arguments->push(getTypeInfo(sc)); - // LDC repaint array type to void[] - if (n->ty != Tvoid) { - e = new CastExp(e->loc, e, e->type); - e->type = Type::tvoid->arrayOf(); - } - arguments->push(e); + // LDC repaint array type to void[] + if (n->ty != Tvoid) { + e = new CastExp(e->loc, e, e->type); + e->type = Type::tvoid->arrayOf(); + } + arguments->push(e); if (!dup) arguments->push(new IntegerExp(0, size, Type::tsize_t)); @@ -1759,7 +1759,6 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) { Expression *ec; Expressions *arguments; - bool isBit = (n->ty == Tbit); //LDC: Build arguments. static FuncDeclaration *adSort_fd = NULL; @@ -1769,31 +1768,20 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL)); adSort_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort"); } - static FuncDeclaration *adSortBit_fd = NULL; - if(!adSortBit_fd) { - Parameters* args = new Parameters; - args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL)); - args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL)); - adSortBit_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSortBit"); - } - if(isBit) - ec = new VarExp(0, adSortBit_fd); - else - ec = new VarExp(0, adSort_fd); + ec = new VarExp(0, adSort_fd); e = e->castTo(sc, n->arrayOf()); // convert to dynamic array arguments = new Expressions(); - // LDC repaint array type to void[] - if (n->ty != Tvoid) { - e = new CastExp(e->loc, e, e->type); - e->type = Type::tvoid->arrayOf(); - } - arguments->push(e); - - if (next->ty != Tbit) - arguments->push(n->getTypeInfo(sc)); // LDC, we don't support the getInternalTypeInfo - // optimization arbitrarily, not yet at least... + // LDC repaint array type to void[] + if (n->ty != Tvoid) { + e = new CastExp(e->loc, e, e->type); + e->type = Type::tvoid->arrayOf(); + } + arguments->push(e); + // LDC, we don't support the getInternalTypeInfo + // optimization arbitrarily, not yet at least... + arguments->push(n->getTypeInfo(sc)); e = new CallExp(e->loc, ec, arguments); e->type = next->arrayOf(); } diff --git a/dmd2/aggregate.h b/dmd2/aggregate.h index e1b75c30..776f8b60 100644 --- a/dmd2/aggregate.h +++ b/dmd2/aggregate.h @@ -67,6 +67,7 @@ struct AggregateDeclaration : ScopeDsymbol // 0: no size // 1: size is correct // 2: cannot determine size; fwd referenced + Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol int isdeprecated; // !=0 if deprecated #if DMDV2 @@ -234,6 +235,7 @@ struct ClassDeclaration : AggregateDeclaration static ClassDeclaration *object; static ClassDeclaration *classinfo; static ClassDeclaration *throwable; + static ClassDeclaration *exception; ClassDeclaration *baseClass; // NULL only if this is Object #if DMDV1 diff --git a/dmd2/attrib.h b/dmd2/attrib.h index 2e3bcfe6..a0b44ef1 100644 --- a/dmd2/attrib.h +++ b/dmd2/attrib.h @@ -23,9 +23,7 @@ struct LabelDsymbol; struct Initializer; struct Module; struct Condition; -#ifdef _DH struct HdrGenState; -#endif /**************************************************************/ diff --git a/dmd2/cast.c b/dmd2/cast.c index 0f099bc3..dfc20e66 100644 --- a/dmd2/cast.c +++ b/dmd2/cast.c @@ -17,6 +17,10 @@ #include "utf.h" #include "declaration.h" #include "aggregate.h" +#include "scope.h" + +//#define DUMP .dump(__PRETTY_FUNCTION__, this) +#define DUMP /* ==================== implicitCast ====================== */ @@ -158,8 +162,10 @@ MATCH Expression::implicitConvTo(Type *t) */ if (type->isintegral() && t->isintegral() && type->isTypeBasic() && t->isTypeBasic()) - { IntRange ir = getIntRange(); - if (ir.imax <= t->sizemask()) + { IntRange src = this->getIntRange() DUMP; + IntRange targetUnsigned = IntRange::fromType(t, /*isUnsigned*/true) DUMP; + IntRange targetSigned = IntRange::fromType(t, /*isUnsigned*/false) DUMP; + if (targetUnsigned.contains(src) || targetSigned.contains(src)) return MATCHconvert; } @@ -203,7 +209,6 @@ MATCH IntegerExp::implicitConvTo(Type *t) switch (ty) { - case Tbit: case Tbool: value &= 1; ty = Tint32; @@ -248,7 +253,6 @@ MATCH IntegerExp::implicitConvTo(Type *t) // Only allow conversion if no change in value switch (toty) { - case Tbit: case Tbool: if ((value & 1) != value) goto Lno; @@ -1533,7 +1537,17 @@ Expression *CommaExp::castTo(Scope *sc, Type *t) */ Expression *BinExp::scaleFactor(Scope *sc) -{ d_uns64 stride; +{ + if (sc->func && !sc->intypeof) + { + if (sc->func->setUnsafe()) + { + error("pointer arithmetic not allowed in @safe functions"); + return new ErrorExp(); + } + } + + d_uns64 stride; Type *t1b = e1->type->toBasetype(); Type *t2b = e2->type->toBasetype(); @@ -1615,10 +1629,17 @@ bool isVoidArrayLiteral(Expression *e, Type *other) int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression **pe2) { //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars()); - //dump(0); + //e->dump(0); - Expression *e1 = (*pe1)->integralPromotions(sc); - Expression *e2 = (*pe2)->integralPromotions(sc); + Expression *e1 = *pe1; + Expression *e2 = *pe2; + + if (e->op != TOKquestion || + e1->type->toBasetype()->ty != e2->type->toBasetype()->ty) + { + e1 = e1->integralPromotions(sc); + e2 = e2->integralPromotions(sc); + } Type *t1 = e1->type; Type *t2 = e2->type; @@ -1861,7 +1882,15 @@ Lagain: } else if (t1->isintegral() && t2->isintegral()) { - assert(0); + assert(t1->ty == t2->ty); + 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 (e1->isArrayOperand() && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf())) @@ -1974,7 +2003,6 @@ Expression *Expression::integralPromotions(Scope *sc) case Tuns8: case Tint16: case Tuns16: - case Tbit: case Tbool: case Tchar: case Twchar: @@ -2046,233 +2074,309 @@ int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2) uinteger_t getMask(uinteger_t v) { - uinteger_t u = 0; - if (v >= 0x80) - u = 0xFF; - while (u < v) - u = (u << 1) | 1; - return u; + // Ref: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return v /* | 0xff*/; } - IntRange Expression::getIntRange() { - IntRange ir; - ir.imin = 0; - if (type->isintegral()) - ir.imax = type->sizemask(); - else - ir.imax = 0xFFFFFFFFFFFFFFFFULL; // assume the worst - return ir; + return IntRange::fromType(type) DUMP; } IntRange IntegerExp::getIntRange() { - IntRange ir; - ir.imin = value & type->sizemask(); - ir.imax = ir.imin; - return ir; + return IntRange(value).cast(type) DUMP; } IntRange CastExp::getIntRange() { - IntRange ir; - ir = e1->getIntRange(); - // Do sign extension - switch (e1->type->toBasetype()->ty) - { - case Tint8: - if (ir.imax & 0x80) - ir.imax |= 0xFFFFFFFFFFFFFF00ULL; - break; - case Tint16: - if (ir.imax & 0x8000) - ir.imax |= 0xFFFFFFFFFFFF0000ULL; - break; - case Tint32: - if (ir.imax & 0x80000000) - ir.imax |= 0xFFFFFFFF00000000ULL; - break; - } - if (type->isintegral()) - { - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); - } -//printf("CastExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); - return ir; + return e1->getIntRange().cast(type) DUMP; +} + +IntRange AddExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + return IntRange(ir1.imin + ir2.imin, ir1.imax + ir2.imax).cast(type) DUMP; +} + +IntRange MinExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + return IntRange(ir1.imin - ir2.imax, ir1.imax - ir2.imin).cast(type) DUMP; } IntRange DivExp::getIntRange() { - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - if (!(e1->type->isunsigned() || ir1.imax < 0x8000000000000000ULL) && - !(e2->type->isunsigned() || ir2.imax < 0x8000000000000000ULL)) - { - return Expression::getIntRange(); - } + // Should we ignore the possibility of div-by-0??? + if (ir2.containsZero()) + return Expression::getIntRange() DUMP; - if (ir2.imax == 0 || ir2.imin == 0) - return Expression::getIntRange(); - - ir.imin = ir1.imin / ir2.imax; - ir.imax = ir1.imax / ir2.imin; - - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); - -//printf("DivExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] + SignExtendedNumber bdy[4] = { + ir1.imin / ir2.imin, + ir1.imin / ir2.imax, + ir1.imax / ir2.imin, + ir1.imax / ir2.imax + }; + return IntRange::fromNumbers4(bdy).cast(type) DUMP; } +IntRange MulExp::getIntRange() +{ + IntRange ir1 = e1->getIntRange(); + IntRange ir2 = e2->getIntRange(); + + // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)] + SignExtendedNumber bdy[4] = { + ir1.imin * ir2.imin, + ir1.imin * ir2.imax, + ir1.imax * ir2.imin, + ir1.imax * ir2.imax + }; + return IntRange::fromNumbers4(bdy).cast(type) DUMP; +} + +IntRange ModExp::getIntRange() +{ + IntRange irNum = e1->getIntRange(); + IntRange irDen = e2->getIntRange().absNeg(); + + /* + due to the rules of D (C)'s % operator, we need to consider the cases + separately in different range of signs. + + case 1. [500, 1700] % [7, 23] (numerator is always positive) + = [0, 22] + case 2. [-500, 1700] % [7, 23] (numerator can be negative) + = [-22, 22] + case 3. [-1700, -500] % [7, 23] (numerator is always negative) + = [-22, 0] + + the number 22 is the maximum absolute value in the denomator's range. We + don't care about divide by zero. + */ + + // Modding on 0 is invalid anyway. + if (!irDen.imin.negative) + return Expression::getIntRange() DUMP; + + ++ irDen.imin; + irDen.imax = -irDen.imin; + + if (!irNum.imin.negative) + irNum.imin.value = 0; + else if (irNum.imin < irDen.imin) + irNum.imin = irDen.imin; + + if (irNum.imax.negative) + { + irNum.imax.negative = false; + irNum.imax.value = 0; + } + else if (irNum.imax > irDen.imax) + irNum.imax = irDen.imax; + + return irNum.cast(type) DUMP; +} + +// The algorithms for &, |, ^ are not yet the best! Sometimes they will produce +// not the tightest bound. See +// https://github.com/D-Programming-Language/dmd/pull/116 +// for detail. +static IntRange unsignedBitwiseAnd(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // Since '&' computes the digitwise-minimum, the we could set all varying + // digits to 0 to get a lower bound, and set all varying digits to 1 to get + // an upper bound. + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) & (b.imin.value & ~bDiffMask); + result.imax.value = (a.imax.value | aDiffMask) & (b.imax.value | bDiffMask); + // Sometimes the upper bound is overestimated. The upper bound will never + // exceed the input. + if (result.imax.value > a.imax.value) + result.imax.value = a.imax.value; + if (result.imax.value > b.imax.value) + result.imax.value = b.imax.value; + result.imin.negative = result.imax.negative = a.imin.negative && b.imin.negative; + return result; +} +static IntRange unsignedBitwiseOr(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // The imax algorithm by Adam D. Ruppe. + // http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=108796 + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) | (b.imin.value & ~bDiffMask); + result.imax.value = a.imax.value | b.imax.value | getMask(a.imax.value & b.imax.value); + // Sometimes the lower bound is underestimated. The lower bound will never + // less than the input. + if (result.imin.value < a.imin.value) + result.imin.value = a.imin.value; + if (result.imin.value < b.imin.value) + result.imin.value = b.imin.value; + result.imin.negative = result.imax.negative = a.imin.negative || b.imin.negative; + return result; +} +static IntRange unsignedBitwiseXor(const IntRange& a, const IntRange& b) +{ + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + IntRange result; + result.imin.value = (a.imin.value ^ b.imin.value) & ~(aDiffMask | bDiffMask); + result.imax.value = (a.imax.value ^ b.imax.value) | (aDiffMask | bDiffMask); + result.imin.negative = result.imax.negative = a.imin.negative != b.imin.negative; + return result; +} + + IntRange AndExp::getIntRange() { - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - ir.imin = ir1.imin; - if (ir2.imin < ir.imin) - ir.imin = ir2.imin; + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; - ir.imax = ir1.imax; - if (ir2.imax > ir.imax) - ir.imax = ir2.imax; + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - uinteger_t u; - - u = getMask(ir1.imax); - ir.imin &= u; - ir.imax &= u; - - u = getMask(ir2.imax); - ir.imin &= u; - ir.imax &= u; - - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); - -//printf("AndExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2neg), /*ref*/hasResult); + assert(hasResult); + return result.cast(type) DUMP; } -/* - * Adam D. Ruppe's algo for bitwise OR: - * http://www.digitalmars.com/d/archives/digitalmars/D/value_range_propagation_for_logical_OR_108765.html#N108793 - */ - IntRange OrExp::getIntRange() { - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - ir.imin = ir1.imin; - if (ir2.imin < ir.imin) - ir.imin = ir2.imin; + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; - ir.imax = ir1.imax; - if (ir2.imax > ir.imax) - ir.imax = ir2.imax; + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2neg), /*ref*/hasResult); -//printf("OrExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + assert(hasResult); + return result.cast(type) DUMP; } IntRange XorExp::getIntRange() { - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - ir.imin = ir1.imin; - if (ir2.imin < ir.imin) - ir.imin = ir2.imin; + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; - ir.imax = ir1.imax; - if (ir2.imax > ir.imax) - ir.imax = ir2.imax; + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2pos), /*ref*/hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2neg), /*ref*/hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2pos), /*ref*/hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2neg), /*ref*/hasResult); -//printf("XorExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + assert(hasResult); + return result.cast(type) DUMP; } IntRange ShlExp::getIntRange() { - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - ir.imin = getMask(ir1.imin) << ir2.imin; - ir.imax = getMask(ir1.imax) << ir2.imax; + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); + SignExtendedNumber lower = ir1.imin << (ir1.imin.negative ? ir2.imax : ir2.imin); + SignExtendedNumber upper = ir1.imax << (ir1.imax.negative ? ir2.imin : ir2.imax); -//printf("ShlExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + return IntRange(lower, upper).cast(type) DUMP; } IntRange ShrExp::getIntRange() { - if (!e1->type->isunsigned()) - return Expression::getIntRange(); - - IntRange ir; IntRange ir1 = e1->getIntRange(); IntRange ir2 = e2->getIntRange(); - ir.imin = ir1.imin >> ir2.imax; - ir.imax = ir1.imax >> ir2.imin; + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); + SignExtendedNumber lower = ir1.imin >> (ir1.imin.negative ? ir2.imin : ir2.imax); + SignExtendedNumber upper = ir1.imax >> (ir1.imax.negative ? ir2.imax : ir2.imin); -//printf("ShrExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; + return IntRange(lower, upper).cast(type) DUMP; } IntRange UshrExp::getIntRange() { - IntRange ir; - IntRange ir1 = e1->getIntRange(); + IntRange ir1 = e1->getIntRange().castUnsigned(e1->type); IntRange ir2 = e2->getIntRange(); - ir.imin = ir1.imin >> ir2.imax; - ir.imax = ir1.imax >> ir2.imin; + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); - ir.imin &= type->sizemask(); - ir.imax &= type->sizemask(); + return IntRange(ir1.imin >> ir2.imax, ir1.imax >> ir2.imin).cast(type) DUMP; -//printf("UshrExp: imin = x%llx, imax = x%llx\n", ir.imin, ir.imax); -//e1->dump(0); - - return ir; } IntRange CommaExp::getIntRange() { - return e2->getIntRange(); + return e2->getIntRange() DUMP; } +IntRange ComExp::getIntRange() +{ + IntRange ir = e1->getIntRange(); + return IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), + SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(type) DUMP; +} + +IntRange NegExp::getIntRange() +{ + IntRange ir = e1->getIntRange(); + return IntRange(-ir.imax, -ir.imin).cast(type) DUMP; +} diff --git a/dmd2/class.c b/dmd2/class.c index 52ddb7ac..ad2c694b 100644 --- a/dmd2/class.c +++ b/dmd2/class.c @@ -32,6 +32,7 @@ ClassDeclaration *ClassDeclaration::classinfo; ClassDeclaration *ClassDeclaration::object; ClassDeclaration *ClassDeclaration::throwable; +ClassDeclaration *ClassDeclaration::exception; ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) : AggregateDeclaration(loc, id) @@ -190,6 +191,12 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla throwable = this; } + if (id == Id::Exception) + { if (exception) + exception->error("%s", msg); + exception = this; + } + //if (id == Id::ClassInfo) if (id == Id::TypeInfo_Class) { if (classinfo) @@ -695,7 +702,8 @@ void ClassDeclaration::semantic(Scope *sc) if (!ctor && baseClass && baseClass->ctor) { //printf("Creating default this(){} for class %s\n", toChars()); - CtorDeclaration *ctor = new CtorDeclaration(loc, 0, NULL, 0, 0); + Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); + CtorDeclaration *ctor = new CtorDeclaration(loc, 0, 0, tf); ctor->fbody = new CompoundStatement(0, new Statements()); members->push(ctor); ctor->addMember(sc, this, 1); @@ -758,6 +766,12 @@ void ClassDeclaration::semantic(Scope *sc) } #endif //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); + + if (deferred) + { + deferred->semantic2(sc); + deferred->semantic3(sc); + } } void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) diff --git a/dmd2/clone.c b/dmd2/clone.c index 9d568f86..cb40c76d 100644 --- a/dmd2/clone.c +++ b/dmd2/clone.c @@ -338,21 +338,26 @@ FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) fcp = new FuncDeclaration(loc, 0, Id::cpctor, STCundefined, ftype); fcp->storage_class |= postblit->storage_class & STCdisable; - // Build *this = p; - Expression *e = new ThisExp(0); + if (!(fcp->storage_class & STCdisable)) + { + // Build *this = p; + Expression *e = new ThisExp(0); #if !STRUCTTHISREF - e = new PtrExp(0, e); + e = new PtrExp(0, e); #endif - AssignExp *ea = new AssignExp(0, e, new IdentifierExp(0, Id::p)); - ea->op = TOKblit; - Statement *s = new ExpStatement(0, ea); + AssignExp *ea = new AssignExp(0, e, new IdentifierExp(0, Id::p)); + ea->op = TOKblit; + Statement *s = new ExpStatement(0, ea); - // Build postBlit(); - e = new VarExp(0, postblit, 0); - e = new CallExp(0, e); + // Build postBlit(); + e = new VarExp(0, postblit, 0); + e = new CallExp(0, e); - s = new CompoundStatement(0, s, new ExpStatement(0, e)); - fcp->fbody = s; + s = new CompoundStatement(0, s, new ExpStatement(0, e)); + fcp->fbody = s; + } + else + fcp->fbody = new ExpStatement(0, (Expression *)NULL); members->push(fcp); @@ -391,7 +396,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); - size_t dim = 1; + size_t dim = (tv->ty == Tsarray ? 1 : 0); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; dim *= ((TypeSArray *)tv)->dim->toInteger(); @@ -401,15 +406,20 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (sd->postblit) - { Expression *ex; - + { stc |= sd->postblit->storage_class & STCdisable; + if (stc & STCdisable) + { + e = NULL; + break; + } + // this.v - ex = new ThisExp(0); + Expression *ex = new ThisExp(0); ex = new DotVarExp(0, ex, v, 0); - if (dim == 1) + if (dim == 0) { // this.v.postblit() ex = new DotVarExp(0, ex, sd->postblit, 0); ex = new CallExp(0, ex); @@ -432,7 +442,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) /* Build our own "postblit" which executes e */ - if (e) + if (e || (stc & STCdisable)) { //printf("Building __fieldPostBlit()\n"); PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, Lexer::idPool("__fieldPostBlit")); dd->storage_class |= stc; @@ -455,6 +465,11 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) for (size_t i = 0; i < postblits.dim; i++) { FuncDeclaration *fd = (FuncDeclaration *)postblits.data[i]; stc |= fd->storage_class & STCdisable; + if (stc & STCdisable) + { + e = NULL; + break; + } Expression *ex = new ThisExp(0); ex = new DotVarExp(0, ex, fd, 0); ex = new CallExp(0, ex); @@ -493,7 +508,7 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); - size_t dim = 1; + size_t dim = (tv->ty == Tsarray ? 1 : 0); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; dim *= ((TypeSArray *)tv)->dim->toInteger(); @@ -509,7 +524,7 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) ex = new ThisExp(0); ex = new DotVarExp(0, ex, v, 0); - if (dim == 1) + if (dim == 0) { // this.v.dtor() ex = new DotVarExp(0, ex, sd->dtor, 0); ex = new CallExp(0, ex); diff --git a/dmd2/cond.c b/dmd2/cond.c index 831a10d6..51557961 100644 --- a/dmd2/cond.c +++ b/dmd2/cond.c @@ -20,10 +20,8 @@ #include "module.h" #include "template.h" #include "lexer.h" -#ifdef _DH #include "mtype.h" #include "scope.h" -#endif int findCondition(Array *ids, Identifier *ident) { @@ -138,6 +136,7 @@ void VersionCondition::checkPredefined(Loc loc, const char *ident) "D_NET", #endif "OSX", "FreeBSD", + "OpenBSD", "Solaris", "LittleEndian", "BigEndian", "all", diff --git a/dmd2/cond.h b/dmd2/cond.h index 8302c23d..e3cde453 100644 --- a/dmd2/cond.h +++ b/dmd2/cond.h @@ -18,13 +18,9 @@ struct Module; struct Scope; struct ScopeDsymbol; struct DebugCondition; -#ifdef _DH #include "lexer.h" // dmdhg -#endif enum TOK; -#ifdef _DH struct HdrGenState; -#endif int findCondition(Array *ids, Identifier *ident); diff --git a/dmd2/constfold.c b/dmd2/constfold.c index 7551922e..784bac21 100644 --- a/dmd2/constfold.c +++ b/dmd2/constfold.c @@ -27,12 +27,10 @@ #include "declaration.h" #include "utf.h" -#if __FreeBSD__ -#define fmodl fmod // hack for now, fix later -#endif - #define LOG 0 +int RealEquals(real_t x1, real_t x2); + Expression *expType(Type *type, Expression *e) { if (type != e->type) @@ -486,7 +484,7 @@ Expression *Mod(Type *type, Expression *e1, Expression *e2) { real_t r2 = e2->toReal(); #ifdef __DMC__ - c = fmodl(e1->toReal(), r2) + fmodl(e1->toImaginary(), r2) * I; + c = Port::fmodl(e1->toReal(), r2) + Port::fmodl(e1->toImaginary(), r2) * I; #elif defined(IN_GCC) c = complex_t(e1->toReal() % r2, e1->toImaginary() % r2); #elif (defined(__FreeBSD__) && __FreeBSD_version < 800000) || defined(__arm__) || defined(__thumb__) @@ -494,14 +492,14 @@ Expression *Mod(Type *type, Expression *e1, Expression *e2) // arm also doesn't like fmodl c = complex_t(fmod(e1->toReal(), r2), fmod(e1->toImaginary(), r2)); #else - c = complex_t(fmodl(e1->toReal(), r2), fmodl(e1->toImaginary(), r2)); + c = complex_t(Port::fmodl(e1->toReal(), r2), Port::fmodl(e1->toImaginary(), r2)); #endif } else if (e2->type->isimaginary()) { real_t i2 = e2->toImaginary(); #ifdef __DMC__ - c = fmodl(e1->toReal(), i2) + fmodl(e1->toImaginary(), i2) * I; + c = Port::fmodl(e1->toReal(), i2) + Port::fmodl(e1->toImaginary(), i2) * I; #elif defined(IN_GCC) c = complex_t(e1->toReal() % i2, e1->toImaginary() % i2); #elif (defined(__FreeBSD__) && __FreeBSD_version < 800000) || defined(__arm__) || defined(__thumb__) @@ -509,7 +507,7 @@ Expression *Mod(Type *type, Expression *e1, Expression *e2) // arm also doesn't like fmodl c = complex_t(fmod(e1->toReal(), i2), fmod(e1->toImaginary(), i2)); #else - c = complex_t(fmodl(e1->toReal(), i2), fmodl(e1->toImaginary(), i2)); + c = complex_t(Port::fmodl(e1->toReal(), i2), Port::fmodl(e1->toImaginary(), i2)); #endif } else @@ -876,11 +874,27 @@ Expression *Identity(enum TOK op, Type *type, Expression *e1, Expression *e2) cmp = (es1->var == es2->var && es1->offset == es2->offset); } - else if (e1->isConst() == 1 && e2->isConst() == 1) - return Equal((op == TOKidentity) ? TOKequal : TOKnotequal, - type, e1, e2); else - assert(0); + { + 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 + return Equal((op == TOKidentity) ? TOKequal : TOKnotequal, + type, e1, e2); + } if (op == TOKnotidentity) cmp ^= 1; return new IntegerExp(loc, cmp, type); @@ -1089,6 +1103,13 @@ Expression *Cast(Type *type, Type *to, Expression *e1) to->implicitConvTo(e1->type) >= MATCHconst) return expType(to, e1); + // 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) + return expType(to, e1); + Type *tb = to->toBasetype(); Type *typeb = type->toBasetype(); @@ -1241,30 +1262,34 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) uinteger_t i = e2->toInteger(); if (i >= length) - { e2->error("array index %ju is out of bounds %s[0 .. %ju]", i, e1->toChars(), length); + { e1->error("array index %ju is out of bounds %s[0 .. %ju]", i, e1->toChars(), length); } - else if (e1->op == TOKarrayliteral && !e1->checkSideEffect(2)) + else if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; e = (Expression *)ale->elements->data[i]; e->type = type; + if (e->checkSideEffect(2)) + e = EXP_CANT_INTERPRET; } } else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64) { uinteger_t i = e2->toInteger(); - if (e1->op == TOKarrayliteral && !e1->checkSideEffect(2)) + if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; if (i >= ale->elements->dim) - { e2->error("array index %ju is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); + { e1->error("array index %ju is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); } else { e = (Expression *)ale->elements->data[i]; e->type = type; + if (e->checkSideEffect(2)) + e = EXP_CANT_INTERPRET; } } } - else if (e1->op == TOKassocarrayliteral && !e1->checkSideEffect(2)) + else if (e1->op == TOKassocarrayliteral) { AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1; /* Search the keys backwards, in case there are duplicate keys @@ -1279,6 +1304,8 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) if (ex->isBool(TRUE)) { e = (Expression *)ae->values->data[i]; e->type = type; + if (e->checkSideEffect(2)) + e = EXP_CANT_INTERPRET; break; } } @@ -1405,6 +1432,14 @@ Expression *Cat(Type *type, Expression *e1, Expression *e2) e->type = type; return e; } + else if (e1->op == TOKnull && e2->op == TOKnull) + { + if (type == e1->type) + return e1; + if (type == e2->type) + return e2; + return new NullExp(e1->loc, type); + } else if (e1->op == TOKstring && e2->op == TOKstring) { // Concatenate the strings diff --git a/dmd2/declaration.c b/dmd2/declaration.c index a0a14b8d..49081815 100644 --- a/dmd2/declaration.c +++ b/dmd2/declaration.c @@ -273,10 +273,8 @@ TypedefDeclaration::TypedefDeclaration(Loc loc, Identifier *id, Type *basetype, this->type = new TypeTypedef(this); this->basetype = basetype->toBasetype(); this->init = init; -#ifdef _DH this->htype = NULL; this->hbasetype = NULL; -#endif this->loc = loc; #if IN_DMD this->sinit = NULL; @@ -294,7 +292,7 @@ Dsymbol *TypedefDeclaration::syntaxCopy(Dsymbol *s) assert(!s); TypedefDeclaration *st; st = new TypedefDeclaration(loc, ident, basetype, init); -#ifdef _DH + // Syntax copy for header file if (!htype) // Don't overwrite original { if (type) // Make copy for both old and new instances @@ -312,7 +310,7 @@ Dsymbol *TypedefDeclaration::syntaxCopy(Dsymbol *s) } else st->hbasetype = hbasetype->syntaxCopy(); -#endif + return st; } @@ -389,10 +387,8 @@ AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Type *type) this->loc = loc; this->type = type; this->aliassym = NULL; -#ifdef _DH this->htype = NULL; this->haliassym = NULL; -#endif this->overnext = NULL; this->inSemantic = 0; this->importprot = PROTundefined; @@ -407,10 +403,8 @@ AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Dsymbol *s) this->loc = loc; this->type = NULL; this->aliassym = s; -#ifdef _DH this->htype = NULL; this->haliassym = NULL; -#endif this->overnext = NULL; this->inSemantic = 0; assert(s); @@ -425,7 +419,7 @@ Dsymbol *AliasDeclaration::syntaxCopy(Dsymbol *s) sa = new AliasDeclaration(loc, ident, type->syntaxCopy()); else sa = new AliasDeclaration(loc, ident, aliassym->syntaxCopy(NULL)); -#ifdef _DH + // Syntax copy for header file if (!htype) // Don't overwrite original { if (type) // Make copy for both old and new instances @@ -443,7 +437,7 @@ Dsymbol *AliasDeclaration::syntaxCopy(Dsymbol *s) } else sa->haliassym = haliassym->syntaxCopy(s); -#endif + return sa; } @@ -631,7 +625,7 @@ Dsymbol *AliasDeclaration::toAlias() void AliasDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("alias "); -#if 0 && _DH +#if 0 if (hgs->hdrgen) { if (haliassym) @@ -678,10 +672,8 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer assert(type || init); this->type = type; this->init = init; -#ifdef _DH this->htype = NULL; this->hinit = NULL; -#endif this->loc = loc; offset = 0; noscope = 0; @@ -733,7 +725,7 @@ Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) sv = new VarDeclaration(loc, type ? type->syntaxCopy() : NULL, ident, init); sv->storage_class = storage_class; } -#ifdef _DH + // Syntax copy for header file if (!htype) // Don't overwrite original { if (type) // Make copy for both old and new instances @@ -751,7 +743,7 @@ Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) } else sv->hinit = hinit->syntaxCopy(); -#endif + return sv; } @@ -844,17 +836,33 @@ void VarDeclaration::semantic(Scope *sc) //printf("storage_class = x%x\n", storage_class); #if DMDV2 -#if 1 - if (storage_class & STCgshared && sc->func && sc->func->isSafe()) + // Safety checks + if (sc->func && !sc->intypeof) { - error("__gshared not allowed in safe functions; use shared"); + if (storage_class & STCgshared) + { + if (sc->func->setUnsafe()) + error("__gshared not allowed in safe functions; use shared"); + } + if (init && init->isVoidInitializer() && type->hasPointers()) + { + if (sc->func->setUnsafe()) + error("void initializers for pointers not allowed in safe functions"); + } + if (type->hasPointers() && type->toDsymbol(sc)) + { + Dsymbol *s = type->toDsymbol(sc); + if (s) + { + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (ad && ad->hasUnions) + { + if (sc->func->setUnsafe()) + error("unions containing pointers are not allowed in @safe functions"); + } + } + } } -#else - if (storage_class & STCgshared && global.params.safe && !sc->module->safe) - { - error("__gshared not allowed in safe mode; use shared"); - } -#endif #endif Dsymbol *parent = toParent(); @@ -1184,18 +1192,24 @@ Lagain: ei->exp = resolveProperties(sc, ei->exp); StructDeclaration *sd = ((TypeStruct *)t)->sym; #if DMDV2 + Expression** pinit = &ei->exp; + while ((*pinit)->op == TOKcomma) + { + pinit = &((CommaExp *)*pinit)->e2; + } + /* Look to see if initializer is a call to the constructor */ if (sd->ctor && // there are constructors - ei->exp->type->ty == Tstruct && // rvalue is the same struct - ((TypeStruct *)ei->exp->type)->sym == sd && - ei->exp->op == TOKcall) + (*pinit)->type->ty == Tstruct && // rvalue is the same struct + ((TypeStruct *)(*pinit)->type)->sym == sd && + (*pinit)->op == TOKcall) { /* Look for form of constructor call which is: * *__ctmp.ctor(arguments...) */ if (1) - { CallExp *ce = (CallExp *)ei->exp; + { CallExp *ce = (CallExp *)(*pinit); if (ce->e1->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ce->e1; if (dve->var->isCtorDeclaration()) @@ -1216,12 +1230,12 @@ Lagain: e->op = TOKblit; } e->type = t; - ei->exp = new CommaExp(loc, e, ei->exp); + (*pinit) = new CommaExp(loc, e, (*pinit)); /* Replace __ctmp being constructed with e1 */ dve->e1 = e1; - ei->exp = ei->exp->semantic(sc); + (*pinit) = (*pinit)->semantic(sc); goto Ldtor; } } @@ -1650,6 +1664,7 @@ int VarDeclaration::canTakeAddressOf() return 1; } + /******************************* * Does symbol go into data segment? * Includes extern variables. diff --git a/dmd2/declaration.h b/dmd2/declaration.h index 6714f7b9..d1d53a33 100644 --- a/dmd2/declaration.h +++ b/dmd2/declaration.h @@ -214,10 +214,8 @@ struct TypedefDeclaration : Declaration const char *kind(); Type *getType(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); -#ifdef _DH Type *htype; Type *hbasetype; -#endif void toDocBuffer(OutBuffer *buf); @@ -258,10 +256,8 @@ struct AliasDeclaration : Declaration Type *getType(); Dsymbol *toAlias(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); -#ifdef _DH Type *htype; Dsymbol *haliassym; -#endif void toDocBuffer(OutBuffer *buf); @@ -311,10 +307,8 @@ struct VarDeclaration : Declaration void semantic2(Scope *sc); const char *kind(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); -#ifdef _DH Type *htype; Initializer *hinit; -#endif AggregateDeclaration *isThis(); int needThis(); int isImportedSymbol(); @@ -722,7 +716,6 @@ struct FuncDeclaration : Declaration Loc endloc; // location of closing curly bracket int vtblIndex; // for member functions, index into vtbl[] int naked; // !=0 if naked - int inlineAsm; // !=0 if has inline assembler ILS inlineStatus; int inlineNest; // !=0 if nested inline int cantInterpret; // !=0 if cannot interpret function @@ -759,6 +752,11 @@ struct FuncDeclaration : Declaration Dsymbols closureVars; // local variables in this function // which are referenced by nested // functions + + unsigned flags; + #define FUNCFLAGpurityInprocess 1 // working on determining purity + #define FUNCFLAGsafetyInprocess 2 // working on determining safety + #define FUNCFLAGnothrowInprocess 4 // working on determining nothrow #else int nestedFrameRef; // !=0 if nested variables referenced #endif @@ -798,8 +796,10 @@ struct FuncDeclaration : Declaration int isCodeseg(); int isOverloadable(); enum PURE isPure(); + bool setImpure(); int isSafe(); int isTrusted(); + bool setUnsafe(); virtual int isNested(); int needThis(); virtual int isVirtual(); @@ -857,6 +857,9 @@ struct FuncDeclaration : Declaration // true if overridden with the pragma(allow_inline); stmt bool allowInlining; + + // true if has inline assembler + bool inlineAsm; #endif }; @@ -898,10 +901,8 @@ struct FuncLiteralDeclaration : FuncDeclaration }; struct CtorDeclaration : FuncDeclaration -{ Parameters *arguments; - int varargs; - - CtorDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs, StorageClass stc); +{ + CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -1068,9 +1069,7 @@ struct DeleteDeclaration : FuncDeclaration int isVirtual(); int addPreInvariant(); int addPostInvariant(); -#ifdef _DH DeleteDeclaration *isDeleteDeclaration() { return this; } -#endif }; #endif /* DMD_DECLARATION_H */ diff --git a/dmd2/doc.c b/dmd2/doc.c index 5dd94dbf..9debd549 100644 --- a/dmd2/doc.c +++ b/dmd2/doc.c @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -133,6 +133,7 @@ LINK = $0\n\ LINK2 = $+\n\ LPAREN= (\n\ RPAREN= )\n\ +DOLLAR= $\n\ \n\ RED = $0\n\ BLUE = $0\n\ @@ -374,6 +375,12 @@ void escapeDdocString(OutBuffer *buf, unsigned start) unsigned char c = buf->data[u]; switch(c) { + case '$': + buf->remove(u, 1); + buf->insert(u, "$(DOLLAR)", 9); + u += 8; + break; + case '(': buf->remove(u, 1); //remove the ( buf->insert(u, "$(LPAREN)", 9); //insert this instead @@ -792,29 +799,35 @@ void prefix(OutBuffer *buf, Dsymbol *s) } } -void Declaration::toDocBuffer(OutBuffer *buf) +void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclaration *td) { - //printf("Declaration::toDocbuffer() %s, originalType = %p\n", toChars(), originalType); - if (ident) + //printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--"); + if (decl->ident) { - prefix(buf, this); + prefix(buf, decl); - if (type) + if (decl->type) { HdrGenState hgs; hgs.ddoc = 1; - if (originalType) - { //originalType->print(); - originalType->toCBuffer(buf, ident, &hgs); + Type *origType = decl->originalType ? decl->originalType : decl->type; + if (origType->ty == Tfunction) + { + TypeFunction *attrType = (TypeFunction*)(decl->ident == Id::ctor ? origType : decl->type); + ((TypeFunction*)origType)->toCBufferWithAttributes(buf, decl->ident, &hgs, attrType, td); } else - type->toCBuffer(buf, ident, &hgs); + origType->toCBuffer(buf, decl->ident, &hgs); } else - buf->writestring(ident->toChars()); + buf->writestring(decl->ident->toChars()); buf->writestring(";\n"); } } +void Declaration::toDocBuffer(OutBuffer *buf) +{ + declarationToDocBuffer(this, buf, NULL); +} void AliasDeclaration::toDocBuffer(OutBuffer *buf) { @@ -859,31 +872,9 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf) td->onemember == this) { /* It's a function template */ - HdrGenState hgs; unsigned o = buf->offset; - TypeFunction *tf = (TypeFunction *)type; - hgs.ddoc = 1; - prefix(buf, td); - if (tf) - { if (tf->nextOf()) - tf->nextOf()->toCBuffer(buf, NULL, &hgs); - else - buf->writestring("auto"); - } - buf->writeByte(' '); - buf->writestring(ident->toChars()); - buf->writeByte('('); - for (int i = 0; i < td->origParameters->dim; i++) - { - TemplateParameter *tp = (TemplateParameter *)td->origParameters->data[i]; - if (i) - buf->writestring(", "); - tp->toCBuffer(buf, &hgs); - } - buf->writeByte(')'); - Parameter::argsToCBuffer(buf, &hgs, tf ? tf->parameters : NULL, tf ? tf->varargs : 0); - buf->writestring(";\n"); + declarationToDocBuffer(this, buf, td); highlightCode(NULL, this, buf, o); } @@ -894,6 +885,17 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf) } } +#if DMDV1 +void CtorDeclaration::toDocBuffer(OutBuffer *buf) +{ + HdrGenState hgs; + + buf->writestring("this"); + Parameter::argsToCBuffer(buf, &hgs, arguments, varargs); + buf->writestring(";\n"); +} +#endif + void AggregateDeclaration::toDocBuffer(OutBuffer *buf) { if (ident) diff --git a/dmd2/dsymbol.c b/dmd2/dsymbol.c index b617e2e1..3ba307be 100644 --- a/dmd2/dsymbol.c +++ b/dmd2/dsymbol.c @@ -218,10 +218,13 @@ char *Dsymbol::locToChars() OutBuffer buf; char *p; - Module *m = getModule(); + if (!loc.filename) // avoid bug 5861. + { + Module *m = getModule(); - if (m && m->srcfile) - loc.filename = m->srcfile->toChars(); + if (m && m->srcfile) + loc.filename = m->srcfile->toChars(); + } return loc.toChars(); } diff --git a/dmd2/dsymbol.h b/dmd2/dsymbol.h index f6319935..7bd458ad 100644 --- a/dmd2/dsymbol.h +++ b/dmd2/dsymbol.h @@ -178,10 +178,8 @@ struct Dsymbol : Object Dsymbol *search_correct(Identifier *id); Dsymbol *searchX(Loc loc, Scope *sc, Identifier *id); virtual int overloadInsert(Dsymbol *s); -#ifdef _DH char *toHChars(); virtual void toHBuffer(OutBuffer *buf, HdrGenState *hgs); -#endif virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); virtual void toDocBuffer(OutBuffer *buf); virtual void toJsonBuffer(OutBuffer *buf); @@ -262,9 +260,7 @@ struct Dsymbol : Object virtual ArrayScopeSymbol *isArrayScopeSymbol() { return NULL; } virtual Import *isImport() { return NULL; } virtual EnumDeclaration *isEnumDeclaration() { return NULL; } -#ifdef _DH virtual DeleteDeclaration *isDeleteDeclaration() { return NULL; } -#endif virtual StaticStructInitDeclaration *isStaticStructInitDeclaration() { return NULL; } virtual AttribDeclaration *isAttribDeclaration() { return NULL; } virtual OverloadSet *isOverloadSet() { return NULL; } diff --git a/dmd2/enum.h b/dmd2/enum.h index 70e69536..a07d80d7 100644 --- a/dmd2/enum.h +++ b/dmd2/enum.h @@ -21,9 +21,7 @@ struct Identifier; struct Type; struct Expression; -#ifdef _DH struct HdrGenState; -#endif struct EnumDeclaration : ScopeDsymbol diff --git a/dmd2/expression.c b/dmd2/expression.c index f397c4c0..002fb280 100644 --- a/dmd2/expression.c +++ b/dmd2/expression.c @@ -58,6 +58,7 @@ int isnan(double); #include "attrib.h" #include "hdrgen.h" #include "parse.h" +#include "doc.h" #if IN_DMD Expression *createTypeInfoArray(Scope *sc, Expression *args[], int dim); @@ -199,7 +200,7 @@ FuncDeclaration *hasThis(Scope *sc) break; } - fd = fd->parent->isFuncDeclaration(); + fd = parent->isFuncDeclaration(); } if (!fd->isThis()) @@ -228,9 +229,12 @@ Expression *resolveProperties(Scope *sc, Expression *e) if (t->ty == Tfunction || e->op == TOKoverloadset) { -#if 0 - if (t->ty == Tfunction && !((TypeFunction *)t)->isproperty) +#if 1 + if (t->ty == Tfunction && !((TypeFunction *)t)->isproperty && + global.params.enforcePropertySyntax) + { error(e->loc, "not a property %s\n", e->toChars()); + } #endif e = new CallExp(e->loc, e); e = e->semantic(sc); @@ -410,6 +414,7 @@ Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt condexp.type = NULL; condexp.e1 = e0; condexp.e2 = e; + condexp.loc = e->loc; condexp.semantic(sc); exps->data[j0] = (void *)condexp.e1; e = condexp.e2; @@ -483,6 +488,41 @@ void preFunctionParameters(Loc loc, Scope *sc, Expressions *exps) } } +/************************************************ + * If we want the value of this expression, but do not want to call + * the destructor on it. + */ + +void valueNoDtor(Expression *e) +{ + if (e->op == TOKcall) + { + /* The struct value returned from the function is transferred + * so do not call the destructor on it. + * Recognize: + * ((S _ctmp = S.init), _ctmp).this(...) + * and make sure the destructor is not called on _ctmp + * BUG: if e is a CommaExp, we should go down the right side. + */ + CallExp *ce = (CallExp *)e; + if (ce->e1->op == TOKdotvar) + { DotVarExp *dve = (DotVarExp *)ce->e1; + if (dve->var->isCtorDeclaration()) + { // It's a constructor call + if (dve->e1->op == TOKcomma) + { CommaExp *comma = (CommaExp *)dve->e1; + if (comma->e2->op == TOKvar) + { VarExp *ve = (VarExp *)comma->e2; + VarDeclaration *ctmp = ve->var->isVarDeclaration(); + if (ctmp) + ctmp->noscope = 1; + } + } + } + } + } +} + /********************************************* * Call copy constructor for struct value argument. */ @@ -603,7 +643,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, Identifier *id = Lexer::uniqueId("__arrayArg"); Type *t = new TypeSArray(((TypeArray *)tb)->next, new IntegerExp(nargs - i)); t = t->semantic(loc, sc); - VarDeclaration *v = new VarDeclaration(loc, t, id, new VoidInitializer(loc)); + VarDeclaration *v = new VarDeclaration(loc, t, id, fd->isSafe() ? NULL : new VoidInitializer(loc)); v->storage_class |= STCctfe; v->semantic(sc); v->parent = sc->parent; @@ -712,24 +752,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, /* The struct value returned from the function is transferred * to the function, so the callee should not call the destructor * on it. - * ((S _ctmp = S.init), _ctmp).this(...) */ - CallExp *ce = (CallExp *)arg; - if (ce->e1->op == TOKdotvar) - { DotVarExp *dve = (DotVarExp *)ce->e1; - if (dve->var->isCtorDeclaration()) - { // It's a constructor call - if (dve->e1->op == TOKcomma) - { CommaExp *comma = (CommaExp *)dve->e1; - if (comma->e2->op == TOKvar) - { VarExp *ve = (VarExp *)comma->e2; - VarDeclaration *ctmp = ve->var->isVarDeclaration(); - if (ctmp) - ctmp->noscope = 1; - } - } - } - } + valueNoDtor(arg); } else { /* Not transferring it, so call the copy constructor @@ -1252,6 +1276,11 @@ void Expression::checkDeprecated(Scope *sc, Dsymbol *s) } #if DMDV2 +/********************************************* + * Calling function f. + * Check the purity, i.e. if we're in a pure function + * we can only call other pure functions. + */ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) { #if 1 @@ -1287,12 +1316,14 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) } // If the caller has a pure parent, then either the called func must be pure, // OR, they must have the same pure parent. - if (outerfunc->isPure() && !sc->intypeof && + if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now + !sc->intypeof && !(sc->flags & SCOPEdebug) && !(f->isPure() || (calledparent == outerfunc))) { - error("pure function '%s' cannot call impure function '%s'", - outerfunc->toChars(), f->toChars()); + if (outerfunc->setImpure()) + error("pure function '%s' cannot call impure function '%s'", + outerfunc->toChars(), f->toChars()); } } #else @@ -1302,12 +1333,136 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) #endif } +/******************************************* + * Accessing variable v. + * Check for purity and safety violations. + * If ethis is not NULL, then ethis is the 'this' pointer as in ethis.v + */ + +void Expression::checkPurity(Scope *sc, VarDeclaration *v, Expression *ethis) +{ + /* Look for purity and safety violations when accessing variable v + * from current function. + */ + if (sc->func && + !sc->intypeof && // allow violations inside typeof(expression) + !(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals + v->ident != Id::ctfe && // magic variable never violates pure and safe + !v->isImmutable() && // always safe and pure to access immutables... + !(v->isConst() && v->isDataseg() && !v->type->hasPointers()) && // const global value types are immutable + !(v->storage_class & STCmanifest) // ...or manifest constants + ) + { + if (v->isDataseg()) + { + /* Accessing global mutable state. + * Therefore, this function and all its immediately enclosing + * functions must be pure. + */ + bool msg = FALSE; + for (Dsymbol *s = sc->func; s; s = s->toParent2()) + { + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (ff->setImpure() && !msg) + { error("pure function '%s' cannot access mutable static data '%s'", + sc->func->toChars(), v->toChars()); + msg = TRUE; // only need the innermost message + } + } + } + else + { + if (ethis) + { + Type *t1 = ethis->type->toBasetype(); + + if (t1->isImmutable() || + (t1->ty == Tpointer && t1->nextOf()->isImmutable())) + { + goto L1; + } + if (ethis->op == TOKvar) + { VarExp *ve = (VarExp *)ethis; + + v = ve->var->isVarDeclaration(); + if (v) + checkPurity(sc, v, NULL); + return; + } + if (ethis->op == TOKdotvar) + { DotVarExp *ve = (DotVarExp *)ethis; + + v = ve->var->isVarDeclaration(); + if (v) + checkPurity(sc, v, ve->e1); + return; + } + } + + /* Given: + * void f() + * { int fx; + * pure void g() + * { int gx; + * void h() + * { int hx; + * void i() { } + * } + * } + * } + * i() can modify hx and gx but not fx + */ + + /* Back up until we find the parent function of v, + * requiring each function in between to be impure. + */ + Dsymbol *vparent = v->toParent2(); + Dsymbol *s = sc->func, *snext = s->toParent2(); + // Make sure we're really finding parent *functions*, not parent + // class. + if (vparent->isFuncDeclaration() || snext != vparent) + { + for (; s; s = s->toParent2()) + { + if (s == vparent) + break; + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (ff->setImpure()) + { error("pure nested function '%s' cannot access mutable data '%s'", + ff->toChars(), v->toChars()); + break; + } + } + } + } + + + /* Do not allow safe functions to access __gshared data + */ + if (v->storage_class & STCgshared) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot access __gshared data '%s'", + sc->func->toChars(), v->toChars()); + } + + L1: ; + } +} + void Expression::checkSafety(Scope *sc, FuncDeclaration *f) { - if (sc->func && sc->func->isSafe() && !sc->intypeof && + if (sc->func && !sc->intypeof && !f->isSafe() && !f->isTrusted()) - error("safe function '%s' cannot call system function '%s'", - sc->func->toChars(), f->toChars()); + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system function '%s'", + sc->func->toChars(), f->toChars()); + } } #endif @@ -1606,7 +1761,6 @@ dinteger_t IntegerExp::toInteger() { switch (t->ty) { - case Tbit: case Tbool: value = (value != 0); break; case Tint8: value = (d_int8) value; break; case Tchar: @@ -1750,13 +1904,18 @@ void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) break; } case Tchar: + { + unsigned o = buf->offset; if (v == '\'') buf->writestring("'\\''"); else if (isprint(v) && v != '\\') buf->printf("'%c'", (int)v); else buf->printf("'\\x%02x'", (int)v); + if (hgs->ddoc) + escapeDdocString(buf, o); break; + } case Tint8: buf->writestring("cast(byte)"); @@ -1793,7 +1952,6 @@ void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->printf("%juLU", v); break; - case Tbit: case Tbool: buf->writestring((char *)(v ? "true" : "false")); break; @@ -1940,9 +2098,7 @@ complex_t RealExp::toComplex() int RealEquals(real_t x1, real_t x2) { -//#if 1 // return (Port::isNan(x1) && Port::isNan(x2)) || -//#elif __APPLE__ #if __APPLE__ return (__inline_isnan(x1) && __inline_isnan(x2)) || #else @@ -2064,9 +2220,7 @@ void realToMangleBuffer(OutBuffer *buf, real_t value) * 0X1.9P+2 => 19P2 */ -//#if 1 // if (Port::isNan(value)) -//#elif __APPLE__ #if __APPLE__ if (__inline_isnan(value)) #else @@ -3156,6 +3310,7 @@ unsigned StringExp::charAt(size_t i) void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writeByte('"'); + unsigned o = buf->offset; for (size_t i = 0; i < len; i++) { unsigned c = charAt(i); @@ -3180,6 +3335,8 @@ void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) break; } } + if (hgs->ddoc) + escapeDdocString(buf, o); buf->writeByte('"'); if (postfix) buf->writeByte(postfix); @@ -3279,6 +3436,12 @@ Expression *ArrayLiteralExp::semantic(Scope *sc) type = t0->arrayOf(); //type = new TypeSArray(t0, new IntegerExp(elements->dim)); type = type->semantic(loc, sc); + + /* Disallow array literals of type void being used. + */ + if (elements->dim > 0 && t0->ty == Tvoid) + error("%s of type %s has no value", toChars(), type->toChars()); + return this; } @@ -3304,7 +3467,10 @@ int ArrayLiteralExp::isBool(int result) #if DMDV2 int ArrayLiteralExp::canThrow(bool mustNotThrow) { - return 1; // because it can fail allocating memory + /* Memory allocation failures throw non-recoverable exceptions, which + * we don't need to count as 'throwing'. + */ + return arrayExpressionCanThrow(elements, mustNotThrow); } #endif @@ -3399,7 +3565,11 @@ int AssocArrayLiteralExp::isBool(int result) #if DMDV2 int AssocArrayLiteralExp::canThrow(bool mustNotThrow) { - return 1; + /* Memory allocation failures throw non-recoverable exceptions, which + * we don't need to count as 'throwing'. + */ + return (arrayExpressionCanThrow(keys, mustNotThrow) || + arrayExpressionCanThrow(values, mustNotThrow)); } #endif @@ -4210,7 +4380,22 @@ int NewExp::checkSideEffect(int flag) #if DMDV2 int NewExp::canThrow(bool mustNotThrow) { - return 0; // regard storage allocation failures as not recoverable + if (arrayExpressionCanThrow(newargs, mustNotThrow) || + arrayExpressionCanThrow(arguments, mustNotThrow)) + return 1; + if (member) + { + // See if constructor call can throw + Type *t = member->type->toBasetype(); + if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) + { + if (mustNotThrow) + error("constructor %s is not nothrow", member->toChars()); + return 1; + } + } + // regard storage allocation failures as not recoverable + return 0; } #endif @@ -4284,6 +4469,7 @@ int NewAnonClassExp::checkSideEffect(int flag) #if DMDV2 int NewAnonClassExp::canThrow(bool mustNotThrow) { + assert(0); // should have been lowered by semantic() return 1; } #endif @@ -4438,94 +4624,9 @@ Expression *VarExp::semantic(Scope *sc) VarDeclaration *v = var->isVarDeclaration(); if (v) { -#if 0 - if ((v->isConst() || v->isImmutable()) && - type->toBasetype()->ty != Tsarray && v->init) - { - ExpInitializer *ei = v->init->isExpInitializer(); - if (ei) - { - //ei->exp->implicitCastTo(sc, type)->print(); - return ei->exp->implicitCastTo(sc, type); - } - } -#endif v->checkNestedReference(sc, loc); #if DMDV2 -#if 1 - if (sc->func && !sc->intypeof && !(sc->flags & SCOPEdebug)) - { - /* Given: - * void f() - * { int fx; - * pure void g() - * { int gx; - * void h() - * { int hx; - * void i() { } - * } - * } - * } - * i() can modify hx and gx but not fx - */ - - /* Determine if sc->func is pure or if any function that - * encloses it is also pure. - */ - bool hasPureParent = false; - for (FuncDeclaration *outerfunc = sc->func; outerfunc;) - { - if (outerfunc->isPure()) - { - hasPureParent = true; - break; - } - Dsymbol *parent = outerfunc->toParent2(); - if (!parent) - break; - outerfunc = parent->isFuncDeclaration(); - } - - /* Magic variable __ctfe never violates pure or safe - */ - if (v->ident != Id::ctfe) - { - /* If ANY of its enclosing functions are pure, - * it cannot do anything impure. - * If it is pure, it cannot access any mutable variables other - * than those inside itself - */ - if (hasPureParent && v->isDataseg() && - !v->isImmutable()) - { - error("pure function '%s' cannot access mutable static data '%s'", - sc->func->toChars(), v->toChars()); - } - else if (sc->func->isPure() && - sc->parent->pastMixin() != v->parent->pastMixin() && - !v->isImmutable() && - !(v->storage_class & STCmanifest)) - { - error("pure nested function '%s' cannot access mutable data '%s'", - sc->func->toChars(), v->toChars()); - if (v->isEnumDeclaration()) - error("enum"); - } - } - - /* Do not allow safe functions to access __gshared data - */ - if (sc->func->isSafe() && v->storage_class & STCgshared) - error("safe function '%s' cannot access __gshared data '%s'", - sc->func->toChars(), v->toChars()); - } -#else - if (sc->func && sc->func->isPure() && !sc->intypeof) - { - if (v->isDataseg() && !v->isImmutable()) - error("pure function '%s' cannot access mutable static data '%s'", sc->func->toChars(), v->toChars()); - } -#endif + checkPurity(sc, v, NULL); #endif } #if 0 @@ -4955,15 +5056,88 @@ int DeclarationExp::checkSideEffect(int flag) } #if DMDV2 -int DeclarationExp::canThrow(bool mustNotThrow) +/************************************** + * Does symbol, when initialized, throw? + * Mirrors logic in Dsymbol_toElem(). + */ + +int Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) { - VarDeclaration *v = declaration->isVarDeclaration(); - if (v && v->init) - { ExpInitializer *ie = v->init->isExpInitializer(); - return ie && ie->exp->canThrow(mustNotThrow); + AttribDeclaration *ad; + VarDeclaration *vd; + TemplateMixin *tm; + TupleDeclaration *td; + + //printf("Dsymbol_toElem() %s\n", s->toChars()); + ad = s->isAttribDeclaration(); + if (ad) + { + Array *decl = ad->include(NULL, NULL); + if (decl && decl->dim) + { + for (size_t i = 0; i < decl->dim; i++) + { + s = (Dsymbol *)decl->data[i]; + if (Dsymbol_canThrow(s, mustNotThrow)) + return 1; + } + } + } + else if ((vd = s->isVarDeclaration()) != NULL) + { + s = s->toAlias(); + if (s != vd) + return Dsymbol_canThrow(s, mustNotThrow); + if (vd->storage_class & STCmanifest) + ; + else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) + ; + else + { + if (vd->init) + { ExpInitializer *ie = vd->init->isExpInitializer(); + if (ie && ie->exp->canThrow(mustNotThrow)) + return 1; + } + if (vd->edtor && !vd->noscope) + return vd->edtor->canThrow(mustNotThrow); + } + } + else if ((tm = s->isTemplateMixin()) != NULL) + { + //printf("%s\n", tm->toChars()); + if (tm->members) + { + for (size_t i = 0; i < tm->members->dim; i++) + { + Dsymbol *sm = (Dsymbol *)tm->members->data[i]; + if (Dsymbol_canThrow(sm, mustNotThrow)) + return 1; + } + } + } + else if ((td = s->isTupleDeclaration()) != NULL) + { + for (size_t i = 0; i < td->objects->dim; i++) + { Object *o = (Object *)td->objects->data[i]; + if (o->dyncast() == DYNCAST_EXPRESSION) + { Expression *eo = (Expression *)o; + if (eo->op == TOKdsymbol) + { DsymbolExp *se = (DsymbolExp *)eo; + if (Dsymbol_canThrow(se->s, mustNotThrow)) + return 1; + } + } + } } return 0; } + + +int DeclarationExp::canThrow(bool mustNotThrow) +{ + return Dsymbol_canThrow(declaration, mustNotThrow); +} #endif void DeclarationExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) @@ -6069,7 +6243,8 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) } else { - e1 = resolveProperties(sc, e1); + if (e1->op != TOKtype) + e1 = resolveProperties(sc, e1); eleft = NULL; eright = e1; } @@ -6386,9 +6561,9 @@ Expression *DotVarExp::semantic(Scope *sc) } assert(type); + Type *t1 = e1->type; if (!var->isFuncDeclaration()) // for functions, do checks after overload resolution { - Type *t1 = e1->type; if (t1->ty == Tpointer) t1 = t1->nextOf(); @@ -6401,11 +6576,27 @@ Expression *DotVarExp::semantic(Scope *sc) accessCheck(loc, sc, e1, var); VarDeclaration *v = var->isVarDeclaration(); + if (v) + checkPurity(sc, v, e1); Expression *e = expandVar(WANTvalue, v); if (e) return e; } + Dsymbol *s; + if (sc->func && !sc->intypeof && t1->hasPointers() && + (s = t1->toDsymbol(sc)) != NULL) + { + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (ad && ad->hasUnions) + { + if (sc->func->setUnsafe()) + { error("union %s containing pointers are not allowed in @safe functions", t1->toChars()); + goto Lerr; + } + } + } } + //printf("-DotVarExp::semantic('%s')\n", toChars()); return this; @@ -6976,7 +7167,8 @@ Lagain: } if (e1->op == TOKcomma) - { + { /* Rewrite (a,b)(args) as (a,(b(args))) + */ CommaExp *ce = (CommaExp *)e1; e1 = ce->e2; @@ -7334,13 +7526,15 @@ Lagain: { TypeDelegate *td = (TypeDelegate *)t1; assert(td->next->ty == Tfunction); tf = (TypeFunction *)(td->next); - if (sc->func && sc->func->isPure() && !tf->purity && !(sc->flags & SCOPEdebug)) + if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug)) { - error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars()); + if (sc->func->setImpure()) + error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars()); } - if (sc->func && sc->func->isSafe() && tf->trust <= TRUSTsystem) + if (sc->func && tf->trust <= TRUSTsystem) { - error("safe function '%s' cannot call system delegate '%s'", sc->func->toChars(), e1->toChars()); + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system delegate '%s'", sc->func->toChars(), e1->toChars()); } goto Lcheckargs; } @@ -7348,13 +7542,15 @@ Lagain: { Expression *e = new PtrExp(loc, e1); t1 = ((TypePointer *)t1)->next; - if (sc->func && sc->func->isPure() && !((TypeFunction *)t1)->purity && !(sc->flags & SCOPEdebug)) + if (sc->func && !((TypeFunction *)t1)->purity && !(sc->flags & SCOPEdebug)) { - error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars()); + if (sc->func->setImpure()) + error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars()); } - if (sc->func && sc->func->isSafe() && !((TypeFunction *)t1)->trust <= TRUSTsystem) + if (sc->func && !((TypeFunction *)t1)->trust <= TRUSTsystem) { - error("safe function '%s' cannot call system function pointer '%s'", sc->func->toChars(), e1->toChars()); + if (sc->func->setUnsafe()) + error("safe function '%s' cannot call system function pointer '%s'", sc->func->toChars(), e1->toChars()); } e->type = t1; e1 = e; @@ -7452,31 +7648,43 @@ Lcheckargs: int CallExp::checkSideEffect(int flag) { #if DMDV2 - if (flag != 2) - return 1; + int result = 1; - if (e1->checkSideEffect(2)) - return 1; + /* Calling a function or delegate that is pure nothrow + * has no side effects. + */ + if (e1->type) + { + Type *t = e1->type->toBasetype(); + if ((t->ty == Tfunction && ((TypeFunction *)t)->purity > PUREweak && + ((TypeFunction *)t)->isnothrow) + || + (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->purity > PUREweak && + ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) + ) + { + result = 0; + //if (flag == 0) + //warning("pure nothrow function %s has no effect", e1->toChars()); + } + else + result = 1; + } + + result |= e1->checkSideEffect(1); /* If any of the arguments have side effects, this expression does */ for (size_t i = 0; i < arguments->dim; i++) { Expression *e = (Expression *)arguments->data[i]; - if (e->checkSideEffect(2)) - return 1; + result |= e->checkSideEffect(1); } - /* If calling a function or delegate that is typed as pure, - * then this expression has no side effects. - */ - Type *t = e1->type->toBasetype(); - if (t->ty == Tfunction && ((TypeFunction *)t)->purity > PUREweak) - return 0; - if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->purity > PUREweak) - return 0; -#endif + return result; +#else return 1; +#endif } #if DMDV2 @@ -7488,12 +7696,8 @@ int CallExp::canThrow(bool mustNotThrow) /* If any of the arguments can throw, then this expression can throw */ - for (size_t i = 0; i < arguments->dim; i++) - { Expression *e = (Expression *)arguments->data[i]; - - if (e && e->canThrow(mustNotThrow)) - return 1; - } + if (arrayExpressionCanThrow(arguments, mustNotThrow)) + return 1; if (global.errors && !e1->type) return 0; // error recovery @@ -7615,7 +7819,11 @@ Expression *AddrExp::semantic(Scope *sc) m = sc->module; #endif UnaExp::semantic(sc); + if (e1->type == Type::terror) + return new ErrorExp(); e1 = e1->toLvalue(sc, NULL); + if (e1->op == TOKerror) + return e1; if (!e1->type) { error("cannot take address of %s", e1->toChars()); @@ -7656,9 +7864,23 @@ Expression *AddrExp::semantic(Scope *sc) VarExp *ve = (VarExp *)e1; VarDeclaration *v = ve->var->isVarDeclaration(); - if (v && !v->canTakeAddressOf()) - { error("cannot take address of %s", e1->toChars()); - return new ErrorExp(); + if (v) + { + if (!v->canTakeAddressOf()) + { error("cannot take address of %s", e1->toChars()); + return new ErrorExp(); + } + + if (sc->func && !sc->intypeof && !v->isDataseg()) + { + if (sc->func->setUnsafe()) + { + error("cannot take address of %s %s in @safe function %s", + v->isParameter() ? "parameter" : "local", + v->toChars(), + sc->func->toChars()); + } + } } FuncDeclaration *f = ve->var->isFuncDeclaration(); @@ -8133,8 +8355,7 @@ Expression *CastExp::semantic(Scope *sc) Type *t1b = e1->type->toBasetype(); Type *tob = to->toBasetype(); if (tob->ty == Tstruct && - !tob->equals(t1b) && - ((TypeStruct *)tob)->sym->search(0, Id::call, 0) + !tob->equals(t1b) ) { /* Look to replace: @@ -8145,10 +8366,10 @@ Expression *CastExp::semantic(Scope *sc) // Rewrite as to.call(e1) e = new TypeExp(loc, to); - e = new DotIdExp(loc, e, Id::call); e = new CallExp(loc, e, e1); - e = e->semantic(sc); - return e; + e = e->trySemantic(sc); + if (e) + return e; } // Struct casts are possible only when the sizes match @@ -8173,52 +8394,52 @@ Expression *CastExp::semantic(Scope *sc) return new ErrorExp(); } -#if 1 - if (sc->func && sc->func->isSafe() && !sc->intypeof) -#else - if (global.params.safe && !sc->module->safe && !sc->intypeof) -#endif + // Check for unsafe casts + if (sc->func && !sc->intypeof) { // Disallow unsafe casts Type *tob = to->toBasetype(); Type *t1b = e1->type->toBasetype(); + + // Implicit conversions are always safe + if (t1b->implicitConvTo(tob)) + goto Lsafe; + if (!t1b->isMutable() && tob->isMutable()) - { // Cast not mutable to mutable - Lunsafe: - error("cast from %s to %s not allowed in safe code", e1->type->toChars(), to->toChars()); - return new ErrorExp(); - } - else if (t1b->isShared() && !tob->isShared()) + goto Lunsafe; + + if (t1b->isShared() && !tob->isShared()) // Cast away shared goto Lunsafe; - else if (tob->ty == Tpointer) - { if (t1b->ty != Tpointer) - goto Lunsafe; - Type *tobn = tob->nextOf()->toBasetype(); - Type *t1bn = t1b->nextOf()->toBasetype(); - if (!t1bn->isMutable() && tobn->isMutable()) - // Cast away pointer to not mutable - goto Lunsafe; + if (!tob->hasPointers()) + goto Lsafe; - if (t1bn->isShared() && !tobn->isShared()) - // Cast away pointer to shared - goto Lunsafe; - - if (t1bn->isWild() && !tobn->isConst() && !tobn->isWild()) - // Cast wild to anything but const | wild - goto Lunsafe; - - if (tobn->isTypeBasic() && tobn->size() < t1bn->size()) - // Allow things like casting a long* to an int* - ; - else if (tobn->ty != Tvoid) - // Cast to a pointer other than void* - goto Lunsafe; + if (tob->ty == Tarray && t1b->ty == Tarray) + { + Type* tobn = tob->nextOf()->toBasetype(); + Type* t1bn = t1b->nextOf()->toBasetype(); + if (!tobn->hasPointers() && MODimplicitConv(t1bn->mod, tobn->mod)) + goto Lsafe; + } + if (tob->ty == Tpointer && t1b->ty == Tpointer) + { + Type* tobn = tob->nextOf()->toBasetype(); + Type* t1bn = t1b->nextOf()->toBasetype(); + if (!tobn->hasPointers() && + tobn->ty != Tfunction && t1bn->ty != Tfunction && + tobn->size() <= t1bn->size() && + MODimplicitConv(t1bn->mod, tobn->mod)) + goto Lsafe; } - // BUG: Check for casting array types, such as void[] to int*[] + Lunsafe: + if (sc->func->setUnsafe()) + { error("cast from %s to %s not allowed in safe code", e1->type->toChars(), to->toChars()); + return new ErrorExp(); + } } +Lsafe: e = e1->castTo(sc, to); return e; } @@ -8530,6 +8751,13 @@ void SliceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writeByte(']'); } +int SliceExp::canThrow(bool mustNotThrow) +{ + return UnaExp::canThrow(mustNotThrow) + || (lwr != NULL && lwr->canThrow(mustNotThrow)) + || (upr != NULL && upr->canThrow(mustNotThrow)); +} + /********************** ArrayLength **************************************/ ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1) @@ -8996,7 +9224,7 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) modifiable = 1; if (e1->op == TOKstring) error("string literals are immutable"); - if (type && !type->isMutable()) + if (type && (!type->isMutable() || !type->isAssignable())) error("%s isn't mutable", e->toChars()); Type *t1 = e1->type->toBasetype(); if (t1->ty == Taarray) @@ -9192,6 +9420,7 @@ Expression *AssignExp::semantic(Scope *sc) e = e->semantic(sc); return e; } +#if 0 // Turned off to allow rewriting (a[i]=value) to (a.opIndex(i)=value) else { // Rewrite (a[i] = value) to (a.opIndex(i, value)) @@ -9208,6 +9437,7 @@ Expression *AssignExp::semantic(Scope *sc) return e; } } +#endif } } /* Look for operator overloading of a[i..j]=value. @@ -9304,10 +9534,25 @@ Expression *AssignExp::semantic(Scope *sc) Type *t1 = e1->type->toBasetype(); if (t1->ty == Tfunction) - { // Rewrite f=value to f(value) - Expression *e = new CallExp(loc, e1, e2); - e = e->semantic(sc); - return e; + { /* We have f=value. + * Could mean: + * f() = value + * or: + * f(value) + */ + TypeFunction *tf = (TypeFunction *)t1; + if (tf->isref) + { + // Rewrite e1 = e2 to e1() = e2 + e1 = resolveProperties(sc, e1); + } + else + { + // Rewrite f=value to f(value) + Expression *e = new CallExp(loc, e1, e2); + e = e->semantic(sc); + return e; + } } /* If it is an assignment from a 'foreign' type, @@ -9330,6 +9575,8 @@ Expression *AssignExp::semantic(Scope *sc) VarDeclaration *v = new VarDeclaration(loc, aaValueType, id, new VoidInitializer(NULL)); v->storage_class |= STCctfe; + v->semantic(sc); + v->parent = sc->parent; Expression *de = new DeclarationExp(loc, v); VarExp *ve = new VarExp(loc, v); @@ -9391,24 +9638,8 @@ Expression *AssignExp::semantic(Scope *sc) { /* The struct value returned from the function is transferred * so should not call the destructor on it. - * ((S _ctmp = S.init), _ctmp).this(...) */ - CallExp *ce = (CallExp *)e2; - if (ce->e1->op == TOKdotvar) - { DotVarExp *dve = (DotVarExp *)ce->e1; - if (dve->var->isCtorDeclaration()) - { // It's a constructor call - if (dve->e1->op == TOKcomma) - { CommaExp *comma = (CommaExp *)dve->e1; - if (comma->e2->op == TOKvar) - { VarExp *ve = (VarExp *)comma->e2; - VarDeclaration *ctmp = ve->var->isVarDeclaration(); - if (ctmp) - ctmp->noscope = 1; - } - } - } - } + valueNoDtor(e2); } } } @@ -9455,7 +9686,7 @@ Expression *AssignExp::semantic(Scope *sc) else if (e1->op == TOKslice) { Type *tn = e1->type->nextOf(); - if (tn && !tn->isMutable() && op != TOKconstruct) + if (op == TOKassign && tn && (!tn->isMutable() || !tn->isAssignable())) { error("slice %s is not mutable", e1->toChars()); return new ErrorExp(); } @@ -9491,7 +9722,8 @@ Expression *AssignExp::semantic(Scope *sc) } //error("cannot assign to static array %s", e1->toChars()); } - else if (e1->op == TOKslice) + else if (e1->op == TOKslice && t2->toBasetype()->ty == Tarray && + t2->toBasetype()->nextOf()->implicitConvTo(t1->nextOf())) { e2 = e2->implicitCastTo(sc, e1->type->constOf()); } @@ -9591,7 +9823,7 @@ Expression *AddAssignExp::semantic(Scope *sc) e1->checkNoBool(); if (tb1->ty == Tpointer && tb2->isintegral()) e = scaleFactor(sc); - else if (tb1->ty == Tbit || tb1->ty == Tbool) + else if (tb1->ty == Tbool) { #if 0 // Need to rethink this diff --git a/dmd2/expression.h b/dmd2/expression.h index 58098bf8..53ae8de3 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -15,6 +15,7 @@ #include "identifier.h" #include "lexer.h" #include "arraytypes.h" +#include "intrange.h" struct Type; struct Scope; @@ -79,7 +80,7 @@ void initPrecedence(); Expression *resolveProperties(Scope *sc, Expression *e); void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d); -Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, Identifier *id, Objects *targsi = NULL); +Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, Dsymbol *d); Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid); void inferApplyArgTypes(enum TOK op, Parameters *arguments, Expression *aggr, Module *from); void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); @@ -88,6 +89,7 @@ void expandTuples(Expressions *exps); FuncDeclaration *hasThis(Scope *sc); Expression *fromConstInitializer(int result, Expression *e); int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow); +void valueNoDtor(Expression *e); /* Interpreter: what form of return value expression is required? */ @@ -95,14 +97,10 @@ enum CtfeGoal { ctfeNeedRvalue, // Must return an Rvalue ctfeNeedLvalue, // Must return an Lvalue ctfeNeedAnyValue, // Can return either an Rvalue or an Lvalue + ctfeNeedLvalueRef,// Must return a reference to an Lvalue (for ref types) ctfeNeedNothing // The return value is not required }; -struct IntRange -{ uinteger_t imin; - uinteger_t imax; -}; - struct Expression : Object { Loc loc; // file location @@ -152,6 +150,7 @@ struct Expression : Object Expression *checkArithmetic(); void checkDeprecated(Scope *sc, Dsymbol *s); void checkPurity(Scope *sc, FuncDeclaration *f); + void checkPurity(Scope *sc, VarDeclaration *v, Expression *e1); void checkSafety(Scope *sc, FuncDeclaration *f); virtual Expression *checkToBoolean(Scope *sc); virtual Expression *addDtorHook(Scope *sc); @@ -301,9 +300,7 @@ struct ComplexExp : Expression int isBool(int result); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); -#ifdef _DH OutBuffer hexp; -#endif #if IN_DMD elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); @@ -1161,11 +1158,10 @@ struct AddrExp : UnaExp MATCH implicitConvTo(Type *t); Expression *castTo(Scope *sc, Type *t); Expression *optimize(int result); - + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_LLVM DValue* toElem(IRState* irs); llvm::Constant *toConstElem(IRState *irs); - Expression *interpret(InterState *istate, CtfeGoal goal); #endif }; @@ -1202,6 +1198,7 @@ struct NegExp : UnaExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading Identifier *opId(); @@ -1232,6 +1229,7 @@ struct ComExp : UnaExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading Identifier *opId(); @@ -1352,6 +1350,7 @@ struct SliceExp : UnaExp void scanForNestedRef(Scope *sc); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + int canThrow(bool mustNotThrow); int inlineCost(InlineCostState *ics); Expression *doInline(InlineDoState *ids); @@ -1579,6 +1578,7 @@ struct AddExp : BinExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading int isCommutative(); @@ -1603,6 +1603,7 @@ struct MinExp : BinExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading Identifier *opId(); @@ -1646,6 +1647,7 @@ struct MulExp : BinExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading int isCommutative(); @@ -1692,6 +1694,7 @@ struct ModExp : BinExp Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); + IntRange getIntRange(); // For operator overloading Identifier *opId(); @@ -1917,6 +1920,7 @@ struct InExp : BinExp { InExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBit(); // For operator overloading diff --git a/dmd2/func.c b/dmd2/func.c index 71b046c7..557d504b 100644 --- a/dmd2/func.c +++ b/dmd2/func.c @@ -64,7 +64,6 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla naked = 0; inlineStatus = ILSuninitialized; inlineNest = 0; - inlineAsm = 0; cantInterpret = 0; isArrayOp = 0; semanticRun = PASSinit; @@ -87,13 +86,14 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla #if DMDV2 builtin = BUILTINunknown; tookAddressOf = 0; + flags = 0; #endif #if IN_LLVM // LDC isArrayOp = false; allowInlining = false; - availableExternally = true; // assume this unless proven otherwise + inlineAsm = true; // function types in ldc don't merge if the context parameter differs // so we actually don't care about the function declaration, but only @@ -201,7 +201,7 @@ void FuncDeclaration::semantic(Scope *sc) { sc = sc->push(); sc->stc |= storage_class & (STCref | STCnothrow | STCpure | STCdisable - | STCsafe | STCtrusted | STCsystem); // forward to function type + | STCsafe | STCtrusted | STCsystem | STCproperty); // forward to function type if (isCtorDeclaration()) sc->flags |= SCOPEctor; @@ -282,16 +282,31 @@ void FuncDeclaration::semantic(Scope *sc) linkage = sc->linkage; protection = sc->protection; + /* Purity and safety can be inferred for some functions by examining + * the function body. + */ + if (fbody && + (isFuncLiteralDeclaration() || parent->isTemplateInstance())) + { + if (f->purity == PUREimpure && // purity not specified + !f->hasLazyParameters() + ) + { + flags |= FUNCFLAGpurityInprocess; + } + if (f->trust == TRUSTdefault) + flags |= FUNCFLAGsafetyInprocess; + + if (!f->isnothrow) + flags |= FUNCFLAGnothrowInprocess; + } + if (storage_class & STCscope) error("functions cannot be scope"); if (isAbstract() && !isVirtual()) error("non-virtual functions cannot be abstract"); - // https://github.com/donc/dmd/commit/9f7b2f8cfe5d7482f2de7f9678c176d54abe237f#commitcomment-321724 - //if (isOverride() && !isVirtual()) - //error("cannot override a non-virtual function"); - if ((f->isConst() || f->isImmutable()) && !isThis()) error("without 'this' cannot be const/immutable"); @@ -862,6 +877,20 @@ void FuncDeclaration::semantic3(Scope *sc) } #endif + if (frequire) + { + for (int i = 0; i < foverrides.dim; i++) + { + FuncDeclaration *fdv = (FuncDeclaration *)foverrides.data[i]; + + if (fdv->fbody && !fdv->frequire) + { + error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars()); + break; + } + } + } + frequire = mergeFrequire(frequire); fensure = mergeFensure(fensure); @@ -1393,7 +1422,11 @@ void FuncDeclaration::semantic3(Scope *sc) } else if (!hasReturnExp && type->nextOf()->ty != Tvoid) error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars()); - else if (!inlineAsm) + else if (hasReturnExp & 8) // if inline asm + { + flags &= ~FUNCFLAGnothrowInprocess; + } + else { #if DMDV2 // Check for errors related to 'nothrow'. @@ -1401,6 +1434,12 @@ void FuncDeclaration::semantic3(Scope *sc) int blockexit = fbody ? fbody->blockExit(f->isnothrow) : BEfallthru; if (f->isnothrow && (global.errors != nothrowErrors) ) error("'%s' is nothrow yet may throw", toChars()); + if (flags & FUNCFLAGnothrowInprocess) + { + flags &= ~FUNCFLAGnothrowInprocess; + if (!(blockexit & BEthrow)) + f->isnothrow = TRUE; + } int offend = blockexit & BEfallthru; #endif @@ -1545,15 +1584,10 @@ void FuncDeclaration::semantic3(Scope *sc) // Merge contracts together with body into one compound statement -#ifdef _DH if (frequire && global.params.useIn) { frequire->incontract = 1; a->push(frequire); } -#else - if (frequire && global.params.useIn) - a->push(frequire); -#endif // Precondition invariant if (addPreInvariant()) @@ -1775,6 +1809,20 @@ void FuncDeclaration::semantic3(Scope *sc) sc2->pop(); } + /* If function survived being marked as impure, then it is pure + */ + if (flags & FUNCFLAGpurityInprocess) + { + flags &= ~FUNCFLAGpurityInprocess; + f->purity = PUREfwdref; + } + + if (flags & FUNCFLAGsafetyInprocess) + { + flags &= ~FUNCFLAGsafetyInprocess; + f->trust = TRUSTsafe; + } + if (global.gag && global.errors != nerrors) semanticRun = PASSsemanticdone; // Ensure errors get reported again else @@ -1893,7 +1941,7 @@ Statement *FuncDeclaration::mergeFrequire(Statement *sf) } sf = fdv->mergeFrequire(sf); - if (fdv->fdrequire) + if (sf && fdv->fdrequire) { //printf("fdv->frequire: %s\n", fdv->frequire->toChars()); /* Make the call: @@ -1904,15 +1952,13 @@ Statement *FuncDeclaration::mergeFrequire(Statement *sf) Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, 0), eresult); Statement *s2 = new ExpStatement(loc, e); - if (sf) - { Catch *c = new Catch(loc, NULL, NULL, sf); - Array *catches = new Array(); - catches->push(c); - sf = new TryCatchStatement(loc, s2, catches); - } - else - sf = s2; + Catch *c = new Catch(loc, NULL, NULL, sf); + Array *catches = new Array(); + catches->push(c); + sf = new TryCatchStatement(loc, s2, catches); } + else + return NULL; } return sf; } @@ -2786,9 +2832,11 @@ enum PURE FuncDeclaration::isPure() //printf("FuncDeclaration::isPure() '%s'\n", toChars()); assert(type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)type; - enum PURE purity = tf->purity; - if (purity == PUREfwdref) + if (flags & FUNCFLAGpurityInprocess) + setImpure(); + if (tf->purity == PUREfwdref) tf->purityLevel(); + enum PURE purity = tf->purity; if (purity > PUREweak && needThis()) { // The attribute of the 'this' reference affects purity strength if (type->mod & (MODimmutable | MODwild)) @@ -2798,21 +2846,61 @@ enum PURE FuncDeclaration::isPure() else purity = PUREweak; } + tf->purity = purity; + // ^ This rely on the current situation that every FuncDeclaration has a + // unique TypeFunction. return purity; } +/************************************** + * The function is doing something impure, + * so mark it as impure. + * If there's a purity error, return TRUE. + */ +bool FuncDeclaration::setImpure() +{ + if (flags & FUNCFLAGpurityInprocess) + { + flags &= ~FUNCFLAGpurityInprocess; + } + else if (isPure()) + return TRUE; + return FALSE; +} + int FuncDeclaration::isSafe() { assert(type->ty == Tfunction); + if (flags & FUNCFLAGsafetyInprocess) + setUnsafe(); return ((TypeFunction *)type)->trust == TRUSTsafe; } int FuncDeclaration::isTrusted() { assert(type->ty == Tfunction); + if (flags & FUNCFLAGsafetyInprocess) + setUnsafe(); return ((TypeFunction *)type)->trust == TRUSTtrusted; } +/************************************** + * The function is doing something unsave, + * so mark it as unsafe. + * If there's a safe error, return TRUE. + */ +bool FuncDeclaration::setUnsafe() +{ + if (flags & FUNCFLAGsafetyInprocess) + { + flags &= ~FUNCFLAGsafetyInprocess; + ((TypeFunction *)type)->trust = TRUSTsystem; + } + else if (isSafe()) + return TRUE; + return FALSE; +} + // Determine if function needs // a static frame pointer to its lexically enclosing function @@ -2924,6 +3012,8 @@ int FuncDeclaration::needsClosure() * 1) is a virtual function * 2) has its address taken * 3) has a parent that escapes + * -or- + * 4) this function returns a local struct/class * * Note that since a non-virtual function can be called by * a virtual one, if that non-virtual function accesses a closure @@ -2954,6 +3044,25 @@ int FuncDeclaration::needsClosure() } } } + + /* Look for case (4) + */ + if (closureVars.dim) + { + assert(type->ty == Tfunction); + Type *tret = ((TypeFunction *)type)->next; + assert(tret); + tret = tret->toBasetype(); + if (tret->ty == Tclass || tret->ty == Tstruct) + { Dsymbol *st = tret->toDsymbol(NULL); + for (Dsymbol *s = st->parent; s; s = s->parent) + { + if (s == this) + goto Lyes; + } + } + } + return 0; Lyes: @@ -2978,12 +3087,6 @@ Parameters *FuncDeclaration::getParameters(int *pvarargs) fparameters = fdtype->parameters; fvarargs = fdtype->varargs; } - else // Constructors don't have type's - { CtorDeclaration *fctor = isCtorDeclaration(); - assert(fctor); - fparameters = fctor->arguments; - fvarargs = fctor->varargs; - } if (pvarargs) *pvarargs = fvarargs; return fparameters; @@ -3074,17 +3177,15 @@ void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) /********************************* CtorDeclaration ****************************/ -CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs, StorageClass stc) - : FuncDeclaration(loc, endloc, Id::ctor, stc, NULL) +CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type) + : FuncDeclaration(loc, endloc, Id::ctor, stc, type) { - this->arguments = arguments; - this->varargs = varargs; //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); } Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) { - CtorDeclaration *f = new CtorDeclaration(loc, endloc, NULL, varargs, storage_class); + CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy()); f->outId = outId; f->frequire = frequire ? frequire->syntaxCopy() : NULL; @@ -3092,7 +3193,6 @@ Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) f->fbody = fbody ? fbody->syntaxCopy() : NULL; assert(!fthrows); // deprecated - f->arguments = Parameter::arraySyntaxCopy(arguments); return f; } @@ -3100,6 +3200,10 @@ Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) void CtorDeclaration::semantic(Scope *sc) { //printf("CtorDeclaration::semantic() %s\n", toChars()); + TypeFunction *tf = (TypeFunction *)type; + assert(tf && tf->ty == Tfunction); + Expressions *fargs = ((TypeFunction *)type)->fargs; // for auto ref + sc = sc->push(); sc->stc &= ~STCstatic; // not a static constructor @@ -3118,15 +3222,16 @@ void CtorDeclaration::semantic(Scope *sc) assert(tret); tret = tret->addStorageClass(storage_class | sc->stc); } - if (!type) - type = new TypeFunction(arguments, tret, varargs, LINKd, storage_class | sc->stc); + tf = new TypeFunction(tf->parameters, tret, tf->varargs, LINKd, storage_class | sc->stc); + tf->fargs = fargs; + type = tf; #if STRUCTTHISREF if (ad && ad->isStructDeclaration()) { ((TypeFunction *)type)->isref = 1; if (!originalType) // Leave off the "ref" - originalType = new TypeFunction(arguments, tret, varargs, LINKd, storage_class | sc->stc); + originalType = new TypeFunction(tf->parameters, tret, tf->varargs, LINKd, storage_class | sc->stc); } #endif if (!originalType) @@ -3149,7 +3254,7 @@ void CtorDeclaration::semantic(Scope *sc) sc->pop(); // See if it's the default constructor - if (ad && varargs == 0 && Parameter::dim(arguments) == 0) + if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0) { if (ad->isStructDeclaration()) error("default constructor not allowed for structs"); else @@ -3185,10 +3290,13 @@ int CtorDeclaration::addPostInvariant() void CtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { + TypeFunction *tf = (TypeFunction *)type; + assert(tf && tf->ty == Tfunction); + if (originalType && originalType->ty == Tfunction) ((TypeFunction *)originalType)->attributesToCBuffer(buf, 0); buf->writestring("this"); - Parameter::argsToCBuffer(buf, hgs, arguments, varargs); + Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); bodyToCBuffer(buf, hgs); } diff --git a/dmd2/hdrgen.c b/dmd2/hdrgen.c index 63d70667..e11c212f 100644 --- a/dmd2/hdrgen.c +++ b/dmd2/hdrgen.c @@ -10,8 +10,6 @@ // Routines to emit header files -#ifdef _DH - #define PRETTY_PRINT #define TEST_EMIT_ALL 0 // For Testing @@ -100,5 +98,3 @@ void Dsymbol::toHBuffer(OutBuffer *buf, HdrGenState *hgs) /*************************************/ - -#endif // #ifdef _DH diff --git a/dmd2/identifier.h b/dmd2/identifier.h index 1ba817a2..d8796000 100644 --- a/dmd2/identifier.h +++ b/dmd2/identifier.h @@ -36,9 +36,7 @@ struct Identifier : Object int compare(Object *o); void print(); char *toChars(); -#ifdef _DH char *toHChars(); -#endif const char *toHChars2(); int dyncast(); diff --git a/dmd2/impcnvgen.c b/dmd2/impcnvgen.c index c4d9ac9f..8f182364 100644 --- a/dmd2/impcnvgen.c +++ b/dmd2/impcnvgen.c @@ -23,7 +23,6 @@ int integral_promotion(int t) { case Tchar: case Twchar: - //case Tbit: case Tbool: case Tint8: case Tuns8: @@ -51,29 +50,6 @@ void init() impcnvType1[t1][t2] = nt1; \ impcnvType2[t1][t2] = nt2; - /* ======================= */ - -#if 0 - X(Tbit,Tbit, Tint32,Tint32, Tint32) - X(Tbit,Tint8, Tint32,Tint32, Tint32) - X(Tbit,Tuns8, Tint32,Tint32, Tint32) - X(Tbit,Tint16, Tint32,Tint32, Tint32) - X(Tbit,Tuns16, Tint32,Tint32, Tint32) - X(Tbit,Tint32, Tint32,Tint32, Tint32) - X(Tbit,Tuns32, Tuns32,Tuns32, Tuns32) - X(Tbit,Tint64, Tint64,Tint64, Tint64) - X(Tbit,Tuns64, Tuns64,Tuns64, Tuns64) - - X(Tbit,Tfloat32, Tfloat32,Tfloat32, Tfloat32) - X(Tbit,Tfloat64, Tfloat64,Tfloat64, Tfloat64) - X(Tbit,Tfloat80, Tfloat80,Tfloat80, Tfloat80) - X(Tbit,Timaginary32, Tfloat32,Timaginary32, Tfloat32) - X(Tbit,Timaginary64, Tfloat64,Timaginary64, Tfloat64) - X(Tbit,Timaginary80, Tfloat80,Timaginary80, Tfloat80) - X(Tbit,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32) - X(Tbit,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64) - X(Tbit,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80) -#endif /* ======================= */ @@ -322,17 +298,6 @@ void init() #define Y(t1,t2) impcnvWarn[t1][t2] = 1; -#if 0 - Y(Tint8, Tbit) - Y(Tuns8, Tbit) - Y(Tint16, Tbit) - Y(Tuns16, Tbit) - Y(Tint32, Tbit) - Y(Tuns32, Tbit) - Y(Tint64, Tbit) - Y(Tuns64, Tbit) -#endif - Y(Tuns8, Tint8) Y(Tint16, Tint8) Y(Tuns16, Tint8) diff --git a/dmd2/import.c b/dmd2/import.c index 8a970d15..18b2850c 100644 --- a/dmd2/import.c +++ b/dmd2/import.c @@ -35,9 +35,6 @@ Import::Import(Loc loc, Array *packages, Identifier *id, Identifier *aliasId, this->id = id; this->aliasId = aliasId; this->isstatic = isstatic; -#if IN_LLVM - protection = PROTundefined; -#endif pkg = NULL; mod = NULL; @@ -65,12 +62,6 @@ const char *Import::kind() return isstatic ? (char *)"static import" : (char *)"import"; } -#if IN_LLVM -enum PROT Import::prot() -{ - return protection; -} -#endif Dsymbol *Import::syntaxCopy(Dsymbol *s) { @@ -207,14 +198,13 @@ void Import::semantic(Scope *sc) sc = sc->push(mod); for (size_t i = 0; i < aliasdecls.dim; i++) - { AliasDeclaration *ad = (AliasDeclaration *)aliasdecls.data[i]; + { Dsymbol *s = (Dsymbol *)aliasdecls.data[i]; //printf("\tImport alias semantic('%s')\n", s->toChars()); if (!mod->search(loc, (Identifier *)names.data[i], 0)) error("%s not found", ((Identifier *)names.data[i])->toChars()); - ad->importprot = protection; - ad->semantic(sc); + s->semantic(sc); } sc = sc->pop(); } @@ -288,7 +278,7 @@ void Import::semantic(Scope *sc) ob->writenl(); } - //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); + //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); } void Import::semantic2(Scope *sc) diff --git a/dmd2/import.h b/dmd2/import.h index 2ca47e37..4f442de5 100644 --- a/dmd2/import.h +++ b/dmd2/import.h @@ -24,9 +24,7 @@ struct OutBuffer; struct Module; struct Package; struct AliasDeclaration; -#ifdef _DH struct HdrGenState; -#endif struct Import : Dsymbol { @@ -34,9 +32,6 @@ struct Import : Dsymbol Identifier *id; // module Identifier Identifier *aliasId; int isstatic; // !=0 if static import -#if IN_LLVM - enum PROT protection; -#endif // Pairs of alias=name to bind into current namespace Array names; @@ -52,9 +47,6 @@ struct Import : Dsymbol void addAlias(Identifier *name, Identifier *alias); const char *kind(); -#if IN_LLVM - enum PROT prot(); -#endif Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees void load(Scope *sc); void importAll(Scope *sc); diff --git a/dmd2/inifile.c b/dmd2/inifile.c index 2d74918d..ede7a76c 100644 --- a/dmd2/inifile.c +++ b/dmd2/inifile.c @@ -1,6 +1,6 @@ /* * Some portions copyright (c) 1994-1995 by Symantec - * Copyright (c) 1999-2009 by Digital Mars + * Copyright (c) 1999-2011 by Digital Mars * All Rights Reserved * http://www.digitalmars.com * Written by Walter Bright @@ -22,7 +22,7 @@ #if __APPLE__ #include #endif -#if __FreeBSD__ || __sun&&__SVR4 +#if __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 // for PATH_MAX #include #endif @@ -110,12 +110,12 @@ const char *inifile(const char *argv0x, const char *inifilex) filename = (char *)FileName::replaceName(argv0, inifile); if (!FileName::exists(filename)) { -#if linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4 -#if __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun&&__SVR4 // This fix by Thomas Kuehne +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#if __GLIBC__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 // This fix by Thomas Kuehne /* argv0 might be a symbolic link, * so try again looking past it to the real path */ -#if __APPLE__ || __FreeBSD__ || __sun&&__SVR4 +#if __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 char resolved_name[PATH_MAX + 1]; char* real_argv0 = realpath(argv0, resolved_name); #else diff --git a/dmd2/init.c b/dmd2/init.c index 3b81f27b..72e2bb18 100644 --- a/dmd2/init.c +++ b/dmd2/init.c @@ -726,6 +726,61 @@ Initializer *ExpInitializer::syntaxCopy() return new ExpInitializer(loc, exp->syntaxCopy()); } +bool arrayHasNonConstPointers(Expressions *elems); + +bool hasNonConstPointers(Expression *e) +{ + if (e->op == TOKnull) + return false; + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + return arrayHasNonConstPointers(se->elements); + } + if (e->op == TOKarrayliteral) + { + if (!e->type->nextOf()->hasPointers()) + return false; + ArrayLiteralExp *ae = (ArrayLiteralExp *)e; + return arrayHasNonConstPointers(ae->elements); + } + if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e; + if (ae->type->nextOf()->hasPointers() && + arrayHasNonConstPointers(ae->values)) + return true; + if (((TypeAArray *)ae->type)->index->hasPointers()) + return arrayHasNonConstPointers(ae->keys); + return false; + } + if (e->type->ty== Tpointer && e->type->nextOf()->ty != Tfunction) + { + if (e->op == TOKsymoff) // address of a global is OK + return false; + if (e->op == TOKint64) // cast(void *)int is OK + return false; + if (e->op == TOKstring) // "abc".ptr is OK + return false; + return true; + } + return false; +} + +bool arrayHasNonConstPointers(Expressions *elems) +{ + for (size_t i = 0; i < elems->dim; i++) + { + if (!(Expression *)elems->data[i]) + continue; + if (hasNonConstPointers((Expression *)elems->data[i])) + return true; + } + return false; +} + + + Initializer *ExpInitializer::semantic(Scope *sc, Type *t, int needInterpret) { //printf("ExpInitializer::semantic(%s), type = %s\n", exp->toChars(), t->toChars()); @@ -737,6 +792,14 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, int needInterpret) exp = exp->optimize(wantOptimize); if (!global.gag && olderrors != global.errors) return this; // Failed, suppress duplicate error messages + + // Make sure all pointers are constants + if (needInterpret && hasNonConstPointers(exp)) + { + exp->error("cannot use non-constant CTFE pointer in an initializer '%s'", exp->toChars()); + return this; + } + Type *tb = t->toBasetype(); /* Look for case of initializing a static array with a too-short diff --git a/dmd2/init.h b/dmd2/init.h index 10f8d640..252dfa95 100644 --- a/dmd2/init.h +++ b/dmd2/init.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars +// Copyright (c) 1999-2007 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -26,9 +26,8 @@ struct VoidInitializer; struct StructInitializer; struct ArrayInitializer; struct ExpInitializer; -#ifdef _DH struct HdrGenState; -#endif + struct Initializer : Object { diff --git a/dmd2/inline.c b/dmd2/inline.c index 75fcac9e..824e9ee3 100644 --- a/dmd2/inline.c +++ b/dmd2/inline.c @@ -134,6 +134,11 @@ int ReturnStatement::inlineCost(InlineCostState *ics) return exp ? exp->inlineCost(ics) : 0; } +int ImportStatement::inlineCost(InlineCostState *ics) +{ + return 0; +} + /* -------------------------- */ int arrayInlineCost(InlineCostState *ics, Array *arguments) @@ -464,6 +469,11 @@ Expression *ReturnStatement::doInline(InlineDoState *ids) return exp ? exp->doInline(ids) : 0; } +Expression *ImportStatement::doInline(InlineDoState *ids) +{ + return NULL; +} + /* --------------------------------------------------------------- */ /****************************** @@ -1345,12 +1355,6 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan) } #if !IN_LLVM // LDC: Only extern(C) varargs count, and ctors use extern(D). - else - { CtorDeclaration *ctor = isCtorDeclaration(); - - if (ctor && ctor->varargs == 1) - goto Lno; - } #endif if ( diff --git a/dmd2/interpret.c b/dmd2/interpret.c index 2522073b..48209bba 100644 --- a/dmd2/interpret.c +++ b/dmd2/interpret.c @@ -25,6 +25,7 @@ #include "id.h" #define LOG 0 +#define LOGASSIGN 0 struct InterState { @@ -32,7 +33,10 @@ struct InterState FuncDeclaration *fd; // function being interpreted Dsymbols vars; // variables used in this function Statement *start; // if !=NULL, start execution at this statement - Statement *gotoTarget; // target of EXP_GOTO_INTERPRET result + Statement *gotoTarget; /* target of EXP_GOTO_INTERPRET result; also + * target of labelled EXP_BREAK_INTERPRET or + * EXP_CONTINUE_INTERPRET. (NULL if no label). + */ Expression *localThis; // value of 'this', or NULL if none bool awaitingLvalueReturn; // Support for ref return values: // Any return to this function should return an lvalue. @@ -55,6 +59,68 @@ Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclarati Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference = NULL); Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e, Expression *thisval); +void addVarToInterstate(InterState *istate, VarDeclaration *v); +bool needToCopyLiteral(Expression *expr); +Expression *copyLiteral(Expression *e); +Expression *paintTypeOntoLiteral(Type *type, Expression *lit); + + +// Used for debugging only +void showCtfeExpr(Expression *e) +{ + Expressions *elements = NULL; + // We need the struct definition to detect block assignment + StructDeclaration *sd = NULL; + if (e->op == TOKstructliteral) { + elements = ((StructLiteralExp *)e)->elements; + sd = ((StructLiteralExp *)e)->sd; + printf("STRUCT type = %s %p :\n", e->type->toChars(), e); + } + else if (e->op == TOKarrayliteral) + { + elements = ((ArrayLiteralExp *)e)->elements; + printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(), e); + } + else if (e->op == TOKassocarrayliteral) + { + printf("AA LITERAL type=%s %p:\n", e->type->toChars(), e); + } + else if (e->op == TOKstring) + { + printf(" STRING %s %p\n", e->toChars(), ((StringExp *)e)->string); + } + else if (e->op == TOKslice) + { + printf(" SLICE %p: %s\n", e, e->toChars()); + showCtfeExpr(((SliceExp *)e)->e1); + } + else printf(" VALUE %p: %s\n", e, e->toChars()); + + if (elements) + { + for (size_t i = 0; i < elements->dim; i++) + { Expression *z = (Expression *)elements->data[i]; + if (sd) + { + Dsymbol *s = (Dsymbol *)sd->fields.data[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + // If it is a void assignment, use the default initializer + if (!z) { + printf(" field:void\n"); + continue; + } + if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) + { + printf(" field: block initalized static array\n"); + continue; + } + } + showCtfeExpr(z); + } + } +} + /************************************* * Attempt to interpret a function given the arguments. @@ -126,8 +192,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument Expressions vsave; // place to save previous parameter values size_t dim = 0; if (needThis() && !thisarg) - { cantInterpret = 1; - // error, no this. Prevent segfault. + { // error, no this. Prevent segfault. error("need 'this' to access member %s", toChars()); return NULL; } @@ -154,11 +219,15 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument if (arg->storageClass & (STCout | STCref)) { - if (!istate) - { - earg->error("%s cannot be passed by reference at compile time", earg->toChars()); + if (!istate && (arg->storageClass & STCout)) + { // initializing an out parameter involves writing to it. + earg->error("global %s cannot be passed as an 'out' parameter at compile time", earg->toChars()); return NULL; } + // Convert all reference arguments into lvalue references + earg = earg->interpret(istate, ctfeNeedLvalueRef); + if (earg == EXP_CANT_INTERPRET) + return NULL; } else if (arg->storageClass & STClazy) { @@ -174,11 +243,9 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument */ earg = ((AddrExp *)earg)->e1; } - earg = earg->interpret(istate); // ? istate : &istatex); + earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) - { cantInterpret = 1; return NULL; - } } eargs.data[i] = earg; } @@ -196,14 +263,14 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument VarExp *ve = (VarExp *)earg; VarDeclaration *v2 = ve->var->isVarDeclaration(); if (!v2) - { cantInterpret = 1; + { + error("cannot interpret %s as a ref parameter", ve->toChars()); return NULL; } v->setValueWithoutChecking(earg); /* Don't restore the value of v2 upon function return */ - assert(istate); - for (size_t i = 0; i < istate->vars.dim; i++) + for (size_t i = 0; i < (istate ? istate->vars.dim : 0); i++) { VarDeclaration *vx = (VarDeclaration *)istate->vars.data[i]; if (vx == v2) { istate->vars.data[i] = NULL; @@ -215,8 +282,9 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument { // Value parameters and non-trivial references v->setValueWithoutChecking(earg); } -#if LOG +#if LOG || LOGASSIGN printf("interpreted arg[%d] = %s\n", i, earg->toChars()); + showCtfeExpr(earg); #endif } } @@ -238,15 +306,15 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument /* Save the values of the local variables used */ Expressions valueSaves; - if (istate && !isNested()) + if (istate) { //printf("saving local variables...\n"); valueSaves.setDim(istate->vars.dim); for (size_t i = 0; i < istate->vars.dim; i++) { VarDeclaration *v = (VarDeclaration *)istate->vars.data[i]; - if (v) + if (v && v->parent == this) { - //printf("\tsaving [%d] %s = %s\n", i, v->toChars(), v->value ? v->value->toChars() : ""); + //printf("\tsaving [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); valueSaves.data[i] = v->getValue(); v->setValueNull(); } @@ -279,6 +347,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument else break; } + assert(e != EXP_CONTINUE_INTERPRET && e != EXP_BREAK_INTERPRET); /* Restore the parameter values */ @@ -287,15 +356,19 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument VarDeclaration *v = (VarDeclaration *)parameters->data[i]; v->setValueWithoutChecking((Expression *)vsave.data[i]); } + /* Clear __result. (Bug 6049). + */ + if (vresult) + vresult->setValueNull(); - if (istate && !isNested()) + if (istate) { /* Restore the variable values */ //printf("restoring local variables...\n"); for (size_t i = 0; i < istate->vars.dim; i++) { VarDeclaration *v = (VarDeclaration *)istate->vars.data[i]; - if (v) + if (v && v->parent == this) { v->setValueWithoutChecking((Expression *)valueSaves.data[i]); //printf("\trestoring [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); } @@ -393,11 +466,20 @@ Expression *UnrolledLoopStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_CONTINUE_INTERPRET) - { e = NULL; + { + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level + istate->gotoTarget = NULL; + e = NULL; continue; } if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } if (e) @@ -452,112 +534,87 @@ Expression *ScopeStatement::interpret(InterState *istate) return statement ? statement->interpret(istate) : NULL; } -// Helper for ReturnStatement::interpret() for returning references. -// Given an original expression, which is known to be a reference to a reference, -// turn it into a reference. -Expression * replaceReturnReference(Expression *original, InterState *istate) +Expression *resolveSlice(Expression *e) { - Expression *e = original; - if (e->op == TOKcall) - { // If it's a function call, interpret it now. - // It also needs to return an lvalue. - istate->awaitingLvalueReturn = true; - e = e->interpret(istate); - if (e == EXP_CANT_INTERPRET) - return e; - } - // If it is a reference to a reference, convert it to a reference - if (e->op == TOKvar) - { - VarExp *ve = (VarExp *)e; - VarDeclaration *v = ve->var->isVarDeclaration(); - assert (v && v->getValue()); - return v->getValue(); - } - - if (e->op == TOKthis) - { - return istate->localThis; - } - - Expression *r = e->copy(); - e = r; - Expression *next; - for (;;) - { - if (e->op == TOKindex) - next = ((IndexExp*)e)->e1; - else if (e->op == TOKdotvar) - next = ((DotVarExp *)e)->e1; - else if (e->op == TOKdotti) - next = ((DotTemplateInstanceExp *)e)->e1; - else if (e->op == TOKslice) - next = ((SliceExp*)e)->e1; - else - return EXP_CANT_INTERPRET; - - Expression *old = next; - - if (next->op == TOKcall) - { - bool oldWaiting = istate->awaitingLvalueReturn; - istate->awaitingLvalueReturn = true; - next = next->interpret(istate); - istate->awaitingLvalueReturn = oldWaiting; - if (next == EXP_CANT_INTERPRET) - return next; - } - if (next->op == TOKvar) - { - VarDeclaration * v = ((VarExp*)next)->var->isVarDeclaration(); - if (v) - next = v->getValue(); - } - else if (next->op == TOKthis) - next = istate->localThis; - - if (old == next) - { // Haven't found the reference yet. Need to keep copying. - next = next->copy(); - old = next; - } - if (e->op == TOKindex) - { // The index needs to be evaluated now (it isn't part of the ref) - ((IndexExp*)e)->e1 = next; - ((IndexExp*)e)->e2 = ((IndexExp*)e)->e2->interpret(istate); - if (((IndexExp*)e)->e2 == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - } - else if (e->op == TOKdotvar) - ((DotVarExp *)e)->e1 = next; - else if (e->op == TOKdotti) - ((DotTemplateInstanceExp *)e)->e1 = next; - else if (e->op == TOKslice) - { /* Interpret the slice bounds immediately (they are - * not part of the reference). - */ - ((SliceExp*)e)->e1 = next; - Expression *x = ((SliceExp*)e)->upr; - if (x) - x = x->interpret(istate); - if (x == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - ((SliceExp*)e)->upr = x; - x = ((SliceExp*)e)->lwr; - if (x) - x = x->interpret(istate); - if (x == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - ((SliceExp*)e)->lwr = x; - } - if (old != next) - break; - e = next; - } - - return r; + if ( ((SliceExp *)e)->e1->op == TOKnull) + return ((SliceExp *)e)->e1; + return Slice(e->type, ((SliceExp *)e)->e1, + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); } +/* Determine the array length, without interpreting it. + * e must be an array literal, or a slice + * It's very wasteful to resolve the slice when we only + * need the length. + */ +uinteger_t resolveArrayLength(Expression *e) +{ + if (e->op == TOKnull) + return 0; + if (e->op == TOKslice) + { uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger(); + uinteger_t iup = ((SliceExp *)e)->upr->toInteger(); + return iup - ilo; + } + if (e->op == TOKstring) + { return ((StringExp *)e)->len; + } + if (e->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; + return ale->elements ? ale->elements->dim : 0; + } + if (e->op == TOKassocarrayliteral) + { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; + return ale->keys->dim; + } + assert(0); + return 0; +} + +void scrubArray(Expressions *elems); + +/* All results destined for use outside of CTFE need to have their CTFE-specific + * features removed. + * In particular, all slices must be resolved. + */ +Expression *scrubReturnValue(Expression *e) +{ + if (e->op == TOKslice) + { + e = resolveSlice(e); + } + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + scrubArray(se->elements); + } + if (e->op == TOKarrayliteral) + { + scrubArray(((ArrayLiteralExp *)e)->elements); + } + if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + scrubArray(aae->keys); + scrubArray(aae->values); + } + return e; +} + +// Scrub all members of an array +void scrubArray(Expressions *elems) +{ + for (size_t i = 0; i < elems->dim; i++) + { + Expression *m = (Expression *)elems->data[i]; + if (!m) + continue; + m = scrubReturnValue(m); + elems->data[i] = m; + } +} + + Expression *ReturnStatement::interpret(InterState *istate) { #if LOG @@ -575,8 +632,8 @@ Expression *ReturnStatement::interpret(InterState *istate) { TypeFunction *tf = (TypeFunction *)istate->fd->type; if (tf->isref && istate->caller && istate->caller->awaitingLvalueReturn) - { // We need to return an lvalue. Can't do a normal interpret. - Expression *e = replaceReturnReference(exp, istate); + { // We need to return an lvalue + Expression *e = exp->interpret(istate, ctfeNeedLvalue); if (e == EXP_CANT_INTERPRET) error("ref return %s is not yet supported in CTFE", exp->toChars()); return e; @@ -590,13 +647,27 @@ Expression *ReturnStatement::interpret(InterState *istate) } } #endif - - Expression *e = exp->interpret(istate); + // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + Expression *e; + if ((exp->type->ty == Tpointer && exp->type->nextOf()->ty != Tfunction)) + e = exp->interpret(istate, ctfeNeedLvalue); + else + e = exp->interpret(istate); if (e == EXP_CANT_INTERPRET) return e; - // Convert lvalues into rvalues (See Bugzilla 4825 for rationale) - if (e->op == TOKvar) - e = e->interpret(istate); + if (!istate->caller) + { + e = scrubReturnValue(e); + if (e == EXP_CANT_INTERPRET) + return e; + } + else if (needToCopyLiteral(exp)) + e = copyLiteral(e); +#if LOGASSIGN + printf("RETURN %s\n", loc.toChars()); + showCtfeExpr(e); +#endif return e; } @@ -607,9 +678,21 @@ Expression *BreakStatement::interpret(InterState *istate) #endif START() if (ident) - return EXP_CANT_INTERPRET; - else + { LabelDsymbol *label = istate->fd->searchLabel(ident); + assert(label && label->statement); + Statement *s = label->statement; + if (s->isLabelStatement()) + s = s->isLabelStatement()->statement; + if (s->isScopeStatement()) + s = s->isScopeStatement()->statement; + istate->gotoTarget = s; return EXP_BREAK_INTERPRET; + } + else + { + istate->gotoTarget = NULL; + return EXP_BREAK_INTERPRET; + } } Expression *ContinueStatement::interpret(InterState *istate) @@ -619,7 +702,16 @@ Expression *ContinueStatement::interpret(InterState *istate) #endif START() if (ident) - return EXP_CANT_INTERPRET; + { LabelDsymbol *label = istate->fd->searchLabel(ident); + assert(label && label->statement); + Statement *s = label->statement; + if (s->isLabelStatement()) + s = s->isLabelStatement()->statement; + if (s->isScopeStatement()) + s = s->isScopeStatement()->statement; + istate->gotoTarget = s; + return EXP_CONTINUE_INTERPRET; + } else return EXP_CONTINUE_INTERPRET; } @@ -650,9 +742,21 @@ Expression *DoStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) - return NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + return e; + } if (e == EXP_CONTINUE_INTERPRET) - goto Lcontinue; + if (!istate->gotoTarget || istate->gotoTarget == this) + { + goto Lcontinue; + } + else // else continue at a higher level + return e; if (e) return e; } @@ -663,13 +767,21 @@ Expression *DoStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } if (e && e != EXP_CONTINUE_INTERPRET) break; + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at a higher level Lcontinue: + istate->gotoTarget = NULL; e = condition->interpret(istate); if (e == EXP_CANT_INTERPRET) break; @@ -715,9 +827,21 @@ Expression *ForStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) - return NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + return NULL; + } // else break at a higher level + } if (e == EXP_CONTINUE_INTERPRET) - goto Lcontinue; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + goto Lcontinue; + } // else continue at a higher level + } if (e) return e; } @@ -740,12 +864,20 @@ Expression *ForStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } if (e && e != EXP_CONTINUE_INTERPRET) break; + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at a higher level Lcontinue: + istate->gotoTarget = NULL; if (increment) { e = increment->interpret(istate); @@ -813,11 +945,21 @@ Expression *ForeachStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET) + { + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level + istate->gotoTarget = NULL; e = NULL; + } else if (e) break; } @@ -838,11 +980,21 @@ Expression *ForeachStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET) + { + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level + istate->gotoTarget = NULL; e = NULL; + } else if (e) break; } @@ -898,11 +1050,20 @@ Expression *ForeachRangeStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } + if (e == EXP_CONTINUE_INTERPRET + && istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level if (e == NULL || e == EXP_CONTINUE_INTERPRET) { e = Add(key->value->type, key->value, new IntegerExp(loc, 1, key->value->type)); + istate->gotoTarget = NULL; if (e == EXP_CANT_INTERPRET) break; key->value = e; @@ -934,9 +1095,20 @@ Expression *ForeachRangeStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) - { e = NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level break; } + if (e == EXP_CONTINUE_INTERPRET) + { + if (istate->gotoTarget && istate->gotoTarget != this) + break; // continue at higher level + istate->gotoTarget = NULL; + } } while (e == NULL || e == EXP_CONTINUE_INTERPRET); } key->value = keysave; @@ -962,7 +1134,13 @@ Expression *SwitchStatement::interpret(InterState *istate) if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) - return NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + return NULL; + } // else break at a higher level + } return e; } @@ -970,6 +1148,8 @@ Expression *SwitchStatement::interpret(InterState *istate) Expression *econdition = condition->interpret(istate); if (econdition == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; + if (econdition->op == TOKslice) + econdition = resolveSlice(econdition); Statement *s = NULL; if (cases) @@ -1001,7 +1181,13 @@ Expression *SwitchStatement::interpret(InterState *istate) e = body ? body->interpret(istate) : NULL; assert(!istate->start); if (e == EXP_BREAK_INTERPRET) - return NULL; + { + if (!istate->gotoTarget || istate->gotoTarget == this) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + } return e; } @@ -1136,6 +1322,17 @@ Expression *AsmStatement::interpret(InterState *istate) return EXP_CANT_INTERPRET; } +#if DMDV2 +Expression *ImportStatement::interpret(InterState *istate) +{ +#if LOG + printf("ImportStatement::interpret()\n"); +#endif + START(); + return NULL; +} +#endif + /******************************** Expression ***************************/ Expression *Expression::interpret(InterState *istate, CtfeGoal goal) @@ -1151,8 +1348,10 @@ Expression *Expression::interpret(InterState *istate, CtfeGoal goal) Expression *ThisExp::interpret(InterState *istate, CtfeGoal goal) { + if (istate && istate->localThis && istate->localThis->op == TOKstructliteral) + return istate->localThis; if (istate && istate->localThis) - return istate->localThis->interpret(istate); + return istate->localThis->interpret(istate, goal); error("value of 'this' is not known at compile time"); return EXP_CANT_INTERPRET; } @@ -1187,6 +1386,27 @@ Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("StringExp::interpret() %s\n", toChars()); +#endif + /* Since we are using StringExps as reference types for char[] arrays, + * we need to dup them if there's any chance they'll be modified. + * For efficiency, we try to only dup when necessary. + */ + // Fixed-length char arrays always get duped later anyway. + if (type->ty == Tsarray) + return this; + /* String literals are normally immutable, so we don't need to dup them + * In D2, we can detect attempts to write to read-only literals. + * For D1, we could be pessimistic, and always dup. + * But since it fails only when there has been an explicit cast, and any + * such function would give different results at runtime anyway (eg, it + * may crash), it hardly seems worth the massive performance hit. + */ +#if DMDV2 + if (!((TypeNext *)type)->next->mod & (MODconst | MODimmutable)) + { // It seems this happens only when there has been an explicit cast + error("cannot cast a read-only string literal to mutable in CTFE"); + return EXP_CANT_INTERPRET; + } #endif return this; } @@ -1208,10 +1428,77 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) { return this; } + if (type->ty != Tpointer) + { // Probably impossible + error("Cannot interpret %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + Type *pointee = ((TypePointer *)type)->next; + Expression *val = getVarExp(loc, istate, var, goal); + if (val->type->ty == Tarray || val->type->ty == Tsarray) + { + TypeArray *tar = (TypeArray *)val->type; + dinteger_t sz = pointee->size(); + dinteger_t indx = offset/sz; + assert(sz * indx == offset); + Expression *aggregate = NULL; + if (val->op == TOKarrayliteral || val->op == TOKstring) + aggregate = val; + else if (val->op == TOKslice) + { + aggregate = ((SliceExp *)val)->e1; + Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); + indx += lwr->toInteger(); + } + if (aggregate) + { + IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); + IndexExp *ie = new IndexExp(loc, aggregate, ofs); + ie->type = type; + return ie; + } + } + else if (offset == 0 && pointee == var->type) + { + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + { + VarExp *ve = new VarExp(loc, var); + ve->type = type; + return ve; + } + Expression *e = getVarExp(loc, istate, var, goal); + e = new AddrExp(loc, e); + e->type = type; + return e; + } + error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } +Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("AddrExp::interpret() %s\n", toChars()); +#endif +#if 0 || IN_LLVM + if (e1->op == TOKvar) + { VarExp *ve = (VarExp *)e1; + if (ve->var->isFuncDeclaration()) + return this; + + } +#endif + + Expression *e = e1->interpret(istate, ctfeNeedLvalue); + if (e == EXP_CANT_INTERPRET) + return e; + // Return a simplified address expression + e = new AddrExp(loc, e); + e->type = type; + return e; +} + Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG @@ -1220,26 +1507,6 @@ Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) return this; } -#if IN_LLVM - -Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("AddrExp::interpret() %s\n", toChars()); -#endif - if (e1->op == TOKvar) - { VarExp *ve = (VarExp *)e1; - if (ve->var->isFuncDeclaration()) - return this; - - } - error("Cannot interpret %s at compile time", toChars()); - return EXP_CANT_INTERPRET; -} - -#endif - - // ------------------------------------------------------------- // Remove out, ref, and this // ------------------------------------------------------------- @@ -1263,6 +1530,8 @@ Expression * resolveReferences(Expression *e, Expression *thisval, bool *isRefer // Chase down rebinding of out and ref. VarExp *ve = (VarExp *)e; VarDeclaration *v = ve->var->isVarDeclaration(); + if (v->type->ty == Tpointer) + break; if (v && v->getValue() && v->getValue()->op == TOKvar) // it's probably a reference { // Make sure it's a real reference. @@ -1339,7 +1608,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal if (e && e != EXP_CANT_INTERPRET) v->setValueWithoutChecking(e); } - else if ((v->isCTFE() || (!v->isDataseg() && istate)) && !v->getValue()) + else if (v->isCTFE() && !v->getValue()) { if (v->init && v->type->size() != 0) { @@ -1354,8 +1623,8 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal else e = v->type->defaultInitLiteral(loc); } - else if (!v->isDataseg() && !istate) { - error(loc, "variable %s cannot be read at compile time", v->toChars()); + else if (!v->isDataseg() && !v->isCTFE() && !istate) + { error(loc, "variable %s cannot be read at compile time", v->toChars()); return EXP_CANT_INTERPRET; } else @@ -1368,9 +1637,9 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal error(loc, "variable %s is used before initialization", v->toChars()); else if (e == EXP_CANT_INTERPRET) return e; - else if (goal == ctfeNeedLvalue && (e->op == TOKstring || e->op == TOKslice || - e->op == TOKstructliteral || e->op == TOKarrayliteral || - e->op == TOKassocarrayliteral)) + else if ((goal == ctfeNeedLvalue) || e->op == TOKaddress + || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral + || e->op == TOKassocarrayliteral || e->op == TOKslice) return e; // it's already an Lvalue else e = e->interpret(istate, goal); @@ -1406,14 +1675,26 @@ Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("VarExp::interpret() %s\n", toChars()); #endif + if (goal == ctfeNeedLvalueRef) + { + // If it is a reference, return the thing it's pointing to. + VarDeclaration *v = var->isVarDeclaration(); + if (v && v->getValue() && (v->storage_class & (STCref | STCout))) + return v->getValue(); + if (v && !v->isDataseg() && !v->isCTFE() && !istate) + { error("variable %s cannot be referenced at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else if (v && !v->getValue() && !v->isCTFE() && v->isDataseg()) + { error("static variable %s cannot be referenced at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + return this; + } Expression *e = getVarExp(loc, istate, var, goal); // A VarExp may include an implicit cast. It must be done explicitly. - if (e != EXP_CANT_INTERPRET && e->type != type - && e->implicitConvTo(type) == MATCHexact) - { - e = e->implicitCastTo(0, type); - e = e->interpret(istate, goal); - } + if (e != EXP_CANT_INTERPRET) + e = paintTypeOntoLiteral(type, e); return e; } @@ -1426,6 +1707,11 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) VarDeclaration *v = declaration->isVarDeclaration(); if (v) { + if (v->getValue()) + { + addVarToInterstate(istate, v); + v->setValueNull(); + } Dsymbol *s = v->toAlias(); if (s == v && !v->isStatic() && v->init) { @@ -1497,6 +1783,15 @@ Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) return ex; } + // A tuple of assignments can contain void (Bug 5676). + if (goal == ctfeNeedNothing) + continue; + if (ex == EXP_VOID_INTERPRET) + { + error("ICE: void element %s in tuple", e->toChars()); + assert(0); + } + /* If any changes, do Copy On Write */ if (ex != e) @@ -1562,7 +1857,13 @@ Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) ae->type = type; return ae; } - return this; +#if DMDV2 + if (((TypeNext *)type)->next->mod & (MODconst | MODimmutable)) + { // If it's immutable, we don't need to dup it + return this; + } +#endif + return copyLiteral(this); Lerror: if (expsx) @@ -1711,7 +2012,7 @@ Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) se->type = type; return se; } - return this; + return copyLiteral(this); } /****************************** @@ -1768,6 +2069,28 @@ Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) ((TypeArray *)newtype)->next->defaultInitLiteral(), lenExpr->toInteger()); } + if (newtype->toBasetype()->ty == Tstruct) + { + Expression *se = newtype->defaultInitLiteral(); +#if DMDV2 + if (member) + { + int olderrors = global.errors; + member->interpret(istate, arguments, se); + if (olderrors != global.errors) + { + error("cannot evaluate %s at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + } +#else // The above code would fail on D1 because it doesn't use STRUCTTHISREF, + // but that's OK because D1 doesn't have struct constructors anyway. + assert(!member); +#endif + Expression *e = new AddrExp(loc, se); + e->type = type; + return e; + } if (newtype->ty == Tclass) { error("classes are not yet supported in CTFE"); @@ -1787,9 +2110,6 @@ Expression *UnaExp::interpretCommon(InterState *istate, CtfeGoal goal, Expressi e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; - if (e1->isConst() != 1) - goto Lcant; - e = (*fp)(type, e1); return e; @@ -1808,6 +2128,104 @@ UNA_INTERPRET(Com) UNA_INTERPRET(Not) UNA_INTERPRET(Bool) +Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs) +{ + *ofs = 0; + if (e->op == TOKaddress) + e = ((AddrExp *)e)->e1; + if (e->op == TOKindex) + { + IndexExp *ie = (IndexExp *)e; + // Note that each AA element is part of its own memory block + if ((ie->e1->type->ty == Tarray || ie->e1->type->ty == Tsarray + || ie->e1->op == TOKstring || ie->e1->op==TOKarrayliteral) && + ie->e2->op == TOKint64) + { + *ofs = ie->e2->toInteger(); + return ie->e1; + } + } + return e; +} + +// return e1 - e2 as an integer, or error if not possible +Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) +{ + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + if (agg1 == agg2) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + else if (agg1->op == TOKstring && agg2->op == TOKstring) + { + if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + } +#if LOGASSIGN + printf("FAILED POINTER DIFF\n"); + showCtfeExpr(agg1); + showCtfeExpr(agg2); +#endif + error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " + "pointers to two different memory blocks", + e1->toChars(), e2->toChars()); + return EXP_CANT_INTERPRET; +} + +// Return eptr op e2, where eptr is a pointer, e2 is an integer, +// and op is TOKadd or TOKmin +Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type, + Expression *eptr, Expression *e2) +{ + dinteger_t ofs1, ofs2; + if (eptr->op == TOKaddress) + eptr = ((AddrExp *)eptr)->e1; + Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); + if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) + { + error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); + return EXP_CANT_INTERPRET; + } + ofs2 = e2->toInteger(); + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + Expression *dollar = ArrayLength(Type::tsize_t, agg1); + assert(dollar != EXP_CANT_INTERPRET); + dinteger_t len = dollar->toInteger(); + + Expression *val = agg1; + TypeArray *tar = (TypeArray *)val->type; + dinteger_t indx = ofs1; + if (op == TOKadd || op == TOKaddass) + indx = indx + ofs2/sz; + else if (op == TOKmin || op == TOKminass) + indx -= ofs2/sz; + else + error(loc, "CTFE Internal compiler error: bad pointer operation"); + if (val->op != TOKarrayliteral && val->op != TOKstring) + { + error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars()); + return EXP_CANT_INTERPRET; + } + if (indx < 0 || indx > len) + { + error(loc, "cannot assign pointer to index %jd inside memory block [0..%jd]", indx, len); + return EXP_CANT_INTERPRET; + } + + IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); + IndexExp *ie = new IndexExp(loc, val, ofs); + ie->type = type; + return ie; +} typedef Expression *(*fp_t)(Type *, Expression *, Expression *); @@ -1819,6 +2237,31 @@ Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) #if LOG printf("BinExp::interpretCommon() %s\n", toChars()); #endif + if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + return pointerDifference(loc, type, e1, e2); + } + if (this->e1->type->ty == Tpointer && this->e2->type->isintegral()) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + e2 = this->e2->interpret(istate); + if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + return pointerArithmetic(loc, op, type, e1, e2); + } + if (this->e2->type->ty == Tpointer && this->e1->type->ty == TOKint64 && op==TOKadd) + { + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + e1 = this->e1->interpret(istate); + return pointerArithmetic(loc, op, type, e2, e1); + } + if (this->e1->type->ty == Tpointer || this->e2->type->ty == Tpointer) + { + error("pointer expression %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; @@ -1832,6 +2275,8 @@ Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) goto Lcant; e = (*fp)(type, e1, e2); + if (e == EXP_CANT_INTERPRET) + error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: @@ -1859,6 +2304,63 @@ BIN_INTERPRET(Xor) typedef Expression *(*fp2_t)(enum TOK, Type *, Expression *, Expression *); +// Return EXP_CANT_INTERPRET if they point to independent memory blocks +Expression *comparePointers(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) +{ + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + if (agg1 == agg2 || + (agg1->op == TOKstring && agg2->op == TOKstring && + ((StringExp *)agg1)->string == ((StringExp *)agg2)->string)) + + { + dinteger_t cm = ofs1 - ofs2; + dinteger_t n; + dinteger_t zero = 0; + switch(op) + { + case TOKlt: n = (ofs1 < ofs2); break; + case TOKle: n = (ofs1 <= ofs2); break; + case TOKgt: n = (ofs1 > ofs2); break; + case TOKge: n = (ofs1 >= ofs2); break; + case TOKidentity: + case TOKequal: n = (ofs1 == ofs2); break; + case TOKnotidentity: + case TOKnotequal: n = (ofs1 != ofs2); break; + default: + assert(0); + } + return new IntegerExp(loc, n, type); + } + int cmp; + if (e1->op == TOKnull) + { + cmp = (e2->op == TOKnull); + } + else if (e2->op == TOKnull) + { + cmp = 0; + } + else + { + switch(op) + { + case TOKidentity: + case TOKequal: + case TOKnotidentity: // 'cmp' gets inverted below + case TOKnotequal: + cmp = 0; + break; + default: + return EXP_CANT_INTERPRET; + } + } + if (op == TOKnotidentity || op == TOKnotequal) + cmp ^= -1; + return new IntegerExp(loc, cmp, type); +} + Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp) { Expression *e; Expression *e1; @@ -1867,9 +2369,25 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp #if LOG printf("BinExp::interpretCommon2() %s\n", toChars()); #endif + if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + e2 = this->e2->interpret(istate, ctfeNeedLvalue); + e = comparePointers(loc, op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + { + error("%s and %s point to independent memory blocks and " + "cannot be compared at compile time", this->e1->toChars(), + this->e2->toChars()); + } + return e; + } e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; + if (e1->op == TOKslice) + e1 = resolveSlice(e1); + if (e1->isConst() != 1 && e1->op != TOKnull && e1->op != TOKstring && @@ -1883,6 +2401,8 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) goto Lcant; + if (e2->op == TOKslice) + e2 = resolveSlice(e2); if (e2->isConst() != 1 && e2->op != TOKnull && e2->op != TOKstring && @@ -1892,8 +2412,9 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp error("cannot compare %s at compile time", e2->toChars()); goto Lcant; } - e = (*fp)(op, type, e1, e2); + if (e == EXP_CANT_INTERPRET) + error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: @@ -2013,68 +2534,7 @@ UnaExp *isUnaExp(Expression *e) default: break; } - return NULL; -} - -// To resolve an assignment expression, we need to walk to the end of the -// expression to find the ultimate variable which is modified. But, in building -// up the expression, we need to walk the tree *backwards*. There isn't a -// standard way to do this, but if we know we're at depth d, iterating from -// the root up to depth d-1 will give us the parent node. Inefficient, but -// depth is almost always < 3. -struct ExpressionReverseIterator -{ - Expression *totalExpr; // The root expression - Expression *thisval; // The value to be used for TOKthis - int totalDepth; - - ExpressionReverseIterator(Expression *root, Expression *thisexpr) - { - totalExpr = root; - thisval = thisexpr; - totalDepth = findExpressionDepth(totalExpr); - } - - int findExpressionDepth(Expression *e); - Expression *getExpressionAtDepth(int depth); -}; - -// Determines the depth in unary expressions. -int ExpressionReverseIterator::findExpressionDepth(Expression *e) -{ - int depth = 0; - for (;;) - { - e = resolveReferences(e, thisval); - if (e->op == TOKvar) - return depth; - if (e->op == TOKcall) - return depth; - ++depth; - UnaExp *u = isUnaExp(e); - if (u) - e = u->e1; - else - return depth; - } -} - -Expression *ExpressionReverseIterator::getExpressionAtDepth(int depth) -{ - Expression *e = totalExpr; - int d = 0; - for (;;) - { - e = resolveReferences(e, thisval); - if (d == depth) return e; - ++d; - assert(e->op != TOKvar); - UnaExp *u = isUnaExp(e); - if (u) - e = u->e1; - else - return e; - } + return NULL; } // Returns the variable which is eventually modified, or NULL if an rvalue. @@ -2102,40 +2562,6 @@ VarDeclaration * findParentVar(Expression *e, Expression *thisval) return v; } -// Returns the value to be assigned to the last dotVar, given the existing value at this depth. -Expression *assignDotVar(ExpressionReverseIterator rvs, int depth, Expression *existing, Expression *newval) -{ - if (depth == 0) - return newval; - assert(existing && existing != EXP_CANT_INTERPRET); - Expression *e = rvs.getExpressionAtDepth(depth - 1); - if (e->op == TOKdotvar) - { - VarDeclaration *member = ((DotVarExp *)e)->var->isVarDeclaration(); - assert(member); - assert(existing); - assert(existing != EXP_CANT_INTERPRET); - assert(existing->op == TOKstructliteral); - if (existing->op != TOKstructliteral) - return EXP_CANT_INTERPRET; - - StructLiteralExp *se = (StructLiteralExp *)existing; - int fieldi = se->getFieldIndex(member->type, member->offset); - if (fieldi == -1) - return EXP_CANT_INTERPRET; - assert(fieldi>=0 && fieldi < se->elements->dim); - Expression *ex = (Expression *)(se->elements->data[fieldi]); - - newval = assignDotVar(rvs, depth - 1, ex, newval); - Expressions *expsx = changeOneElement(se->elements, fieldi, newval); - Expression * ee = new StructLiteralExp(se->loc, se->sd, expsx); - ee->type = se->type; - return ee; - } - assert(0); - return NULL; -} - // Given expr, which evaluates to an array/AA/string literal, // return true if it needs to be copied bool needToCopyLiteral(Expression *expr) @@ -2146,9 +2572,9 @@ bool needToCopyLiteral(Expression *expr) { case TOKarrayliteral: case TOKassocarrayliteral: - case TOKstring: case TOKstructliteral: return true; + case TOKstring: case TOKthis: case TOKvar: return false; @@ -2161,23 +2587,29 @@ bool needToCopyLiteral(Expression *expr) expr = ((UnaExp *)expr)->e1; continue; case TOKcat: + return needToCopyLiteral(((BinExp *)expr)->e1) || + needToCopyLiteral(((BinExp *)expr)->e2); case TOKcatass: - return false; - case TOKcall: - // TODO: Return statement should - // guarantee we never return a naked literal, but - // currently it doesn't. - return true; - - // There are probably other cases which don't need - // a copy. But for now, we conservatively copy all - // other cases. + expr = ((BinExp *)expr)->e2; + continue; default: - return true; + return false; } } } +Expressions *copyLiteralArray(Expressions *oldelems) +{ + if (!oldelems) + return oldelems; + Expressions *newelems = new Expressions(); + newelems->setDim(oldelems->dim); + for (size_t i = 0; i < oldelems->dim; i++) + newelems->data[i] = copyLiteral((Expression *)(oldelems->data[i])); + return newelems; +} + + // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. // This value will be used for in-place modification. @@ -2199,12 +2631,16 @@ Expression *copyLiteral(Expression *e) else if (e->op == TOKarrayliteral) { ArrayLiteralExp *ae = (ArrayLiteralExp *)e; - Expressions *oldelems = ae->elements; - Expressions *newelems = new Expressions(); - newelems->setDim(oldelems->dim); - for (size_t i = 0; i < oldelems->dim; i++) - newelems->data[i] = copyLiteral((Expression *)(oldelems->data[i])); - ArrayLiteralExp *r = new ArrayLiteralExp(ae->loc, newelems); + ArrayLiteralExp *r = new ArrayLiteralExp(e->loc, + copyLiteralArray(ae->elements)); + r->type = e->type; + return r; + } + else if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc, + copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); r->type = e->type; return r; } @@ -2226,14 +2662,20 @@ Expression *copyLiteral(Expression *e) Dsymbol *s = (Dsymbol *)sd->fields.data[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v); + // If it is a void assignment, use the default initializer + if (!m) + m = v->type->defaultInitLiteral(e->loc); + if (m->op == TOKslice) + m = resolveSlice(m); if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) { // Block assignment from inside struct literals TypeSArray *tsa = (TypeSArray *)v->type; uinteger_t length = tsa->dim->toInteger(); - m = createBlockDuplicatedArrayLiteral(v->type, m, length); - } else m = copyLiteral(m); + } + else if (v->type->ty != Tarray) // NOTE: do not copy array references + m = copyLiteral(m); newelems->data[i] = m; } #if DMDV2 @@ -2250,7 +2692,171 @@ Expression *copyLiteral(Expression *e) return r; } -void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val) +/* Deal with type painting. + * Type painting is a major nuisance: we can't just set + * e->type = type, because that would change the original literal. + * But, we can't simply copy the literal either, because that would change + * the values of any pointers. + */ +Expression *paintTypeOntoLiteral(Type *type, Expression *lit) +{ + if (lit->type == type) + return lit; + Expression *e; + if (lit->op == TOKslice) + { + SliceExp *se = (SliceExp *)lit; + e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr); + } + else if (lit->op == TOKindex) + { + IndexExp *ie = (IndexExp *)lit; + e = new IndexExp(lit->loc, ie->e1, ie->e2); + } + else if (lit->op == TOKarrayliteral) + { + ArrayLiteralExp *ae = (ArrayLiteralExp *)lit; + e = new ArrayLiteralExp(lit->loc, ae->elements); + } + else if (lit->op == TOKstring) + { + // For strings, we need to introduce another level of indirection + e = new SliceExp(lit->loc, lit, + new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); + } + else if (lit->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; + e = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); + } + else + e = copyLiteral(lit); + e->type = type; + return e; +} + +/* Set a slice of char array literal 'existingAE' from a string 'newval'. + * existingAE[firstIndex..firstIndex+newval.length] = newval. + */ +void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex) +{ + size_t newlen = newval->len; + size_t sz = newval->sz; + unsigned char *s = (unsigned char *)newval->string; + Type *elemType = existingAE->type->nextOf(); + for (size_t j = 0; j < newlen; j++) + { + dinteger_t val; + switch (sz) + { + case 1: val = s[j]; break; + case 2: val = ((unsigned short *)s)[j]; break; + case 4: val = ((unsigned *)s)[j]; break; + default: + assert(0); + break; + } + existingAE->elements->data[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, int firstIndex) +{ + unsigned char *s = (unsigned char *)existingSE->string; + for (size_t j = 0; j < newae->elements->dim; j++) + { + unsigned value = ((Expression *)(newae->elements->data[j]))->toInteger(); + switch (existingSE->sz) + { + case 1: s[j+firstIndex] = value; break; + case 2: ((unsigned short *)s)[j+firstIndex] = value; break; + case 4: ((unsigned *)s)[j+firstIndex] = value; 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, int firstIndex) +{ + unsigned char *s = (unsigned char *)existingSE->string; + size_t sz = existingSE->sz; + assert(sz == newstr->sz); + memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len); +} + + +/* Set dest = src, where both dest and src are container value literals + * (ie, struct literals, or static arrays (can be an array literal or a string) + * Assignment is recursively in-place. + * Purpose: any reference to a member of 'dest' will remain valid after the + * assignment. + */ +void assignInPlace(Expression *dest, Expression *src) +{ + assert(dest->op == TOKstructliteral || dest->op == TOKarrayliteral || + dest->op == TOKstring); + Expressions *oldelems; + Expressions *newelems; + if (dest->op == TOKstructliteral) + { + assert(dest->op == src->op); + oldelems = ((StructLiteralExp *)dest)->elements; + newelems = ((StructLiteralExp *)src)->elements; + } + else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) + { + oldelems = ((ArrayLiteralExp *)dest)->elements; + newelems = ((ArrayLiteralExp *)src)->elements; + } + else if (dest->op == TOKstring && src->op == TOKstring) + { + sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0); + return; + } + else if (dest->op == TOKarrayliteral && src->op == TOKstring) + { + sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0); + return; + } + else if (src->op == TOKarrayliteral && dest->op == TOKstring) + { + sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0); + return; + } + else assert(0); + + assert(oldelems->dim == newelems->dim); + + for (size_t i= 0; i < oldelems->dim; ++i) + { + Expression *e = (Expression *)newelems->data[i]; + Expression *o = (Expression *)oldelems->data[i]; + if (e->op == TOKstructliteral) + { + assert(o->op == e->op); + assignInPlace(o, e); + } + else if (e->type->ty == Tsarray && o->type->ty == Tsarray) + { + assignInPlace(o, e); + } + else + { + oldelems->data[i] = newelems->data[i]; + } + } +} + +void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) { assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); #if DMDV2 @@ -2260,13 +2866,23 @@ void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val) Type *desttype = ((TypeArray *)ae->type)->next; bool directblk = (val->type->toBasetype()) == desttype; #endif + + bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral + || val->op == TOKstring); + for (size_t k = 0; k < ae->elements->dim; k++) { if (!directblk && ((Expression *)(ae->elements->data[k]))->op == TOKarrayliteral) { - recursiveBlockAssign((ArrayLiteralExp *)(ae->elements->data[k]), val); + recursiveBlockAssign((ArrayLiteralExp *)(ae->elements->data[k]), val, wantRef); + } + else + { + if (wantRef || cow) + ae->elements->data[k] = val; + else + assignInPlace((Expression *)ae->elements->data[k], val); } - else ae->elements->data[k] = val; } } @@ -2276,12 +2892,12 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ #if LOG printf("BinExp::interpretAssignCommon() %s\n", toChars()); #endif - Expression *e = EXP_CANT_INTERPRET; + Expression *returnValue = EXP_CANT_INTERPRET; Expression *e1 = this->e1; if (!istate) { error("value of %s is not known at compile time", e1->toChars()); - return e; + return returnValue; } /* Before we begin, we need to know if this is a reference assignment * (dynamic array, AA, or class) or a value assignment. @@ -2335,6 +2951,9 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ VarDeclaration * targetVar = findParentVar(e2, istate->localThis); if (!(targetVar && targetVar->isConst())) wantRef = true; + // slice assignment of static arrays is not reference assignment + if ((e1->op==TOKslice) && ((SliceExp *)e1)->e1->type->ty == Tsarray) + wantRef = false; #endif } if (isBlockAssignment && (e2->type->toBasetype()->ty == Tarray || e2->type->toBasetype()->ty == Tsarray)) @@ -2347,6 +2966,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ * construction of a struct with an invariant(). */ if (op==TOKconstruct && this->e1->op==TOKvar && this->e2->op != TOKthis + && this->e2->op != TOKcomma && ((VarExp*)this->e1)->var->storage_class & STCref) wantRef = true; @@ -2373,6 +2993,21 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ istate->awaitingLvalueReturn = oldWaiting; if (e1 == EXP_CANT_INTERPRET) return e1; + if (e1->op == TOKarrayliteral || e1->op == TOKstring) + { + // f() = e2, when f returns an array, is always a slice assignment. + // Convert into arr[0..arr.length] = e2 + e1 = new SliceExp(loc, e1, + new IntegerExp(0, 0, Type::tsize_t), + ArrayLength(Type::tsize_t, e1)); + e1->type = type; + } + } + if (e1->op == TOKstar) + { + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (e1 == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; } if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar @@ -2384,10 +3019,17 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ Expression * newval = NULL; if (!wantRef) - newval = this->e2->interpret(istate); + { // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + assert(e1); + assert(e1->type); + if ((e1->type->ty == Tpointer && e1->type->nextOf()->ty != Tfunction) && (e2->op == TOKsymoff || e2->op==TOKaddress || e2->op==TOKvar)) // && (e1->op==TOKaddress)) //TOKsymoff || e1->op==TOKdotvar)) + newval = this->e2->interpret(istate, ctfeNeedLvalue); + else + newval = this->e2->interpret(istate); + } if (newval == EXP_CANT_INTERPRET) return newval; - // ---------------------------------------------------- // Deal with read-modify-write assignments. // Set 'newval' to the final assignment value @@ -2411,26 +3053,52 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (fp) { - newval = (*fp)(type, oldval, newval); + // ~= can create new values (see bug 6052) + if (op == TOKcatass) + { + if (needToCopyLiteral(this->e2)) + newval = copyLiteral(newval); + if (newval->op == TOKslice) + newval = resolveSlice(newval); + } + if (oldval->op == TOKslice) + oldval = resolveSlice(oldval); + if (this->e1->type->ty == Tpointer && this->e2->type->isintegral() + && (op==TOKaddass || op == TOKminass)) + { + oldval = this->e1->interpret(istate, ctfeNeedLvalue); + newval = this->e2->interpret(istate); + if (oldval == EXP_CANT_INTERPRET || newval == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + newval = pointerArithmetic(loc, op, type, oldval, newval); + } + else if (this->e1->type->ty == Tpointer) + { + error("pointer expression %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + else + { + newval = (*fp)(type, oldval, newval); + } if (newval == EXP_CANT_INTERPRET) { error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } // Determine the return value - e = Cast(type, type, post ? oldval : newval); - if (e == EXP_CANT_INTERPRET) - return e; + returnValue = Cast(type, type, post ? oldval : newval); + if (returnValue == EXP_CANT_INTERPRET) + return returnValue; } else - e = newval; - + returnValue = newval; if (e1->op == TOKarraylength) { size_t oldlen = oldval->toInteger(); size_t newlen = newval->toInteger(); if (oldlen == newlen) // no change required -- we're done! - return e; + return returnValue; // Now change the assignment from arr.length = n into arr = newval e1 = ((ArrayLengthExp *)e1)->e1; if (oldlen != 0) @@ -2441,6 +3109,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ oldval = oldval->interpret(istate); } } + if (oldval->op == TOKslice) + oldval = resolveSlice(oldval); Type *t = e1->type->toBasetype(); if (t->ty == Tarray) { @@ -2452,15 +3122,29 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ Expressions *elements = new Expressions(); elements->setDim(newlen); size_t copylen = oldlen < newlen ? oldlen : newlen; + if (oldlen !=0) + assert(oldval->op == TOKarrayliteral); ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; for (size_t i = 0; i < copylen; i++) - elements->data[i] = ae->elements->data[i]; - - for (size_t i = copylen; i < newlen; i++) - elements->data[i] = defaultElem; + elements->data[i] = ae->elements->data[i]; + if (elemType->ty == Tstruct || elemType->ty == Tsarray) + { /* If it is an aggregate literal representing a value type, + * we need to create a unique copy for each element + */ + for (size_t i = copylen; i < newlen; i++) + elements->data[i] = copyLiteral(defaultElem); + } + else + { + for (size_t i = copylen; i < newlen; i++) + elements->data[i] = defaultElem; + } ArrayLiteralExp *aae = new ArrayLiteralExp(0, elements); aae->type = t; newval = aae; + // We have changed it into a reference assignment + // Note that returnValue is still the new length. + wantRef = true; } else { @@ -2478,7 +3162,12 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ newval = type->defaultInitLiteral(loc); } newval = Cast(type, type, newval); - e = newval; + if (newval == EXP_CANT_INTERPRET) + { + error("CTFE error: cannot cast %s to type %s", this->e2->toChars(), type->toChars()); + return EXP_CANT_INTERPRET; + } + returnValue = newval; } if (newval == EXP_CANT_INTERPRET) return newval; @@ -2497,14 +3186,18 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // This happens inside compiler-generated foreach statements. if (op==TOKconstruct && this->e1->op==TOKvar && this->e2->op != TOKthis + && this->e2->op != TOKcomma && ((VarExp*)this->e1)->var->storage_class & STCref) { - //error("assignment to ref variable %s is not yet supported in CTFE", this->toChars()); VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration(); v->setValueNull(); v->createStackValue(e2); +#if (LOGASSIGN) + printf("FOREACH ASSIGN %s=%s\n", v->toChars(), e2->toChars()); +#endif return e2; } + bool destinationIsReference = false; e1 = resolveReferences(e1, istate->localThis, &destinationIsReference); @@ -2514,247 +3207,60 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (e1->op != TOKvar && ultimateVar && !ultimateVar->getValue()) ultimateVar->createRefValue(copyLiteral(ultimateVar->type->defaultInitLiteral())); - // ---------------------------------------------------------- - // Deal with dotvar expressions - non-reference types - // ---------------------------------------------------------- - // Because structs are not reference types, dotvar expressions can be - // collapsed into a single assignment. - bool startedWithCall = false; - if (e1->op == TOKcall) - startedWithCall = true; - while (!wantRef && (e1->op == TOKdotvar || e1->op == TOKcall)) - { - ExpressionReverseIterator rvs(e1, istate->localThis); - Expression *lastNonDotVar = e1; - // Strip of all of the leading dotvars. - if (e1->op == TOKdotvar) - { - int numDotVars = 0; - while(lastNonDotVar->op == TOKdotvar) - { - ++numDotVars; - if (lastNonDotVar->op == TOKdotvar) - lastNonDotVar = ((DotVarExp *)lastNonDotVar)->e1; - lastNonDotVar = resolveReferences(lastNonDotVar, istate->localThis); - assert(lastNonDotVar); - } - // We need the value of this first nonvar, since only part of it will be - // modified. - Expression * existing = lastNonDotVar->interpret(istate); - if (existing == EXP_CANT_INTERPRET) - return existing; - assert(newval !=EXP_CANT_INTERPRET); - newval = assignDotVar(rvs, numDotVars, existing, newval); - e1 = lastNonDotVar; - if (e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - v->setRefValue(newval); - return e; - } - assert(newval !=EXP_CANT_INTERPRET); - - } // end tokdotvar - else - { - Expression * existing = lastNonDotVar->interpret(istate); - if (existing == EXP_CANT_INTERPRET) - return existing; - // It might be a reference. Turn it into an rvalue, by interpreting again. - existing = existing->interpret(istate); - if (existing == EXP_CANT_INTERPRET) - return existing; - assert(newval !=EXP_CANT_INTERPRET); - newval = assignDotVar(rvs, 0, existing, newval); - assert(newval !=EXP_CANT_INTERPRET); - } - if (e1->op == TOKcall) - { - bool oldWaiting = istate->awaitingLvalueReturn; - istate->awaitingLvalueReturn = true; - e1 = e1->interpret(istate); - istate->awaitingLvalueReturn = oldWaiting; - - if (e1 == EXP_CANT_INTERPRET) - return e1; - assert(newval); - assert(newval != EXP_CANT_INTERPRET); - } - } // --------------------------------------- // Deal with reference assignment + // (We already have 'newval' for arraylength operations) // --------------------------------------- - if (wantRef) + if (wantRef && this->e1->op != TOKarraylength) { - if (this->e2->op == TOKvar) - newval = this->e2; - else if (this->e2->op==TOKslice) - { - SliceExp * sexp = (SliceExp *)this->e2; - - /* Set the $ variable - */ - Expression *e1val = sexp->e1->interpret(istate); - Expression *dollar; - if (e1val->op == TOKnull) - dollar = new IntegerExp(0, 0, Type::tsize_t); - else - dollar = ArrayLength(Type::tsize_t, e1val); - - if (dollar != EXP_CANT_INTERPRET && sexp->lengthVar) - { - sexp->lengthVar->createStackValue(dollar); - } - Expression *upper = NULL; - Expression *lower = NULL; - if (sexp->upr) - upper = sexp->upr->interpret(istate); - else upper = dollar; - if (sexp->lwr) - lower = sexp->lwr->interpret(istate); - else - lower = new IntegerExp(loc, 0, Type::tsize_t); - if (sexp->lengthVar) - sexp->lengthVar->setValueNull(); // $ is defined only in [L..U] - if (upper == EXP_CANT_INTERPRET || lower == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - // We need the interpreted aggregate, except in the case where it - // was a variable. - if (sexp->e1->op == TOKvar) - e1val = sexp->e1; - newval = new SliceExp(sexp->loc, e1val, lower, upper); - newval->type = sexp->type; - } - else - newval = this->e2->interpret(istate, ctfeNeedLvalue); + newval = this->e2->interpret(istate, ctfeNeedLvalue); if (newval == EXP_CANT_INTERPRET) return newval; + // If it is an assignment from a array function parameter passed by + // reference, resolve the reference. (This should NOT happen for + // non-reference types). + if (newval->op == TOKvar && (newval->type->ty == Tarray || + newval->type->ty == Tclass)) + { + newval = newval->interpret(istate); + } - if (newval->op == TOKarrayliteral || (newval->op == TOKassocarrayliteral) - || newval->op == TOKstring) + if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || + newval->op==TOKarrayliteral) { if (needToCopyLiteral(this->e2)) newval = copyLiteral(newval); } - else if (newval->op == TOKnull) - { // do nothing - } - else if (newval->op == TOKvar) - { - VarExp *vv = (VarExp *)newval; - - VarDeclaration *v2 = vv->var->isVarDeclaration(); - assert(v2 && v2->getValue()); - newval = v2->getValue(); - } - else if ((e1->op == TOKdotvar || e1->op == TOKvar) && newval->op == TOKslice) - { - // This one is interesting because it could be a slice of itself - SliceExp * sexp = (SliceExp *)newval; - Expression *agg = sexp->e1; - dinteger_t newlo = sexp->lwr->toInteger(); - dinteger_t newup = sexp->upr->toInteger(); - if (agg->op == TOKvar) - { - VarExp *vv = (VarExp *)agg; - VarDeclaration *v2 = vv->var->isVarDeclaration(); - assert(v2 && v2->getValue()); - if (v2->getValue()->op == TOKarrayliteral - || v2->getValue()->op == TOKstring) - { - Expression *dollar = ArrayLength(Type::tsize_t, v2->getValue()); - if ((newup < newlo) || (newup > dollar->toInteger())) - { - error("slice [%jd..%jd] exceeds array bounds [0..%jd]", - newlo, newup, dollar->toInteger()); - return EXP_CANT_INTERPRET; - } - sexp->e1 = v2->getValue(); - newval = sexp; - } - else if (v2->getValue()->op == TOKslice) - { - SliceExp *sexpold = (SliceExp *)v2->getValue(); - sexp->e1 = sexpold->e1; - dinteger_t hi = newup + sexpold->lwr->toInteger(); - dinteger_t lo = newlo + sexpold->lwr->toInteger(); - if ((newup < newlo) || (hi > sexpold->upr->toInteger())) - { - error("slice [%jd..%jd] exceeds array bounds [0..%jd]", - newlo, newup, sexpold->upr->toInteger()-sexpold->lwr->toInteger()); - return EXP_CANT_INTERPRET; - } - sexp->lwr = new IntegerExp(loc, lo, Type::tsize_t); - sexp->upr = new IntegerExp(loc, hi, Type::tsize_t); - newval = sexp; - } - else - { - newval = newval->interpret(istate); - if (newval == EXP_CANT_INTERPRET) - return newval; - } - } - else - { - newval = newval->interpret(istate); - if (newval == EXP_CANT_INTERPRET) - return newval; - } - } - if (e1->op == TOKvar || e1->op == TOKdotvar) - { - - assert((newval->op == TOKarrayliteral || - newval->op == TOKassocarrayliteral || - newval->op == TOKstring || - newval->op == TOKslice || - newval->op == TOKnull) ); - if (newval->op == TOKslice) - { - Expression *sss = ((SliceExp *)newval)->e1; - assert(sss->op == TOKarrayliteral || sss->op == TOKstring); - } - } - - if (e1->op == TOKdotvar) - { - Expression *exx = ((DotVarExp *)e1)->e1->interpret(istate); - if (exx == EXP_CANT_INTERPRET) - return exx; - if (exx->op != TOKstructliteral) - { - error("CTFE internal error: Dotvar assignment"); - return EXP_CANT_INTERPRET; - } - StructLiteralExp *se3 = (StructLiteralExp *)exx; - VarDeclaration *vv = ((DotVarExp *)e1)->var->isVarDeclaration(); - if (!vv) - { - error("CTFE internal error: Dotvar assignment"); - return EXP_CANT_INTERPRET; - } - int se_indx = se3->getFieldIndex(e1->type, vv->offset); - se3->elements->data[se_indx] = newval; - // Mark the parent variable as modified - if (!destinationIsReference) - addVarToInterstate(istate, ultimateVar); - return newval; - } - else if (e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - if (!destinationIsReference) - addVarToInterstate(istate, v); - v->setValueNull(); - v->createRefValue(newval); - return newval; - } - e = newval; + returnValue = newval; } + // --------------------------------------- + // Deal with dotvar expressions + // --------------------------------------- + // Because structs are not reference types, dotvar expressions can be + // collapsed into a single assignment. + if (!wantRef && e1->op == TOKdotvar) + { + // Strip of all of the leading dotvars, unless we started with a call + // (in which case, we already have the lvalue). + if (this->e1->op != TOKcall) + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (e1 == EXP_CANT_INTERPRET) + return e1; + if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) + { + assignInPlace(e1, newval); + return returnValue; + } + } +#if LOGASSIGN + if (wantRef) + printf("REF ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); + else + printf("ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); + showCtfeExpr(newval); +#endif + /* Assignment to variable of the form: * v = newval */ @@ -2764,23 +3270,32 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ VarDeclaration *v = ve->var->isVarDeclaration(); if (!destinationIsReference) addVarToInterstate(istate, v); - if (e1->type->toBasetype()->ty == Tstruct) + if (wantRef) { - // This should be an in-place modification - if (newval->op == TOKstructliteral) + v->setValueNull(); + v->createRefValue(newval); + } + else if (e1->type->toBasetype()->ty == Tstruct) + { + // In-place modification + if (newval->op != TOKstructliteral) { - v->setValueNull(); - v->createRefValue(copyLiteral(newval)); + error("CTFE internal error assigning struct"); + return EXP_CANT_INTERPRET; } - else v->setRefValue(newval); + newval = copyLiteral(newval); + if (v->getValue()) + assignInPlace(v->getValue(), newval); + else + v->createRefValue(newval); } else { if (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray) { // arr op= arr if (!v->getValue()) - v->createRefValue(newval->interpret(istate)); - else v->setRefValue(newval->interpret(istate)); + v->createRefValue(newval); + else v->setRefValue(newval); } else { @@ -2791,13 +3306,49 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } } } + else if (e1->op == TOKdotvar) + { + /* Assignment to member variable of the form: + * e.v = newval + */ + Expression *exx = ((DotVarExp *)e1)->e1; + if (wantRef && exx->op != TOKstructliteral) + { + exx = exx->interpret(istate); + if (exx == EXP_CANT_INTERPRET) + return exx; + } + if (exx->op != TOKstructliteral) + { + error("CTFE internal error: Dotvar assignment"); + return EXP_CANT_INTERPRET; + } + StructLiteralExp *se = (StructLiteralExp *)exx; + VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); + if (!member) + { + error("CTFE internal error: Dotvar assignment"); + return EXP_CANT_INTERPRET; + } + int fieldi = se->getFieldIndex(member->type, member->offset); + if (fieldi == -1) + return EXP_CANT_INTERPRET; + assert(fieldi>=0 && fieldi < se->elements->dim); + if (newval->op == TOKstructliteral) + assignInPlace((Expression *)(se->elements->data[fieldi]), newval); + else + se->elements->data[fieldi] = newval; + if (ultimateVar && !destinationIsReference) + addVarToInterstate(istate, ultimateVar); + return returnValue; + } else if (e1->op == TOKindex) { /* Assignment to array element of the form: * aggregate[i] = newval */ IndexExp *ie = (IndexExp *)e1; - int destarraylen = 0; // not for AAs + uinteger_t destarraylen = 0; // not for AAs // Set the $ variable, and find the array literal to modify if (ie->e1->type->toBasetype()->ty != Taarray) @@ -2808,12 +3359,19 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ error("cannot index null array %s", ie->e1->toChars()); return EXP_CANT_INTERPRET; } - Expression *dollar = ArrayLength(Type::tsize_t, oldval); - if (dollar == EXP_CANT_INTERPRET) + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice) + { + error("cannot determine length of %s at compile time", + ie->e1->toChars()); return EXP_CANT_INTERPRET; - destarraylen = dollar->toInteger(); + } + destarraylen = resolveArrayLength(oldval); if (ie->lengthVar) - ie->lengthVar->createStackValue(dollar); + { + IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); + ie->lengthVar->createStackValue(dollarExp); + } } Expression *index = ie->e2->interpret(istate); if (ie->lengthVar) @@ -2830,7 +3388,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (ie->e1->type->toBasetype()->ty != Taarray) { indexToModify = index->toInteger(); - if (indexToModify > destarraylen) + if (indexToModify >= destarraylen) { error("array index %d is out of bounds [0..%d]", indexToModify, destarraylen); @@ -2843,9 +3401,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ /* The only possible indexable LValue aggregates are array literals, * slices of array literals, and AA literals. */ - if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || - aggregate->op == TOKslice) + aggregate->op == TOKslice || aggregate->op == TOKcall) { aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) @@ -2856,7 +3413,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ VarExp *ve = (VarExp *)aggregate; VarDeclaration *v = ve->var->isVarDeclaration(); aggregate = v->getValue(); - if (v->getValue()->op == TOKnull) + if (aggregate->op == TOKnull) { if (v->type->ty == Taarray) { // Assign to empty associative array @@ -2871,7 +3428,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ aae2->type = v->type; newval = aae2; v->setRefValue(newval); - return e; + return returnValue; } // This would be a runtime segfault error("cannot index null array %s", v->toChars()); @@ -2896,10 +3453,23 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ error("CTFE internal compiler error %s", aggregate->toChars()); return EXP_CANT_INTERPRET; } + if (!wantRef && newval->op == TOKslice) + { + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) + { + error("Compiler error: CTFE index assign %s", toChars()); + assert(0); + } + } + if (existingAE) { - existingAE->elements->data[indexToModify] = newval; - return e; + if (newval->op == TOKstructliteral) + assignInPlace((Expression *)(existingAE->elements->data[indexToModify]), newval); + else + existingAE->elements->data[indexToModify] = newval; + return returnValue; } if (existingSE) { @@ -2914,20 +3484,20 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ assert(0); break; } - return e; + return returnValue; } else if (existingAA) { if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; - return e; + return returnValue; } else { error("Index assignment %s is not yet supported in CTFE ", toChars()); return EXP_CANT_INTERPRET; } - return e; + return returnValue; } else if (e1->op == TOKslice) { @@ -2937,19 +3507,30 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // ------------------------------ SliceExp * sexp = (SliceExp *)e1; // Set the $ variable - Expression *oldval = sexp->e1->interpret(istate); - if (oldval->op == TOKnull) + Expression *oldval = sexp->e1; + bool assignmentToSlicedPointer = false; + if (oldval->type->toBasetype()->ty == Tpointer && oldval->type->toBasetype()->nextOf()->ty != Tfunction) + { // Slicing a pointer + oldval = oldval->interpret(istate, ctfeNeedLvalue); + dinteger_t ofs; + oldval = getAggregateFromPointer(oldval, &ofs); + assignmentToSlicedPointer = true; + } else + oldval = oldval->interpret(istate); + + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice && oldval->op != TOKnull) { - error("cannot slice null array %s", sexp->e1->toChars()); + error("CTFE ICE: cannot resolve array length"); return EXP_CANT_INTERPRET; } - Expression *arraylen = ArrayLength(Type::tsize_t, oldval); - if (arraylen == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; + uinteger_t dollar = resolveArrayLength(oldval); if (sexp->lengthVar) { + Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); sexp->lengthVar->createStackValue(arraylen); } + Expression *upper = NULL; Expression *lower = NULL; if (sexp->upr) @@ -2960,16 +3541,19 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ sexp->lengthVar->setValueNull(); // $ is defined only in [L..U] if (upper == EXP_CANT_INTERPRET || lower == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; - int dim = arraylen->toInteger(); + + int dim = dollar; int upperbound = upper ? upper->toInteger() : dim; int lowerbound = lower ? lower->toInteger() : 0; - if (((int)lowerbound < 0) || (upperbound > dim)) + if (!assignmentToSlicedPointer && (((int)lowerbound < 0) || (upperbound > dim))) { error("Array bounds [0..%d] exceeded in slice [%d..%d]", dim, lowerbound, upperbound); return EXP_CANT_INTERPRET; } + if (upperbound == lowerbound) + return newval; Expression *aggregate = resolveReferences(((SliceExp *)e1)->e1, istate->localThis); int firstIndex = lowerbound; @@ -2982,7 +3566,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ */ if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || - aggregate->op == TOKslice) + aggregate->op == TOKslice || aggregate->op == TOKcall) { aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) @@ -3008,11 +3592,35 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } aggregate = sexpold->e1; } + if (aggregate->type->toBasetype()->ty == Tpointer && aggregate->type->toBasetype()->nextOf()->ty != Tfunction) + { // Slicing a pointer --> change the bounds + aggregate = sexp->e1->interpret(istate, ctfeNeedLvalue); + dinteger_t ofs; + aggregate = getAggregateFromPointer(aggregate, &ofs); + dinteger_t hi = upperbound + ofs; + firstIndex = lowerbound + ofs; + if (firstIndex < 0 || hi > dim) + { + error("slice [%d..%jd] exceeds memory block bounds [0..%jd]", + firstIndex, hi, dim); + return EXP_CANT_INTERPRET; + } + } if (aggregate->op==TOKarrayliteral) existingAE = (ArrayLiteralExp *)aggregate; else if (aggregate->op==TOKstring) existingSE = (StringExp *)aggregate; + if (!wantRef && newval->op == TOKslice) + { + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) + { + error("Compiler error: CTFE slice %s", toChars()); + assert(0); + } + } + // For slice assignment, we check that the lengths match. size_t srclen = 0; if (newval->op == TOKarrayliteral) @@ -3037,57 +3645,21 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } else if (newval->op == TOKstring && existingSE) { - StringExp * newstr = (StringExp *)newval; - unsigned char *s = (unsigned char *)existingSE->string; - size_t sz = existingSE->sz; - assert(sz == ((StringExp *)newval)->sz); - memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len); + sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, firstIndex); return newval; } else if (newval->op == TOKstring && existingAE) { /* Mixed slice: it was initialized as an array literal of chars. * Now a slice of it is being set with a string. */ - size_t newlen = ((StringExp *)newval)->len; - size_t sz = ((StringExp *)newval)->sz; - unsigned char *s = (unsigned char *)((StringExp *)newval)->string; - Type *elemType = existingAE->type->nextOf(); - for (size_t j = 0; j < newlen; j++) - { - dinteger_t val; - switch (sz) - { - case 1: val = s[j]; break; - case 2: val = ((unsigned short *)s)[j]; break; - case 4: val = ((unsigned *)s)[j]; break; - default: - assert(0); - break; - } - existingAE->elements->data[j+firstIndex] - = new IntegerExp(newval->loc, val, elemType); - } + sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, firstIndex); return newval; } else if (newval->op == TOKarrayliteral && existingSE) { /* Mixed slice: it was initialized as a string literal. * Now a slice of it is being set with an array literal. */ - unsigned char *s = (unsigned char *)existingSE->string; - ArrayLiteralExp *newae = (ArrayLiteralExp *)newval; - for (size_t j = 0; j < newae->elements->dim; j++) - { - unsigned value = ((Expression *)(newae->elements->data[j]))->toInteger(); - switch (existingSE->sz) - { - case 1: s[j+firstIndex] = value; break; - case 2: ((unsigned short *)s)[j+firstIndex] = value; break; - case 4: ((unsigned *)s)[j+firstIndex] = value; break; - default: - assert(0); - break; - } - } + sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, firstIndex); return newval; } else if (existingSE) @@ -3131,13 +3703,20 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ Type *desttype = ((TypeArray *)existingAE->type)->next; bool directblk = (e2->type->toBasetype()) == desttype; #endif + bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral + || newval->op == TOKstring); for (size_t j = 0; j < upperbound-lowerbound; j++) { if (!directblk) // Multidimensional array block assign - recursiveBlockAssign((ArrayLiteralExp *)w->data[j+firstIndex], newval); + recursiveBlockAssign((ArrayLiteralExp *)w->data[j+firstIndex], newval, wantRef); else - existingAE->elements->data[j+firstIndex] = newval; + { + if (wantRef || cow) + existingAE->elements->data[j+firstIndex] = newval; + else + assignInPlace((Expression *)existingAE->elements->data[j+firstIndex], newval); + } } if (goal == ctfeNeedNothing) return NULL; // avoid creating an unused literal @@ -3150,40 +3729,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ else error("Slice operation %s cannot be evaluated at compile time", toChars()); } - else if (e1->op == TOKstar) - { - /* Assignment to struct member of the form: - * *(symoffexp) = newval - */ - if (((PtrExp *)e1)->e1->op == TOKsymoff) - { SymOffExp *soe = (SymOffExp *)((PtrExp *)e1)->e1; - VarDeclaration *v = soe->var->isVarDeclaration(); - if (v->isDataseg() && !v->isCTFE()) - { - error("%s cannot be modified at compile time", v->toChars()); - return EXP_CANT_INTERPRET; - } - if (fp && !v->getValue()) - { error("variable %s is used before initialization", v->toChars()); - return e; - } - Expression *vie = v->getValue(); - if (vie->op == TOKvar) - { - Declaration *d = ((VarExp *)vie)->var; - vie = getVarExp(e1->loc, istate, d, ctfeNeedRvalue); - } - if (vie->op != TOKstructliteral) - return EXP_CANT_INTERPRET; - - StructLiteralExp *se = (StructLiteralExp *)vie; - - newval = modifyStructField(type, se, soe->offset, newval); - - addVarToInterstate(istate, v); - v->setRefValue(newval); - } - } else { error("%s cannot be evaluated at compile time", toChars()); @@ -3191,7 +3736,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ dump(0); #endif } - return e; + return returnValue; } Expression *AssignExp::interpret(InterState *istate, CtfeGoal goal) @@ -3394,6 +3939,14 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) pthis = istate ? istate->localThis : NULL; else if (pthis->op == TOKcomma) pthis = pthis->interpret(istate); + if (pthis == EXP_CANT_INTERPRET) + return NULL; + // Evaluate 'this' + if (pthis->op != TOKvar) + pthis = pthis->interpret(istate, ctfeNeedLvalue); + if (pthis == EXP_CANT_INTERPRET) + return NULL; + if (!fd->fbody) { error("%s cannot be interpreted at compile time," @@ -3447,7 +4000,10 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) e = e->interpret(istate); if (e != EXP_CANT_INTERPRET) { + if (e->op == TOKslice) + e= resolveSlice(e); e = expType(type, e); + e = copyLiteral(e); } } else @@ -3494,7 +4050,8 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) // If the comma returns a temporary variable, it needs to be an lvalue // (this is particularly important for struct constructors) if (e1->op == TOKdeclaration && e2->op == TOKvar - && ((DeclarationExp *)e1)->declaration == ((VarExp*)e2)->var) + && ((DeclarationExp *)e1)->declaration == ((VarExp*)e2)->var + && ((VarExp*)e2)->var->storage_class & STCctfe) // same as Expression::isTemp { VarExp* ve = (VarExp *)e2; VarDeclaration *v = ve->var->isVarDeclaration(); @@ -3515,11 +4072,13 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) v->setValueWithoutChecking(copyLiteral(newval)); } } - return e2; + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + return e2; + return e2->interpret(istate, goal); } - Expression *e = e1->interpret(istate); + Expression *e = e1->interpret(istate, ctfeNeedNothing); if (e != EXP_CANT_INTERPRET) - e = e2->interpret(istate); + e = e2->interpret(istate, goal); return e; } @@ -3528,15 +4087,29 @@ Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("CondExp::interpret() %s\n", toChars()); #endif - Expression *e = econd->interpret(istate); + Expression *e; + if (econd->type->ty == Tpointer && econd->type->nextOf()->ty != Tfunction) + { + e = econd->interpret(istate, ctfeNeedLvalue); + if (e == EXP_CANT_INTERPRET) + return e; + if (e->op != TOKnull) + e = new IntegerExp(loc, 1, Type::tbool); + } + else + e = econd->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (e->isBool(TRUE)) - e = e1->interpret(istate); + e = e1->interpret(istate, goal); else if (e->isBool(FALSE)) - e = e2->interpret(istate); + e = e2->interpret(istate, goal); else + { + error("%s does not evaluate to boolean result at compile time", + econd->toChars()); e = EXP_CANT_INTERPRET; + } } return e; } @@ -3551,59 +4124,81 @@ Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) e1 = this->e1->interpret(istate); assert(e1); if (e1 == EXP_CANT_INTERPRET) - goto Lcant; - if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKassocarrayliteral) + return EXP_CANT_INTERPRET; + if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice + || e1->op == TOKassocarrayliteral || e1->op == TOKnull) { - e = ArrayLength(type, e1); - } - else if (e1->op == TOKnull) - { - e = new IntegerExp(loc, 0, type); + e = new IntegerExp(loc, resolveArrayLength(e1), type); } else - goto Lcant; + { + error("%s cannot be evaluated at compile time", toChars()); + return EXP_CANT_INTERPRET; + } return e; +} -Lcant: - return EXP_CANT_INTERPRET; +/* Given an AA literal 'ae', and a key 'e2': + * Return ae[e2] if present, or NULL if not found. + * Return EXP_CANT_INTERPRET on error. + */ +Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2) +{ + /* Search the keys backwards, in case there are duplicate keys + */ + for (size_t i = ae->keys->dim; i;) + { + i--; + Expression *ekey = (Expression *)ae->keys->data[i]; + Expression *ex = Equal(TOKequal, Type::tbool, ekey, e2); + if (ex == EXP_CANT_INTERPRET) + { + error("cannot evaluate %s==%s at compile time", + ekey->toChars(), e2->toChars()); + return ex; + } + if (ex->isBool(TRUE)) + { + return (Expression *)ae->values->data[i]; + } + } + return NULL; } Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; +{ + Expression *e1 = NULL; Expression *e2; #if LOG printf("IndexExp::interpret() %s\n", toChars()); #endif - e1 = this->e1->interpret(istate, goal); + + e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + return EXP_CANT_INTERPRET; if (e1->op == TOKnull) { error("cannot index null array %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } - - if (e1->op == TOKstring || e1->op == TOKarrayliteral) + /* Set the $ variable. + * Note that foreach uses indexing but doesn't need $ + */ + if (lengthVar && (e1->op == TOKstring || e1->op == TOKarrayliteral + || e1->op == TOKslice)) { - /* Set the $ variable - */ - e = ArrayLength(Type::tsize_t, e1); - if (e == EXP_CANT_INTERPRET) - goto Lcant; - if (lengthVar) - { - lengthVar->createStackValue(e); - } + uinteger_t dollar = resolveArrayLength(e1); + Expression *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + lengthVar->createStackValue(dollarExp); } e2 = this->e2->interpret(istate); if (lengthVar) lengthVar->setValueNull(); // $ is defined only inside [] if (e2 == EXP_CANT_INTERPRET) - goto Lcant; + return EXP_CANT_INTERPRET; if (e1->op == TOKslice && e2->op == TOKint64) { // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] @@ -3614,24 +4209,53 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) if (indx > iup - ilo) { error("index %ju exceeds array length %ju", indx, iup - ilo); - goto Lcant; + return EXP_CANT_INTERPRET; } indx += ilo; e1 = ((SliceExp *)e1)->e1; e2 = new IntegerExp(e2->loc, indx, e2->type); } - e = Index(type, e1, e2); + Expression *e = NULL; + if ((goal == ctfeNeedLvalue && type->ty != Taarray && type->ty != Tarray + && type->ty != Tsarray && type->ty != Tstruct && type->ty != Tclass) + || (goal == ctfeNeedLvalueRef && type->ty != Tsarray && type->ty != Tstruct) + ) + { // Pointer or reference of a scalar type + e = new IndexExp(loc, e1, e2); + e->type = type; + return e; + } + if (e1->op == TOKassocarrayliteral) + { + e = findKeyInAA((AssocArrayLiteralExp *)e1, e2); + if (!e) + { + error("key %s not found in associative array %s", + e2->toChars(), this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (e == EXP_CANT_INTERPRET) + return e; + assert(!e->checkSideEffect(2)); + e = paintTypeOntoLiteral(type, e); + } + else + { + e = Index(type, e1, e2); + } if (e == EXP_CANT_INTERPRET) + { error("%s cannot be interpreted at compile time", toChars()); + return e; + } + if (goal == ctfeNeedRvalue && (e->op == TOKslice || e->op == TOKdotvar)) + e = e->interpret(istate); return e; - -Lcant: - return EXP_CANT_INTERPRET; } Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; +{ Expression *e1; Expression *lwr; Expression *upr; @@ -3639,51 +4263,103 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("SliceExp::interpret() %s\n", toChars()); #endif - e1 = this->e1->interpret(istate, goal); + + if (this->e1->type->toBasetype()->ty == Tpointer) + { + // Slicing a pointer. Note that there is no $ in this case. + e1 = this->e1->interpret(istate); + if (e1 == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + + /* Evaluate lower and upper bounds of slice + */ + lwr = this->lwr->interpret(istate); + if (lwr == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + upr = this->upr->interpret(istate); + if (upr == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + uinteger_t ilwr; + uinteger_t iupr; + ilwr = lwr->toInteger(); + iupr = upr->toInteger(); + Expression *e; + dinteger_t ofs; + Expression *agg = getAggregateFromPointer(e1, &ofs); + if (agg->op == TOKnull) + { + if (iupr == ilwr) + { + IntegerExp * zero = new IntegerExp(loc, 0, Type::tsize_t); + e = new SliceExp(loc, agg, zero, zero); + e->type = type; + return e; + } + error("cannot slice null pointer %s", this->e1->toChars()); + return EXP_CANT_INTERPRET; + } + assert(agg->op == TOKarrayliteral || agg->op == TOKstring); + dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); + Type *pointee = ((TypePointer *)agg->type)->next; + if ((ilwr + ofs) < 0 || (iupr+ofs) > (len + 1) || iupr < ilwr) + { + error("pointer slice [%jd..%jd] exceeds allocated memory block [0..%jd]", + ilwr+ofs, iupr+ofs, len); + return EXP_CANT_INTERPRET; + } + e = new SliceExp(loc, agg, lwr, upr); + e->type = type; + return e; + } + if (goal == ctfeNeedRvalue && this->e1->op == TOKstring) + e1 = this->e1; // Will get duplicated anyway + else + e1 = this->e1->interpret(istate, goal); if (e1 == EXP_CANT_INTERPRET) - goto Lcant; + return EXP_CANT_INTERPRET; + if (e1->op == TOKvar) + e1 = e1->interpret(istate); if (!this->lwr) { - if (goal == ctfeNeedLvalue) + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) return e1; - e = e1->castTo(NULL, type); + Expression *e = e1->castTo(NULL, type); return e->interpret(istate); } /* Set the $ variable */ - if (e1->op == TOKnull) - e = new IntegerExp(0, 0, Type::tsize_t); - else if (e1->op == TOKslice) - { - // For lvalue slices, slice ends have already been calculated - e = new IntegerExp(0, ((SliceExp *)e1)->upr->toInteger() - - ((SliceExp *)e1)->lwr->toInteger(), Type::tsize_t); - } - else - e = ArrayLength(Type::tsize_t, e1); - if (e == EXP_CANT_INTERPRET) + if (e1->op != TOKarrayliteral && e1->op != TOKstring && + e1->op != TOKnull && e1->op != TOKslice) { error("Cannot determine length of %s at compile time\n", e1->toChars()); - goto Lcant; + return EXP_CANT_INTERPRET; } + uinteger_t dollar = resolveArrayLength(e1); if (lengthVar) - lengthVar->createStackValue(e); + { + IntegerExp *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); + lengthVar->createStackValue(dollarExp); + } /* Evaluate lower and upper bounds of slice */ lwr = this->lwr->interpret(istate); - if (lwr == EXP_CANT_INTERPRET) - goto Lcant; - upr = this->upr->interpret(istate); - if (upr == EXP_CANT_INTERPRET) - goto Lcant; + if (lwr != EXP_CANT_INTERPRET) + upr = this->upr->interpret(istate); if (lengthVar) lengthVar->setValueNull(); // $ is defined only inside [L..U] + if (lwr == EXP_CANT_INTERPRET || upr == EXP_CANT_INTERPRET) { - uinteger_t ilwr = lwr->toInteger(); - uinteger_t iupr = upr->toInteger(); + return EXP_CANT_INTERPRET; + } + + Expression *e; + uinteger_t ilwr; + uinteger_t iupr; + ilwr = lwr->toInteger(); + iupr = upr->toInteger(); if (e1->op == TOKnull) { if (ilwr== 0 && iupr == 0) @@ -3691,45 +4367,70 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) e1->error("slice [%ju..%ju] is out of bounds", ilwr, iupr); return EXP_CANT_INTERPRET; } - if (goal == ctfeNeedLvalue) + if (e1->op == TOKslice) { - if (e1->op == TOKslice) + SliceExp *se = (SliceExp *)e1; + // Simplify slice of slice: + // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] + uinteger_t lo1 = se->lwr->toInteger(); + uinteger_t up1 = se->upr->toInteger(); + if (ilwr > iupr || iupr > up1 - lo1) { - SliceExp *se = (SliceExp *)e1; - // Simplify slice of slice: - // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] - uinteger_t lo1 = se->lwr->toInteger(); - uinteger_t up1 = se->upr->toInteger(); - if (ilwr > iupr || iupr > up1 - lo1) - { - error("slice[%ju..%ju] exceeds array bounds[%ju..%ju]", - ilwr, iupr, lo1, up1); - goto Lcant; - } - ilwr += lo1; - iupr += lo1; - e = new SliceExp(loc, se->e1, - new IntegerExp(loc, ilwr, lwr->type), - new IntegerExp(loc, iupr, upr->type)); - e->type = type; - return e; + error("slice[%ju..%ju] exceeds array bounds[%ju..%ju]", + ilwr, iupr, lo1, up1); + return EXP_CANT_INTERPRET; } - e = new SliceExp(loc, e1, lwr, upr); + ilwr += lo1; + iupr += lo1; + e = new SliceExp(loc, se->e1, + new IntegerExp(loc, ilwr, lwr->type), + new IntegerExp(loc, iupr, upr->type)); e->type = type; return e; } - e = Slice(type, e1, lwr, upr); - if (e == EXP_CANT_INTERPRET) - error("%s cannot be interpreted at compile time", toChars()); + if (e1->op == TOKarrayliteral + || e1->op == TOKstring) + { + if (iupr < ilwr || ilwr < 0 || iupr > dollar) + { + error("slice [%jd..%jd] exceeds array bounds [0..%jd]", + ilwr, iupr, dollar); + return EXP_CANT_INTERPRET; + } } + e = new SliceExp(loc, e1, lwr, upr); + e->type = type; return e; - -Lcant: - if (lengthVar) - lengthVar->setValueNull(); - return EXP_CANT_INTERPRET; } +Expression *InExp::interpret(InterState *istate, CtfeGoal goal) +{ Expression *e = EXP_CANT_INTERPRET; + +#if LOG + printf("InExp::interpret() %s\n", toChars()); +#endif + Expression *e1 = this->e1->interpret(istate); + if (e1 == EXP_CANT_INTERPRET) + return e1; + Expression *e2 = this->e2->interpret(istate); + if (e2 == EXP_CANT_INTERPRET) + return e2; + if (e2->op == TOKnull) + return new NullExp(loc, type); + if (e2->op != TOKassocarrayliteral) + { + error(" %s cannot be interpreted at compile time", toChars()); + return EXP_CANT_INTERPRET; + } + e = findKeyInAA((AssocArrayLiteralExp *)e2, e1); + if (e == EXP_CANT_INTERPRET) + return e; + if (!e) + return new NullExp(loc, type); + e = new IndexExp(loc, e2, e1); + e->type = type; + return e; +} Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; @@ -3744,9 +4445,15 @@ Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) { goto Lcant; } + if (e1->op == TOKslice) + { + e1 = resolveSlice(e1); + } e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) goto Lcant; + if (e2->op == TOKslice) + e2 = resolveSlice(e2); e = Cat(type, e1, e2); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); @@ -3767,9 +4474,63 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("CastExp::interpret() %s\n", toChars()); #endif - e1 = this->e1->interpret(istate); + e1 = this->e1->interpret(istate, goal); if (e1 == EXP_CANT_INTERPRET) goto Lcant; + if (to->ty == Tpointer && e1->op != TOKnull) + { // Deal with casts from char[] to char * + if (e1->op == TOKslice) + { + if ( ((SliceExp *)e1)->e1->op == TOKnull) + { + return paintTypeOntoLiteral(type, ((SliceExp *)e1)->e1); + } + e = new IndexExp(loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr); + e->type = type; + return e; + } + if (e1->op == TOKarrayliteral) + { + e = new IndexExp(loc, e1, new IntegerExp(loc, 0, Type::tsize_t)); + e->type = type; + return e; + } + if (e1->op == TOKstring) + { + return e1; + } + } + if (to->ty == Tarray && e1->op == TOKslice) + { + e1 = new SliceExp(e1->loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr, + ((SliceExp *)e1)->upr); + e1->type = to; + return e1; + } + // Disallow array type painting, except for conversions between built-in + // types of identical size. + if ((to->ty == Tsarray || to->ty == Tarray) && + (e1->type->ty == Tsarray || e1->type->ty == Tarray) && +#if DMDV2 + e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) +#else + e1->type->nextOf() != to->nextOf() +#endif + && !(to->nextOf()->isTypeBasic() && e1->type->nextOf()->isTypeBasic() + && to->nextOf()->size() == e1->type->nextOf()->size()) ) + { + error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); + return EXP_CANT_INTERPRET; + } + if (to->ty == Tsarray && e1->op == TOKslice) + e1 = resolveSlice(e1); + if (to->toBasetype()->ty == Tbool && e1->type->ty==Tpointer) + { + return new IntegerExp(loc, e1->op != TOKnull, to); + } + if (e1->op == TOKnull) + return paintTypeOntoLiteral(to, e1); + e = Cast(type, to, e1); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); @@ -3790,16 +4551,6 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("AssertExp::interpret() %s\n", toChars()); #endif - if( this->e1->op == TOKaddress) - { // Special case: deal with compiler-inserted assert(&this, "null this") - AddrExp *ade = (AddrExp *)this->e1; - if (ade->e1->op == TOKthis && istate->localThis) - if (istate->localThis->op == TOKdotvar - && ((DotVarExp *)(istate->localThis))->e1->op == TOKthis) - return getVarExp(loc, istate, ((DotVarExp*)(istate->localThis))->var, ctfeNeedRvalue); - else - return istate->localThis->interpret(istate); - } if (this->e1->op == TOKthis) { if (istate->localThis) @@ -3811,10 +4562,20 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) return istate->localThis->interpret(istate); } } - e1 = this->e1->interpret(istate); + // Deal with pointers (including compiler-inserted assert(&this, "null this")) + if (this->e1->type->ty == Tpointer && this->e1->type->nextOf()->ty != Tfunction) + { + e1 = this->e1->interpret(istate, ctfeNeedLvalue); + if (e1 == EXP_CANT_INTERPRET) + goto Lcant; + if (e1->op != TOKnull) + return new IntegerExp(loc, 1, Type::tbool); + } + else + e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; - if (e1->isBool(TRUE)) + if ((this->e1->op == TOKaddress && e1->op != TOKnull) || e1->isBool(TRUE)) { } else if (e1->isBool(FALSE)) @@ -3831,7 +4592,10 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) goto Lcant; } else + { + error("%s is not a compile-time boolean expression", e1->toChars()); goto Lcant; + } return e1; Lcant: @@ -3844,7 +4608,6 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) #if LOG printf("PtrExp::interpret() %s\n", toChars()); #endif - // Constant fold *(&structliteral + offset) if (e1->op == TOKadd) { AddExp *ae = (AddExp *)e1; @@ -3866,19 +4629,6 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) } e = Ptr(type, e1); } - else if (e1->op == TOKsymoff) - { SymOffExp *soe = (SymOffExp *)e1; - VarDeclaration *v = soe->var->isVarDeclaration(); - if (v) - { Expression *ev = getVarExp(loc, istate, v, ctfeNeedLvalue); - if (ev != EXP_CANT_INTERPRET && ev->op == TOKstructliteral) - { StructLiteralExp *se = (StructLiteralExp *)ev; - e = se->getField(type, soe->offset); - if (!e) - e = EXP_CANT_INTERPRET; - } - } - } #if DMDV2 #else // this is required for D1, where structs return *this instead of 'this'. else if (e1->op == TOKthis) @@ -3888,7 +4638,53 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) } #endif else - error("Cannot interpret %s at compile time", toChars()); + { // It's possible we have an array bounds error. We need to make sure it + // errors with this line number, not the one where the pointer was set. + e = e1->interpret(istate, ctfeNeedLvalue); + if (e == EXP_CANT_INTERPRET) + return e; + if (e->op == TOKaddress) + e = ((AddrExp*)e)->e1; + if (goal != ctfeNeedLvalue) + { + if (e->op == TOKindex && e->type->ty == Tpointer) + { + IndexExp *ie = (IndexExp *)e; + if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + && ie->e2->op == TOKint64) + { + Expression *dollar = ArrayLength(Type::tsize_t, ie->e1); + dinteger_t len = dollar->toInteger(); + dinteger_t indx = ie->e2->toInteger(); + assert(indx >=0 && indx <= len); // invalid pointer + if (indx == len) + { + error("dereference of pointer %s one past end of memory block limits [0..%jd]", + toChars(), len); + return EXP_CANT_INTERPRET; + } + return Index(type, ie->e1, ie->e2); + } + } + if (e->op == TOKstructliteral) + return e; + e = e1->interpret(istate, goal); + if (e->op == TOKaddress) + { + e = ((AddrExp*)e)->e1; + if (e->op == TOKdotvar || e->op == TOKindex) + e = e->interpret(istate, goal); + } + if (e == EXP_CANT_INTERPRET) + return e; + e->type = type; + } + if (e->op == TOKnull) + { + error("dereference of null pointer '%s'", e1->toChars()); + return EXP_CANT_INTERPRET; + } + } #if LOG if (e == EXP_CANT_INTERPRET) @@ -3907,12 +4703,14 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) Expression *ex = e1->interpret(istate); if (ex != EXP_CANT_INTERPRET) { + if (ex->op == TOKaddress) + ex = ((AddrExp *)ex)->e1; if (ex->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)ex; VarDeclaration *v = var->isVarDeclaration(); if (v) { - if (goal == ctfeNeedLvalue) + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { // We can't use getField, because it makes a copy int i = se->getFieldIndex(type, v->offset); @@ -3923,12 +4721,24 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) } e = (Expression *)se->elements->data[i]; // If it is an lvalue literal, return it... - if (e->op == TOKstructliteral || e->op == TOKarrayliteral || + if (e->op == TOKstructliteral) + return e; + if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( + e->op == TOKarrayliteral || e->op == TOKassocarrayliteral || e->op == TOKstring || - e->op == TOKslice) + e->op == TOKslice)) + return e; + /* Element is an allocated pointer, which was created in + * CastExp. + */ + if (goal == ctfeNeedLvalue && e->op == TOKindex && + e->type == type && + (type->ty == Tpointer && type->nextOf()->ty != Tfunction)) return e; // ...Otherwise, just return the (simplified) dotvar expression - return new DotVarExp(loc, ex, v); + e = new DotVarExp(loc, ex, v); + e->type = type; + return e; } e = se->getField(type, v->offset); if (!e) @@ -3936,7 +4746,16 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) error("couldn't find field %s in %s", v->toChars(), type->toChars()); e = EXP_CANT_INTERPRET; } - return e; + // If it is an rvalue literal, return it... + if (e->op == TOKstructliteral || e->op == TOKarrayliteral || + e->op == TOKassocarrayliteral || e->op == TOKstring) + return e; + if (type->ty == Tpointer && type->nextOf()->ty != Tfunction) + { + assert(e->type == type); + return e; + } + return e->interpret(istate, goal); } } else @@ -3980,15 +4799,15 @@ Expression *interpret_aaKeys(InterState *istate, Expressions *arguments) earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); Type *elemType = ((TypeAArray *)aae->type)->index; e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); - return e; + return copyLiteral(e); } Expression *interpret_aaValues(InterState *istate, Expressions *arguments) @@ -4002,16 +4821,16 @@ Expression *interpret_aaValues(InterState *istate, Expressions *arguments) earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->values); Type *elemType = ((TypeAArray *)aae->type)->next; e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); - printf("result is %s\n", e->toChars()); - return e; + //printf("result is %s\n", e->toChars()); + return copyLiteral(e); } #endif @@ -4039,10 +4858,10 @@ Expression *interpret_keys(InterState *istate, Expression *earg, FuncDeclaration earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); @@ -4050,7 +4869,7 @@ Expression *interpret_keys(InterState *istate, Expression *earg, FuncDeclaration assert(fd->type->nextOf()->ty == Tarray); Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->keys->dim)); - return e; + return copyLiteral(e); } Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclaration *fd) @@ -4061,10 +4880,10 @@ Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclarati earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); + if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) + return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->values); @@ -4073,7 +4892,7 @@ Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclarati Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->values->dim)); //printf("result is %s\n", e->toChars()); - return e; + return copyLiteral(e); } #endif @@ -4084,14 +4903,55 @@ Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclarati bool isStackValueValid(Expression *newval) { + if (newval->type->ty == Tpointer && newval->type->nextOf()->ty != Tfunction) + { + if (newval->op == TOKaddress || newval->op == TOKnull || + newval->op == TOKstring) + return true; + if (newval->op == TOKindex) + { + Expression *g = ((IndexExp *)newval)->e1; + if (g->op == TOKarrayliteral || g->op == TOKstring || + g->op == TOKassocarrayliteral) + return true; + } + if (newval->op == TOKvar) + return true; + newval->error("CTFE internal error: illegal pointer value %s\n", newval->toChars()); + return false; + } if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || (newval->op == TOKnull) || (newval->op == TOKslice)) { return false; } - if (newval->op == TOKvar) return true; - if (newval->op == TOKdotvar) return true; - if (newval->op == TOKindex) return true; + if (newval->op == TOKvar) + { + VarExp *ve = (VarExp *)newval; + VarDeclaration *vv = ve->var->isVarDeclaration(); + // Must not be a reference to a reference + if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) + return true; + } + if (newval->op == TOKdotvar) + { + if (((DotVarExp *)newval)->e1->op == TOKstructliteral) + return true; + } + if (newval->op == TOKindex) + { + IndexExp *ie = (IndexExp *)newval; + if (ie->e2->op == TOKint64) + { + if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + return true; + } + if (ie->e1->op == TOKassocarrayliteral) + return true; + // BUG: Happens ONLY in ref foreach. Should tighten this. + if (ie->e2->op == TOKvar) + return true; + } if (newval->op == TOKfunction) return true; // function/delegate literal if (newval->op == TOKdelegate) return true; if (newval->op == TOKsymoff) // function pointer @@ -4122,11 +4982,17 @@ bool isRefValueValid(Expression *newval) (newval->op == TOKnull)) { return true; } + // Dynamic arrays passed by ref may be null. When this happens + // they may originate from an index or dotvar expression. + if (newval->type->ty == Tarray || newval->type->ty == Taarray + || newval->type->ty == Tclass) + if (newval->op == TOKdotvar || newval->op == TOKindex) + return isStackValueValid(newval); // actually must be null if (newval->op == TOKslice) { SliceExp *se = (SliceExp *)newval; - assert(se->lwr && se->lwr != EXP_CANT_INTERPRET); - assert(se->upr && se->upr != EXP_CANT_INTERPRET); + assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64); + assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64); assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring); return true; } diff --git a/dmd2/intrange.c b/dmd2/intrange.c new file mode 100644 index 00000000..12128ca2 --- /dev/null +++ b/dmd2/intrange.c @@ -0,0 +1,1105 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by KennyTM +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include "intrange.h" +#include "mars.h" +#include "mtype.h" +#include "expression.h" + +#ifndef PERFORM_UNITTEST +#define PERFORM_UNITTEST 0 +#endif + +// Copy the sign to the value *x*. Equivalent to `sign ? -x : x`. +static uinteger_t copySign(uinteger_t x, bool sign) +{ + // return sign ? -x : x; + return (x - sign) ^ -sign; +} + +#ifndef UINT64_MAX +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + +//==================== SignExtendedNumber ====================================== + +SignExtendedNumber SignExtendedNumber::fromInteger(uinteger_t value_) +{ + return SignExtendedNumber(value_, value_ >> 63); +} + +bool SignExtendedNumber::operator==(const SignExtendedNumber& a) const +{ + return value == a.value && negative == a.negative; +} + +bool SignExtendedNumber::operator<(const SignExtendedNumber& a) const +{ + return (negative && !a.negative) + || (negative == a.negative && value < a.value); +} + +SignExtendedNumber SignExtendedNumber::extreme(bool minimum) +{ + return SignExtendedNumber(minimum-1, minimum); +} + +SignExtendedNumber SignExtendedNumber::max() +{ + return SignExtendedNumber(UINT64_MAX, false); +} + +SignExtendedNumber SignExtendedNumber::operator-() const +{ + if (value == 0) + return SignExtendedNumber(-negative); + else + return SignExtendedNumber(-value, !negative); +} + +SignExtendedNumber SignExtendedNumber::operator+(const SignExtendedNumber& a) const +{ + uinteger_t sum = value + a.value; + bool carry = sum < value && sum < a.value; + if (negative != a.negative) + return SignExtendedNumber(sum, !carry); + else if (negative) + return SignExtendedNumber(carry ? sum : 0, true); + else + return SignExtendedNumber(carry ? UINT64_MAX : sum, false); +} + +SignExtendedNumber SignExtendedNumber::operator-(const SignExtendedNumber& a) const +{ + if (a.isMinimum()) + return negative ? SignExtendedNumber(value, false) : max(); + else + return *this + (-a); +} + + +SignExtendedNumber SignExtendedNumber::operator*(const SignExtendedNumber& a) const +{ + // perform *saturated* multiplication, otherwise we may get bogus ranges + // like 0x10 * 0x10 == 0x100 == 0. + + /* Special handling for zeros: + INT65_MIN * 0 = 0 + INT65_MIN * + = INT65_MIN + INT65_MIN * - = INT65_MAX + 0 * anything = 0 + */ + if (value == 0) + { + if (!negative) + return *this; + else if (a.negative) + return max(); + else + return a.value == 0 ? a : *this; + } + else if (a.value == 0) + return a * *this; // don't duplicate the symmetric case. + + SignExtendedNumber rv; + // these are != 0 now surely. + uinteger_t tAbs = copySign(value, negative); + uinteger_t aAbs = copySign(a.value, a.negative); + rv.negative = negative != a.negative; + if (UINT64_MAX / tAbs < aAbs) + rv.value = rv.negative-1; + else + rv.value = copySign(tAbs * aAbs, rv.negative); + return rv; +} + +SignExtendedNumber SignExtendedNumber::operator/(const SignExtendedNumber& a) const +{ + /* special handling for zeros: + INT65_MIN / INT65_MIN = 1 + anything / INT65_MIN = 0 + + / 0 = INT65_MAX (eh?) + - / 0 = INT65_MIN (eh?) + */ + if (a.value == 0) + { + if (a.negative) + return SignExtendedNumber(value == 0 && negative); + else + return extreme(negative); + } + + uinteger_t aAbs = copySign(a.value, a.negative); + uinteger_t rvVal; + + if (!isMinimum()) + rvVal = copySign(value, negative) / aAbs; + // Special handling for INT65_MIN + // if the denominator is not a power of 2, it is same as UINT64_MAX / x. + else if (aAbs & (aAbs-1)) + rvVal = UINT64_MAX / aAbs; + // otherwise, it's the same as reversing the bits of x. + else + { + if (aAbs == 1) + return extreme(!a.negative); + rvVal = 1ULL << 63; + aAbs >>= 1; + if (aAbs & 0xAAAAAAAAAAAAAAAAULL) rvVal >>= 1; + if (aAbs & 0xCCCCCCCCCCCCCCCCULL) rvVal >>= 2; + if (aAbs & 0xF0F0F0F0F0F0F0F0ULL) rvVal >>= 4; + if (aAbs & 0xFF00FF00FF00FF00ULL) rvVal >>= 8; + if (aAbs & 0xFFFF0000FFFF0000ULL) rvVal >>= 16; + if (aAbs & 0xFFFFFFFF00000000ULL) rvVal >>= 32; + } + bool rvNeg = negative != a.negative; + rvVal = copySign(rvVal, rvNeg); + + return SignExtendedNumber(rvVal, rvVal != 0 && rvNeg); +} + +SignExtendedNumber SignExtendedNumber::operator%(const SignExtendedNumber& a) const +{ + if (a.value == 0) + return !a.negative ? a : isMinimum() ? SignExtendedNumber(0) : *this; + + uinteger_t aAbs = copySign(a.value, a.negative); + uinteger_t rvVal; + + // a % b == sgn(a) * abs(a) % abs(b). + if (!isMinimum()) + rvVal = copySign(value, negative) % aAbs; + // Special handling for INT65_MIN + // if the denominator is not a power of 2, it is same as UINT64_MAX%x + 1. + else if (aAbs & (aAbs - 1)) + rvVal = UINT64_MAX % aAbs + 1; + // otherwise, the modulus is trivially zero. + else + rvVal = 0; + + rvVal = copySign(rvVal, negative); + return SignExtendedNumber(rvVal, rvVal != 0 && negative); +} + +SignExtendedNumber& SignExtendedNumber::operator++() +{ + if (value != UINT64_MAX) + ++ value; + else if (negative) + { + value = 0; + negative = false; + } + return *this; +} + +SignExtendedNumber SignExtendedNumber::operator<<(const SignExtendedNumber& a) const +{ + // assume left-shift the shift-amount is always unsigned. Thus negative + // shifts will give huge result. + if (value == 0) + return *this; + else if (a.negative) + return extreme(negative); + + uinteger_t v = copySign(value, negative); + + // compute base-2 log of 'v' to determine the maximum allowed bits to shift. + // Ref: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog + + size_t r, s; + + r = (v > 0xFFFFFFFFULL) << 5; v >>= r; + s = (v > 0xFFFFULL ) << 4; v >>= s; r |= s; + s = (v > 0xFFULL ) << 3; v >>= s; r |= s; + s = (v > 0xFULL ) << 2; v >>= s; r |= s; + s = (v > 0x3ULL ) << 1; v >>= s; r |= s; + r |= (v >> 1); + + uinteger_t allowableShift = 63 - r; + if (a.value > allowableShift) + return extreme(negative); + else + return SignExtendedNumber(value << a.value, negative); +} + +SignExtendedNumber SignExtendedNumber::operator>>(const SignExtendedNumber& a) const +{ + if (a.negative || a.value > 64) + return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0); + else if (isMinimum()) + return a.value == 0 ? *this : SignExtendedNumber(-1ULL << (64-a.value), true); + + uinteger_t x = value ^ -negative; + x >>= a.value; + return SignExtendedNumber(x ^ -negative, negative); +} + + +//==================== IntRange ================================================ + +IntRange IntRange::widest() +{ + return IntRange(SignExtendedNumber::min(), SignExtendedNumber::max()); +} + +#if !PERFORM_UNITTEST +IntRange IntRange::fromType(Type *type) +{ + return fromType(type, type->isunsigned()); +} + +IntRange IntRange::fromType(Type *type, bool isUnsigned) +{ + if (!type->isintegral()) + return widest(); + + uinteger_t mask = type->sizemask(); + SignExtendedNumber lower(0), upper(mask); + if (type->toBasetype()->ty == Tdchar) + upper.value = 0x10FFFFULL; + else if (!isUnsigned) + { + lower.value = ~(mask >> 1); + lower.negative = true; + upper.value = (mask >> 1); + } + return IntRange(lower, upper); +} +#endif + +IntRange IntRange::fromNumbers2(const SignExtendedNumber numbers[2]) +{ + if (numbers[0] < numbers[1]) + return IntRange(numbers[0], numbers[1]); + else + return IntRange(numbers[1], numbers[0]); +} +IntRange IntRange::fromNumbers4(const SignExtendedNumber numbers[4]) +{ + IntRange ab = fromNumbers2(numbers); + IntRange cd = fromNumbers2(numbers + 2); + if (cd.imin < ab.imin) + ab.imin = cd.imin; + if (cd.imax > ab.imax) + ab.imax = cd.imax; + return ab; +} + +bool IntRange::contains(const IntRange& a) const +{ + return imin <= a.imin && imax >= a.imax; +} + +bool IntRange::containsZero() const +{ + return (imin.negative && !imax.negative) + || (!imin.negative && imin.value == 0); +} + +IntRange& IntRange::castUnsigned(uinteger_t mask) +{ + // .... 0x1eff ] [0x1f00 .. 0x1fff] [0 .. 0xff] [0x100 .. 0x1ff] [0x200 .... + // + // regular unsigned type. We just need to see if ir steps across the + // boundary of validRange. If yes, ir will represent the whole validRange, + // otherwise, we just take the modulus. + // e.g. [0x105, 0x107] & 0xff == [5, 7] + // [0x105, 0x207] & 0xff == [0, 0xff] + uinteger_t minChunk = imin.value & ~mask; + uinteger_t maxChunk = imax.value & ~mask; + if (minChunk == maxChunk && imin.negative == imax.negative) + { + imin.value &= mask; + imax.value &= mask; + } + else + { + imin.value = 0; + imax.value = mask; + } + imin.negative = imax.negative = false; + return *this; +} + +IntRange& IntRange::castSigned(uinteger_t mask) +{ + // .... 0x1e7f ] [0x1e80 .. 0x1f7f] [0x1f80 .. 0x7f] [0x80 .. 0x17f] [0x180 .... + // + // regular signed type. We use a technique similar to the unsigned version, + // but the chunk has to be offset by 1/2 of the range. + uinteger_t halfChunkMask = mask >> 1; + uinteger_t minHalfChunk = imin.value & ~halfChunkMask; + uinteger_t maxHalfChunk = imax.value & ~halfChunkMask; + int minHalfChunkNegativity = imin.negative; // 1 = neg, 0 = nonneg, -1 = chunk containing ::max + int maxHalfChunkNegativity = imax.negative; + if (minHalfChunk & mask) + { + minHalfChunk += halfChunkMask+1; + if (minHalfChunk == 0) + -- minHalfChunkNegativity; + } + if (maxHalfChunk & mask) + { + maxHalfChunk += halfChunkMask+1; + if (maxHalfChunk == 0) + -- maxHalfChunkNegativity; + } + if (minHalfChunk == maxHalfChunk && minHalfChunkNegativity == maxHalfChunkNegativity) + { + imin.value &= mask; + imax.value &= mask; + // sign extend if necessary. + imin.negative = imin.value & ~halfChunkMask; + imax.negative = imax.value & ~halfChunkMask; + halfChunkMask += 1; + imin.value = (imin.value ^ halfChunkMask) - halfChunkMask; + imax.value = (imax.value ^ halfChunkMask) - halfChunkMask; + } + else + { + imin = SignExtendedNumber(~halfChunkMask, true); + imax = SignExtendedNumber(halfChunkMask, false); + } + return *this; +} + +IntRange& IntRange::castDchar() +{ + // special case for dchar. Casting to dchar means "I'll ignore all + // invalid characters." + castUnsigned(0xFFFFFFFFULL); + if (imin.value > 0x10FFFFULL) // ?? + imin.value = 0x10FFFFULL; // ?? + if (imax.value > 0x10FFFFULL) + imax.value = 0x10FFFFULL; + return *this; +} + +#if !PERFORM_UNITTEST +IntRange& IntRange::cast(Type *type) +{ + if (!type->isintegral()) + return *this; + else if (!type->isunsigned()) + return castSigned(type->sizemask()); + else if (type->toBasetype()->ty == Tdchar) + return castDchar(); + else + return castUnsigned(type->sizemask()); +} + +IntRange& IntRange::castUnsigned(Type *type) +{ + if (!type->isintegral()) + return castUnsigned(UINT64_MAX); + else if (type->toBasetype()->ty == Tdchar) + return castDchar(); + else + return castUnsigned(type->sizemask()); +} +#endif + +IntRange IntRange::absNeg() const +{ + if (imax.negative) + return *this; + else if (!imin.negative) + return IntRange(-imax, -imin); + else + { + SignExtendedNumber imaxAbsNeg = -imax; + return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, + SignExtendedNumber(0)); + } +} + +IntRange IntRange::unionWith(const IntRange& other) const +{ + return IntRange(imin < other.imin ? imin : other.imin, + imax > other.imax ? imax : other.imax); +} + +void IntRange::unionOrAssign(const IntRange& other, bool& union_) +{ + if (!union_ || imin > other.imin) + imin = other.imin; + if (!union_ || imax < other.imax) + imax = other.imax; + union_ = true; +} + +void IntRange::splitBySign(IntRange& negRange, bool& hasNegRange, + IntRange& nonNegRange, bool& hasNonNegRange) const +{ + hasNegRange = imin.negative; + if (hasNegRange) + { + negRange.imin = imin; + negRange.imax = imax.negative ? imax : SignExtendedNumber(-1, true); + } + hasNonNegRange = !imax.negative; + if (hasNonNegRange) + { + nonNegRange.imin = imin.negative ? SignExtendedNumber(0) : imin; + nonNegRange.imax = imax; + } +} + + +#if !PERFORM_UNITTEST +const IntRange& IntRange::dump(const char* funcName, Expression *e) const +{ + printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n", + imin.negative?'-':'+', imin.value, + imax.negative?'-':'+', imax.value, + funcName, e->toChars()); + return *this; +} +#endif + +//------------------------------------------------------------------------------ + +#if PERFORM_UNITTEST +#include +#include + +class AssertionError : public std::exception { +public: + AssertionError() : std::exception() {} +}; + +void _assertPred(uinteger_t x, uinteger_t y, int line) { + if (x != y) { + printf("Line %d: %#018llx != %#018llx\n", line, x, y); + throw AssertionError(); + } +} +void _assertPred(const SignExtendedNumber& x, const SignExtendedNumber& y, int line) { + if (x != y) { + printf("Line %d: (%c)%#018llx != (%c)%#018llx\n", line, + x.negative?'-':'+', x.value, + y.negative?'-':'+', y.value); + throw AssertionError(); + } +} +void _assertPred(bool x, bool y, int line) { + if (x != y) { + static const char* const names[] = {"false", "true"}; + printf("Line %d: %s != %s\n", line, names[x], names[y]); + throw AssertionError(); + } +} +#define assertPred(x, y) _assertPred(x, y, __LINE__) +#define RUN(testName) \ + try { \ + testName(); \ + } catch (const AssertionError&) { \ + printf("********" #testName " failed\n"); \ + } + +void testAssertSanity() { + int saneCount = 0; + + printf("Testing 'assert' sanity. You should see 3 assertion failures below\n"); + + assertPred(true, true); + try { + assertPred(true, false); + } catch (const AssertionError&) { + ++ saneCount; + } + + assertPred(4ULL, 4ULL); + try { + assertPred(3ULL, -3ULL); + } catch (const AssertionError&) { + ++ saneCount; + } + + assertPred(SignExtendedNumber(5, false), SignExtendedNumber(5, false)); + try { + assertPred(SignExtendedNumber(4, false), SignExtendedNumber(4, true)); + } catch (const AssertionError&) { + ++ saneCount; + } + + printf("--------------\n"); + + if (saneCount != 3) throw AssertionError(); +} + +void testNegation() { + SignExtendedNumber s (4); + SignExtendedNumber t = -s; + assertPred(t.value, -4ULL); + assertPred(t.negative, true); + + s = SignExtendedNumber::max(); + t = -s; + assertPred(t.value, 1); + assertPred(t.negative, true); + + s = SignExtendedNumber::fromInteger(-4); + assertPred(s.value, -4ULL); + assertPred(s.negative, true); + + t = -s; + assertPred(t.value, 4); + assertPred(t.negative, false); + + s = SignExtendedNumber::min(); + t = -s; + assertPred(t.value, UINT64_MAX); + assertPred(t.negative, false); + + s = SignExtendedNumber(0); + t = -s; + assertPred(t.value, 0); + assertPred(t.negative, false); +} + +void testCompare() { + SignExtendedNumber a = SignExtendedNumber::min(); + SignExtendedNumber b = SignExtendedNumber(-5, true); + SignExtendedNumber c = SignExtendedNumber(0, false); + SignExtendedNumber d = SignExtendedNumber(5, false); + SignExtendedNumber e = SignExtendedNumber::max(); + + assertPred(a == a, true); + assertPred(a != a, false); + assertPred(a < b, true); + assertPred(b < c, true); + assertPred(c < d, true); + assertPred(d < e, true); + assertPred(a < c, true); + assertPred(c < e, true); + assertPred(b < d, true); + assertPred(b < a, false); + assertPred(c < b, false); + assertPred(d < c, false); + assertPred(e < d, false); + assertPred(e < c, false); + assertPred(d < b, false); + assertPred(c < a, false); + + assertPred(a, a); + assertPred(SignExtendedNumber::extreme(false), SignExtendedNumber::max()); + assertPred(SignExtendedNumber::extreme(true), SignExtendedNumber::min()); +} + +void testAddition() { + assertPred(SignExtendedNumber(4, false) + SignExtendedNumber(8, false), + SignExtendedNumber(12, false)); + assertPred(SignExtendedNumber(4, false) + SignExtendedNumber(-9, true), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-9, true) + SignExtendedNumber(4, false), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-4, true) + SignExtendedNumber(9, false), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(9, false) + SignExtendedNumber(-4, true), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(9, true) + SignExtendedNumber(-4, false), + SignExtendedNumber(5, false)); + assertPred(SignExtendedNumber(-4, true) + SignExtendedNumber(-6, true), + SignExtendedNumber(-10, true)); + assertPred(SignExtendedNumber::max() + SignExtendedNumber(1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(UINT64_MAX/2+1, false) + SignExtendedNumber(UINT64_MAX/2+1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() + SignExtendedNumber::min(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() + SignExtendedNumber(-1, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() + SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() + SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(1, true) + SignExtendedNumber(1, true), + SignExtendedNumber::min()); + + SignExtendedNumber x(0); + assertPred(++x, SignExtendedNumber(1)); + x = SignExtendedNumber(-1, true); + assertPred(++x, SignExtendedNumber(0)); + x = SignExtendedNumber::min(); + assertPred(++x, SignExtendedNumber(1, true)); + x = SignExtendedNumber::max(); + assertPred(++x, SignExtendedNumber::max()); +} + +void testSubtraction() { + assertPred(SignExtendedNumber(4, false) - SignExtendedNumber(8, false), + SignExtendedNumber(-4, true)); + assertPred(SignExtendedNumber(4, false) - SignExtendedNumber(-9, true), + SignExtendedNumber(13, false)); + assertPred(SignExtendedNumber(-9, true) - SignExtendedNumber(4, false), + SignExtendedNumber(-13, true)); + assertPred(SignExtendedNumber(-4, true) - SignExtendedNumber(9, false), + SignExtendedNumber(-13, true)); + assertPred(SignExtendedNumber(9, false) - SignExtendedNumber(-4, true), + SignExtendedNumber(13, false)); + assertPred(SignExtendedNumber(9, true) - SignExtendedNumber(-4, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-4, true) - SignExtendedNumber(-6, true), + SignExtendedNumber(2, false)); + assertPred(SignExtendedNumber::max() - SignExtendedNumber(-1, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() - SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() - SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() - SignExtendedNumber(1, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(1, false) - SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() - SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(1, true) - SignExtendedNumber::min(), + SignExtendedNumber(1, false)); +} + +void testMultiplication() { + assertPred(SignExtendedNumber(4, false) * SignExtendedNumber(8, false), + SignExtendedNumber(32, false)); + assertPred(SignExtendedNumber(4, false) * SignExtendedNumber(-9, true), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(-9, true) * SignExtendedNumber(4, false), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(-4, true) * SignExtendedNumber(9, false), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(9, false) * SignExtendedNumber(-4, true), + SignExtendedNumber(-36, true)); + assertPred(SignExtendedNumber(9, true) * SignExtendedNumber(-4, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-4, true) * SignExtendedNumber(-6, true), + SignExtendedNumber(24, false)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(0) * SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) * SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() * SignExtendedNumber::max(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() * SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-6, false) * SignExtendedNumber(2, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-6, false) * SignExtendedNumber(-2, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(-1, true), + SignExtendedNumber(1, true)); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(-2, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::max() * SignExtendedNumber(2, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(2, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() * SignExtendedNumber(-1, true), + SignExtendedNumber::max()); +} + +void testDivision() { + assertPred(SignExtendedNumber(4, false) / SignExtendedNumber(8, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(8, false) / SignExtendedNumber(4, false), + SignExtendedNumber(2, false)); + assertPred(SignExtendedNumber(4, false) / SignExtendedNumber(-9, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-9, true) / SignExtendedNumber(4, false), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(-4, true) / SignExtendedNumber(9, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(9, false) / SignExtendedNumber(-4, true), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(4, true) / SignExtendedNumber(-9, false), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-6, true) / SignExtendedNumber(-4, true), + SignExtendedNumber(1, false)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber::max(), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(0), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() / SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) / SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) / SignExtendedNumber(0), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(0) / SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(0), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber::min(), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber(-6, false) / SignExtendedNumber(2, false), + SignExtendedNumber((~5ULL)>>1)); + assertPred(SignExtendedNumber(-6, false) / SignExtendedNumber(-2, true), + SignExtendedNumber(3 | 1ULL<<63, true)); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(-1, true), + SignExtendedNumber(1, true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(-1, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::max() / SignExtendedNumber(1, false), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(1, false), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(2, false), + SignExtendedNumber(-(1ULL << 63), true)); + assertPred(SignExtendedNumber::min() / SignExtendedNumber(-1024, true), + SignExtendedNumber(1ULL << 54)); +} + +void testModulus() { + assertPred(SignExtendedNumber(4, false) % SignExtendedNumber(8, false), + SignExtendedNumber(4, false)); + assertPred(SignExtendedNumber(8, false) % SignExtendedNumber(4, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(4, false) % SignExtendedNumber(-9, true), + SignExtendedNumber(4, false)); + assertPred(SignExtendedNumber(-9, true) % SignExtendedNumber(4, false), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-4, true) % SignExtendedNumber(9, false), + SignExtendedNumber(-4, true)); + assertPred(SignExtendedNumber(9, false) % SignExtendedNumber(-4, true), + SignExtendedNumber(1, false)); + assertPred(SignExtendedNumber(4, true) % SignExtendedNumber(-9, false), + SignExtendedNumber(-5, true)); + assertPred(SignExtendedNumber(-6, true) % SignExtendedNumber(-4, true), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(0) % SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) % SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(0), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-6, false) % SignExtendedNumber(2, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-6, false) % SignExtendedNumber(-2, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(-1, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(-1, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::max() % SignExtendedNumber(1, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(1, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(2, false), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber::min() % SignExtendedNumber(999, false), + SignExtendedNumber(-160, true)); +} + +void testShift() { + assertPred(SignExtendedNumber(0) << SignExtendedNumber(4), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) << SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(4), + SignExtendedNumber(16)); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(74), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber(-5, true), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber::max(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(1) << SignExtendedNumber::min(), + SignExtendedNumber::max()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(4), + SignExtendedNumber(-16, true)); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(74), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber(-5, true), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber::max(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(-1, true) << SignExtendedNumber::min(), + SignExtendedNumber::min()); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(12, false), + SignExtendedNumber(0xabcdef000ULL)); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(40, false), + SignExtendedNumber(0xabcdef0000000000ULL)); + assertPred(SignExtendedNumber(0xabcdef) << SignExtendedNumber(41, false), + SignExtendedNumber::max()); + + + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(4), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(0) >> SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(4), + SignExtendedNumber(1)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(74), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber(-5, true), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber::max(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(16) >> SignExtendedNumber::min(), + SignExtendedNumber(0)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(4), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(74), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber(-5, true), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber::max(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(-32, true) >> SignExtendedNumber::min(), + SignExtendedNumber(-1, true)); + assertPred(SignExtendedNumber(0xabcdef, false) >> SignExtendedNumber(12, false), + SignExtendedNumber(0xabcULL)); + assertPred(SignExtendedNumber(0xabcdef, true) >> SignExtendedNumber(12, false), + SignExtendedNumber(0xFFF0000000000ABCULL, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(1, false), + SignExtendedNumber(0x8000000000000000ULL, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(63, false), + SignExtendedNumber(-2, true)); + assertPred(SignExtendedNumber::min() >> SignExtendedNumber(65, false), + SignExtendedNumber(-1, true)); +} + +void testFromNumbers() { + SignExtendedNumber a[] = { + SignExtendedNumber(12, false), + SignExtendedNumber(-35, true), + SignExtendedNumber(40, false), + SignExtendedNumber(-21, true), + SignExtendedNumber::min() + }; + + IntRange ir1 = IntRange::fromNumbers2(a); + assertPred(ir1.imin, SignExtendedNumber(-35, true)); + assertPred(ir1.imax, SignExtendedNumber(12, false)); + + IntRange ir2 = IntRange::fromNumbers2(a+1); + assertPred(ir2.imin, SignExtendedNumber(-35, true)); + assertPred(ir2.imax, SignExtendedNumber(40, false)); + + IntRange ir3 = IntRange::fromNumbers4(a); + assertPred(ir3.imin, SignExtendedNumber(-35, true)); + assertPred(ir3.imax, SignExtendedNumber(40, false)); + + IntRange ir4 = IntRange::fromNumbers4(a+1); + assertPred(ir4.imin, SignExtendedNumber::min()); + assertPred(ir4.imax, SignExtendedNumber(40, false)); + + assertPred(ir4.contains(ir3), true); + assertPred(ir1.contains(ir2), false); + + IntRange ir5 = IntRange::widest(); + assertPred(ir5.imin, SignExtendedNumber::min()); + assertPred(ir5.imax, SignExtendedNumber::max()); + assertPred(ir5.contains(ir4), true); +} + +void testContainsZero() { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(4)); + assertPred(ir1.containsZero(), true); + + IntRange ir2 (SignExtendedNumber(-4, true), SignExtendedNumber(0)); + assertPred(ir2.containsZero(), true); + + IntRange ir3 (SignExtendedNumber(-5, true), SignExtendedNumber(5)); + assertPred(ir3.containsZero(), true); + + assertPred(IntRange::widest().containsZero(), true); + + IntRange ir4 (SignExtendedNumber(8), SignExtendedNumber(9)); + assertPred(ir4.containsZero(), false); + + IntRange ir5 (SignExtendedNumber(-5, true), SignExtendedNumber(-2, true)); + assertPred(ir5.containsZero(), false); + + IntRange ir6 (SignExtendedNumber(0), SignExtendedNumber(0)); + assertPred(ir6.containsZero(), true); +} + +void testCast() { + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0xFFFF)); + ir1.castUnsigned(0xFF); + assertPred(ir1.imin, SignExtendedNumber(0)); + assertPred(ir1.imax, SignExtendedNumber(0xFF)); + + IntRange ir2 (SignExtendedNumber(0x101), SignExtendedNumber(0x105)); + ir2.castUnsigned(0xFF); + assertPred(ir2.imin, SignExtendedNumber(1)); + assertPred(ir2.imax, SignExtendedNumber(5)); + + IntRange ir3 (SignExtendedNumber(-7, true), SignExtendedNumber(7, false)); + ir3.castUnsigned(0xFF); + assertPred(ir3.imin, SignExtendedNumber(0)); + assertPred(ir3.imax, SignExtendedNumber(0xFF)); + + IntRange ir4 (SignExtendedNumber(0x997F), SignExtendedNumber(0x9999)); + ir4.castUnsigned(0xFF); + assertPred(ir4.imin, SignExtendedNumber(0x7F)); + assertPred(ir4.imax, SignExtendedNumber(0x99)); + + IntRange ir5 (SignExtendedNumber(-1, true), SignExtendedNumber(1, false)); + ir5.castUnsigned(UINT64_MAX); + assertPred(ir5.imin, SignExtendedNumber(0)); + assertPred(ir5.imax, SignExtendedNumber::max()); + + IntRange ir6 (SignExtendedNumber::min(), SignExtendedNumber(0)); + ir6.castUnsigned(UINT64_MAX); + assertPred(ir6.imin, SignExtendedNumber(0)); + assertPred(ir6.imax, SignExtendedNumber::max()); + + IntRange ir7 (SignExtendedNumber::min(), SignExtendedNumber(-0x80, true)); + ir7.castUnsigned(UINT64_MAX); + assertPred(ir7.imin, SignExtendedNumber(0)); + assertPred(ir7.imax, SignExtendedNumber(-0x80, false)); + + IntRange ir8 = IntRange::widest(); + ir8.castUnsigned(0xFF); + assertPred(ir8.imin, SignExtendedNumber(0)); + assertPred(ir8.imax, SignExtendedNumber(0xFF)); + } + + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0xFFFF)); + ir1.castSigned(0xFF); + assertPred(ir1.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir1.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir2 (SignExtendedNumber(0x101), SignExtendedNumber(0x105)); + ir2.castSigned(0xFF); + assertPred(ir2.imin, SignExtendedNumber(1)); + assertPred(ir2.imax, SignExtendedNumber(5)); + + IntRange ir3 (SignExtendedNumber(-7, true), SignExtendedNumber(7, false)); + ir3.castSigned(0xFF); + assertPred(ir3.imin, SignExtendedNumber(-7, true)); + assertPred(ir3.imax, SignExtendedNumber(7, false)); + + IntRange ir4 (SignExtendedNumber(0x997F), SignExtendedNumber(0x9999)); + ir4.castSigned(0xFF); + assertPred(ir4.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir4.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir5 (SignExtendedNumber(-0xFF, true), SignExtendedNumber(-0x80, true)); + ir5.castSigned(0xFF); + assertPred(ir5.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir5.imax, SignExtendedNumber(0x7F, false)); + + IntRange ir6 (SignExtendedNumber(-0x80, true), SignExtendedNumber(-0x80, true)); + ir6.castSigned(0xFF); + assertPred(ir6.imin, SignExtendedNumber(-0x80, true)); + assertPred(ir6.imax, SignExtendedNumber(-0x80, true)); + + IntRange ir7 = IntRange::widest(); + ir7.castSigned(0xFFFFFFFFULL); + assertPred(ir7.imin, SignExtendedNumber(-0x80000000ULL, true)); + assertPred(ir7.imax, SignExtendedNumber( 0x7FFFFFFFULL, false)); + } + + { + IntRange ir1 (SignExtendedNumber(0), SignExtendedNumber(0x9999)); + ir1.castDchar(); + assertPred(ir1.imin, SignExtendedNumber(0)); + assertPred(ir1.imax, SignExtendedNumber(0x9999)); + + IntRange ir2 (SignExtendedNumber(0xFFFF), SignExtendedNumber(0x7FFFFFFF)); + ir2.castDchar(); + assertPred(ir2.imin, SignExtendedNumber(0xFFFF)); + assertPred(ir2.imax, SignExtendedNumber(0x10FFFF)); + + IntRange ir3 = IntRange::widest(); + ir3.castDchar(); + assertPred(ir3.imin, SignExtendedNumber(0)); + assertPred(ir3.imax, SignExtendedNumber(0x10FFFF)); + } +} + +void testAbsNeg() { + IntRange ir1 = IntRange(SignExtendedNumber(5), SignExtendedNumber(104)).absNeg(); + assertPred(ir1.imin, SignExtendedNumber(-104, true)); + assertPred(ir1.imax, SignExtendedNumber(-5, true)); + + IntRange ir2 = IntRange(SignExtendedNumber(-46, true), SignExtendedNumber(-3, true)).absNeg(); + assertPred(ir2.imin, SignExtendedNumber(-46, true)); + assertPred(ir2.imax, SignExtendedNumber(-3, true)); + + IntRange ir3 = IntRange(SignExtendedNumber(-7, true), SignExtendedNumber(9)).absNeg(); + assertPred(ir3.imin, SignExtendedNumber(-9, true)); + assertPred(ir3.imax, SignExtendedNumber(0)); + + IntRange ir4 = IntRange(SignExtendedNumber(-12, true), SignExtendedNumber(2)).absNeg(); + assertPred(ir4.imin, SignExtendedNumber(-12, true)); + assertPred(ir4.imax, SignExtendedNumber(0)); + + IntRange ir5 = IntRange::widest().absNeg(); + assertPred(ir5.imin, SignExtendedNumber::min()); + assertPred(ir5.imax, SignExtendedNumber(0)); + + IntRange ir6 = IntRange(SignExtendedNumber(0), SignExtendedNumber::max()).absNeg(); + assertPred(ir6.imin, SignExtendedNumber(1, true)); + assertPred(ir6.imax, SignExtendedNumber(0)); +} + +int main() { + RUN(testAssertSanity); + RUN(testNegation); + RUN(testCompare); + RUN(testAddition); + RUN(testSubtraction); + RUN(testMultiplication); + RUN(testDivision); + RUN(testModulus); + RUN(testShift); + RUN(testFromNumbers); + RUN(testContainsZero); + RUN(testCast); + RUN(testAbsNeg); + printf("Finished all tests.\n"); +} + + +#endif + + diff --git a/dmd2/intrange.h b/dmd2/intrange.h new file mode 100644 index 00000000..2904dab9 --- /dev/null +++ b/dmd2/intrange.h @@ -0,0 +1,149 @@ + +// Compiler implementation of the D programming language +// Copyright (c) 1999-2011 by Digital Mars +// All Rights Reserved +// written by KennyTM +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + + +#ifndef DMD_SXNUM_H +#define DMD_SXNUM_H + +#include "mars.h" // for uinteger_t +struct Type; +struct Expression; + +/** +This class represents a "sign-extended number", i.e. a 65-bit number, which can +represent all built-in integer types in D. This class is mainly used for +performing value-range propagation only, therefore all arithmetic are done with +saturation, not wrapping as usual. +*/ +struct SignExtendedNumber +{ + /// The lower 64-bit of the number. + uinteger_t value; + /// The sign (i.e. the most significant bit) of the number. + bool negative; + + /// Create an uninitialized sign-extended number. + SignExtendedNumber() {} + + /// Create a sign-extended number from an unsigned 64-bit number. + SignExtendedNumber(uinteger_t value_) + : value(value_), negative(false) {} + /// Create a sign-extended number from the lower 64-bit and the sign bit. + SignExtendedNumber(uinteger_t value_, bool negative_) + : value(value_), negative(negative_) {} + + /// Create a sign-extended number from a signed 64-bit number. + static SignExtendedNumber fromInteger(uinteger_t value_); + + /// Get the minimum or maximum value of a sign-extended number. + static SignExtendedNumber extreme(bool minimum); + static SignExtendedNumber max(); + static SignExtendedNumber min() { return SignExtendedNumber(0, true); } + + /// Check if the sign-extended number is minimum or zero. + bool isMinimum() const { return negative && value == 0; } + + /// Compare two sign-extended number. + bool operator==(const SignExtendedNumber&) const; + bool operator!=(const SignExtendedNumber& a) const { return !(*this == a); } + bool operator<(const SignExtendedNumber&) const; + bool operator>(const SignExtendedNumber& a) const { return a < *this; } + bool operator<=(const SignExtendedNumber& a) const { return !(a < *this); } + bool operator>=(const SignExtendedNumber& a) const { return !(*this < a); } + + /// Compute the saturated negation of a sign-extended number. + SignExtendedNumber operator-() const; + + /// Compute the saturated sum of two sign-extended number. + SignExtendedNumber operator+(const SignExtendedNumber&) const; + /// Compute the saturated difference of two sign-extended number. + SignExtendedNumber operator-(const SignExtendedNumber& a) const; + /// Compute the saturated product of two sign-extended number. + SignExtendedNumber operator*(const SignExtendedNumber&) const; + /// Compute the saturated quotient of two sign-extended number. + SignExtendedNumber operator/(const SignExtendedNumber&) const; + /// Compute the saturated modulus of two sign-extended number. + SignExtendedNumber operator%(const SignExtendedNumber&) const; + + /// Increase the sign-extended number by 1 (saturated). + SignExtendedNumber& operator++(); + + /// Compute the saturated shifts of two sign-extended number. + SignExtendedNumber operator<<(const SignExtendedNumber&) const; + SignExtendedNumber operator>>(const SignExtendedNumber&) const; +}; + +/** +This class represents a range of integers, denoted by its lower and upper bounds +(inclusive). +*/ +struct IntRange +{ + SignExtendedNumber imin, imax; + + /// Create an uninitialized range. + IntRange() {} + + /// Create a range consisting of a single number. + IntRange(const SignExtendedNumber& a) + : imin(a), imax(a) {} + /// Create a range with the lower and upper bounds. + IntRange(const SignExtendedNumber& lower, const SignExtendedNumber& upper) + : imin(lower), imax(upper) {} + + /// Create the tightest range containing all valid integers in the specified + /// type. + static IntRange fromType(Type *type); + /// Create the tightest range containing all valid integers in the type with + /// a forced signedness. + static IntRange fromType(Type *type, bool isUnsigned); + + + /// Create the tightest range containing all specified numbers. + static IntRange fromNumbers2(const SignExtendedNumber numbers[2]); + static IntRange fromNumbers4(const SignExtendedNumber numbers[4]); + + /// Create the widest range possible. + static IntRange widest(); + + /// Cast the integer range to a signed type with the given size mask. + IntRange& castSigned(uinteger_t mask); + /// Cast the integer range to an unsigned type with the given size mask. + IntRange& castUnsigned(uinteger_t mask); + /// Cast the integer range to the dchar type. + IntRange& castDchar(); + + /// Cast the integer range to a specific type. + IntRange& cast(Type *type); + /// Cast the integer range to a specific type, forcing it to be unsigned. + IntRange& castUnsigned(Type *type); + + /// Check if this range contains another range. + bool contains(const IntRange& a) const; + + /// Check if this range contains 0. + bool containsZero() const; + + /// Compute the range of the negated absolute values of the original range. + IntRange absNeg() const; + + /// Compute the union of two ranges. + IntRange unionWith(const IntRange& other) const; + void unionOrAssign(const IntRange& other, bool& union_); + + /// Dump the content of the integer range to the console. + const IntRange& dump(const char* funcName, Expression *e) const; + + /// Split the range into two nonnegative- and negative-only subintervals. + void splitBySign(IntRange& negRange, bool& hasNegRange, + IntRange& nonNegRange, bool& hasNonNegRange) const; +}; + +#endif diff --git a/dmd2/lexer.h b/dmd2/lexer.h index d5da99e0..abe8df6d 100644 --- a/dmd2/lexer.h +++ b/dmd2/lexer.h @@ -211,7 +211,6 @@ enum TOK case TOKcomplex32: t = Type::tcomplex32; goto LabelX; \ case TOKcomplex64: t = Type::tcomplex64; goto LabelX; \ case TOKcomplex80: t = Type::tcomplex80; goto LabelX; \ - case TOKbit: t = Type::tbit; goto LabelX; \ case TOKbool: t = Type::tbool; goto LabelX; \ case TOKchar: t = Type::tchar; goto LabelX; \ case TOKwchar: t = Type::twchar; goto LabelX; \ diff --git a/dmd2/mangle.c b/dmd2/mangle.c index a6d8d34d..0c19040c 100644 --- a/dmd2/mangle.c +++ b/dmd2/mangle.c @@ -24,7 +24,7 @@ #include "id.h" #include "module.h" -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS char *cpp_mangle(Dsymbol *s); #endif diff --git a/dmd2/mars.c b/dmd2/mars.c index b8471310..8673fbe3 100644 --- a/dmd2/mars.c +++ b/dmd2/mars.c @@ -77,7 +77,7 @@ Global::Global() #else #if TARGET_WINDOS obj_ext = "obj"; -#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS obj_ext = "o"; #elif TARGET_NET #else @@ -86,7 +86,7 @@ Global::Global() #if TARGET_WINDOS lib_ext = "lib"; -#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS lib_ext = "a"; #elif TARGET_NET #else @@ -100,7 +100,7 @@ Global::Global() "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; #endif ; - version = "v2.053"; + version = "v2.054"; #if IN_LLVM ldc_version = "LDC trunk"; llvm_version = "LLVM 2.9"; @@ -182,7 +182,7 @@ void verror(Loc loc, const char *format, va_list ap) #endif fprintf(stdmsg, "\n"); fflush(stdmsg); -halt(); +//halt(); } global.errors++; } @@ -238,8 +238,8 @@ void halt() #endif } - extern signed char tyalignsize[]; + /*********************************** * Parse and append contents of environment variable envvar * to argc and argv[]. diff --git a/dmd2/mars.h b/dmd2/mars.h index b9329f5a..8efc2664 100644 --- a/dmd2/mars.h +++ b/dmd2/mars.h @@ -37,6 +37,7 @@ Macros defined by the compiler, not the code: linux Linux __APPLE__ Mac OSX __FreeBSD__ FreeBSD + __OpenBSD__ OpenBSD __sun&&__SVR4 Solaris, OpenSolaris (yes, both macros are necessary) For the target systems, there are the target operating system and @@ -47,6 +48,7 @@ the target object file format: TARGET_LINUX Covers 32 and 64 bit linux TARGET_OSX Covers 32 and 64 bit Mac OSX TARGET_FREEBSD Covers 32 and 64 bit FreeBSD + TARGET_OPENBSD Covers 32 and 64 bit OpenBSD TARGET_SOLARIS Covers 32 and 64 bit Solaris TARGET_NET Covers .Net @@ -55,7 +57,7 @@ the target object file format: Target object module format: OMFOBJ Intel Object Module Format, used on Windows - ELFOBJ Elf Object Module Format, used on linux, FreeBSD and Solaris + ELFOBJ Elf Object Module Format, used on linux, FreeBSD, OpenBSD and Solaris MACHOBJ Mach-O Object Module Format, used on Mac OSX There are currently no macros for byte endianness order. @@ -103,9 +105,9 @@ void unittests(); #define MODULEINFO_IS_STRUCT DMDV2 // if ModuleInfo is a struct rather than a class // Set if C++ mangling is done by the front end -#define CPP_MANGLE (DMDV2 && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS)) +#define CPP_MANGLE (DMDV2 && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)) -/* Other targets are TARGET_LINUX, TARGET_OSX, TARGET_FREEBSD and +/* Other targets are TARGET_LINUX, TARGET_OSX, TARGET_FREEBSD, TARGET_OPENBSD and * TARGET_SOLARIS, which are * set on the command line via the compiler makefile. */ @@ -115,7 +117,7 @@ void unittests(); #define OMFOBJ 1 #endif -#if TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS +#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS #ifndef ELFOBJ #define ELFOBJ 1 #endif @@ -192,6 +194,7 @@ struct Param bool useInline; // inline expand functions bool warnings; // enable warnings ubyte Dversion; // D version number + char enforcePropertySyntax; char *argv0; // program name Array *imppath; // array of char*'s of where to look for import modules diff --git a/dmd2/module.c b/dmd2/module.c index 0973893f..244dfad3 100644 --- a/dmd2/module.c +++ b/dmd2/module.c @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -544,7 +544,7 @@ void Module::read(Loc loc) inline unsigned readwordLE(unsigned short *p) { -#if __I86__ +#if LITTLE_ENDIAN return *p; #else return (((unsigned char *)p)[1] << 8) | ((unsigned char *)p)[0]; @@ -558,7 +558,7 @@ inline unsigned readwordBE(unsigned short *p) inline unsigned readlongLE(unsigned *p) { -#if __I86__ +#if LITTLE_ENDIAN return *p; #else return ((unsigned char *)p)[0] | diff --git a/dmd2/module.h b/dmd2/module.h index 0183e04f..dc66a981 100644 --- a/dmd2/module.h +++ b/dmd2/module.h @@ -154,9 +154,7 @@ struct Module : Package #if !IN_LLVM void setHdrfile(); // set hdrfile member #endif -#ifdef _DH void genhdrfile(); // generate D import file -#endif // void gensymfile(); void gendocfile(); int needModuleInfo(); diff --git a/dmd2/mtype.c b/dmd2/mtype.c index 01bca3c5..1ad0fb57 100644 --- a/dmd2/mtype.c +++ b/dmd2/mtype.c @@ -52,7 +52,6 @@ #include "import.h" #include "aggregate.h" #include "hdrgen.h" -#include "doc.h" #if IN_LLVM //#include "gen/tollvm.h" @@ -83,7 +82,7 @@ int PTRSIZE = 4; int REALSIZE = 16; int REALPAD = 6; int REALALIGNSIZE = 16; -#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS int REALSIZE = 12; int REALPAD = 2; int REALALIGNSIZE = 4; @@ -264,7 +263,6 @@ void Type::init() mangleChar[Tdchar] = 'w'; // '@' shouldn't appear anywhere in the deco'd names - mangleChar[Tbit] = '@'; mangleChar[Tinstance] = '@'; mangleChar[Terror] = '@'; mangleChar[Ttypeof] = '@'; @@ -313,7 +311,7 @@ void Type::init() #if TARGET_OSX REALSIZE = 16; REALPAD = 6; -#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS +#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS REALSIZE = 12; REALPAD = 2; #else @@ -1244,6 +1242,8 @@ Type *Type::toHeadMutable() Type *Type::pointerTo() { + if (ty == Terror) + return this; if (!pto) { Type *t; @@ -1255,6 +1255,8 @@ Type *Type::pointerTo() Type *Type::referenceTo() { + if (ty == Terror) + return this; if (!rto) { Type *t; @@ -1266,6 +1268,8 @@ Type *Type::referenceTo() Type *Type::arrayOf() { + if (ty == Terror) + return this; if (!arrayof) { Type *t; @@ -1764,6 +1768,8 @@ Expression *Type::getProperty(Loc loc, Identifier *ident) { if (ty == Tvoid) error(loc, "void does not have an initializer"); + if (ty == Tfunction) + error(loc, "function does not have an initializer"); e = defaultInitLiteral(loc); } else if (ident == Id::mangleof) @@ -1928,7 +1934,7 @@ Expression *Type::noMember(Scope *sc, Expression *e, Identifier *ident) { /* Rewrite e.ident as: * e.opDot().ident */ - e = build_overload(e->loc, sc, e, NULL, fd->ident); + e = build_overload(e->loc, sc, e, NULL, fd); e = new DotIdExp(e->loc, e, ident); return e->semantic(sc); } @@ -2562,7 +2568,7 @@ unsigned TypeBasic::alignsize() if (ty == Tvoid) return 1; return GetTypeAlignment(sir, this); -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS case Tint64: case Tuns64: sz = global.params.isX86_64 ? 8 : 4; @@ -3271,7 +3277,6 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) { Expression *ec; Expressions *arguments; - bool isBit = (n->ty == Tbit); //LDC: Build arguments. static FuncDeclaration *adSort_fd = NULL; @@ -3281,18 +3286,8 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL)); adSort_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort"); } - static FuncDeclaration *adSortBit_fd = NULL; - if(!adSortBit_fd) { - Parameters* args = new Parameters; - args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL)); - args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL)); - adSortBit_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSortBit"); - } - if(isBit) - ec = new VarExp(0, adSortBit_fd); - else - ec = new VarExp(0, adSort_fd); + ec = new VarExp(0, adSort_fd); e = e->castTo(sc, n->arrayOf()); // convert to dynamic array arguments = new Expressions(); @@ -3304,13 +3299,9 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) e = exp; } arguments->push(e); - - if (next->ty != Tbit) { - // LDC, we don't support the getInternalTypeInfo - // optimization arbitrarily, not yet at least... - arguments->push(n->getTypeInfo(sc)); - } - + // LDC, we don't support the getInternalTypeInfo + // optimization arbitrarily, not yet at least... + arguments->push(n->getTypeInfo(sc)); e = new CallExp(e->loc, ec, arguments); e->type = next->arrayOf(); } @@ -3517,6 +3508,9 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) dim = dim->optimize(WANTvalue); dinteger_t d2 = dim->toInteger(); + if (dim->op == TOKerror) + goto Lerror; + if (d1 != d2) goto Loverflow; @@ -4142,7 +4136,6 @@ printf("index->ito->ito = x%x\n", index->ito->ito); switch (index->toBasetype()->ty) { - case Tbool: case Tfunction: case Tvoid: case Tnone: @@ -4920,6 +4913,11 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) } void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + toCBufferWithAttributes(buf, ident, hgs, this, NULL); +} + +void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td) { //printf("TypeFunction::toCBuffer() this = %p\n", this); const char *p = NULL; @@ -4932,22 +4930,22 @@ void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs /* Use 'storage class' style for attributes */ - if (mod) + if (attrs->mod) { - MODtoBuffer(buf, mod); + MODtoBuffer(buf, attrs->mod); buf->writeByte(' '); } - if (purity) + if (attrs->purity) buf->writestring("pure "); - if (isnothrow) + if (attrs->isnothrow) buf->writestring("nothrow "); - if (isproperty) + if (attrs->isproperty) buf->writestring("@property "); - if (isref) + if (attrs->isref) buf->writestring("ref "); - switch (trust) + switch (attrs->trust) { case TRUSTsystem: buf->writestring("@system "); @@ -4964,9 +4962,11 @@ void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs if (next && (!ident || ident->toHChars2() == ident->toChars())) next->toCBuffer2(buf, hgs, 0); + else if (hgs->ddoc && !next) + buf->writestring("auto"); if (hgs->ddoc != 1) { - switch (linkage) + switch (attrs->linkage) { case LINKd: p = NULL; break; case LINKc: p = "C "; break; @@ -4988,6 +4988,17 @@ void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs { buf->writeByte(' '); buf->writestring(ident->toHChars2()); } + if (td) + { buf->writeByte('('); + for (int i = 0; i < td->origParameters->dim; i++) + { + TemplateParameter *tp = (TemplateParameter *)td->origParameters->data[i]; + if (i) + buf->writestring(", "); + tp->toCBuffer(buf, hgs); + } + buf->writeByte(')'); + } Parameter::argsToCBuffer(buf, hgs, parameters, varargs); inuse--; } @@ -5210,8 +5221,9 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) size_t tdim = tt->arguments->dim; for (size_t j = 0; j < tdim; j++) { Parameter *narg = (Parameter *)tt->arguments->data[j]; - narg->storageClass = fparam->storageClass; + narg->storageClass |= fparam->storageClass; } + fparam->storageClass = 0; } /* Reset number of parameters, and back up one to do this fparam again, @@ -5548,6 +5560,20 @@ Type *TypeFunction::reliesOnTident() return next ? next->reliesOnTident() : NULL; } +/******************************************** + * Return TRUE if there are lazy parameters. + */ +bool TypeFunction::hasLazyParameters() +{ + size_t dim = Parameter::dim(parameters); + for (size_t i = 0; i < dim; i++) + { Parameter *fparam = Parameter::getNth(parameters, i); + if (fparam->storageClass & STClazy) + return TRUE; + } + return FALSE; +} + /*************************** * Examine function signature for parameter p and see if * p can 'escape' the scope of the function. @@ -5583,6 +5609,12 @@ bool TypeFunction::parameterEscapes(Parameter *p) return TRUE; } +Expression *TypeFunction::defaultInit(Loc loc) +{ + error(loc, "function does not have a default initializer"); + return new ErrorExp(); +} + /***************************** TypeDelegate *****************************/ TypeDelegate::TypeDelegate(Type *t) @@ -5644,7 +5676,7 @@ MATCH TypeDelegate::implicitConvTo(Type *to) //printf("to : %s\n", to->toChars()); if (this == to) return MATCHexact; -#if 0 // not allowing covariant conversions because it interferes with overriding +#if 1 // not allowing covariant conversions because it interferes with overriding if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1) return MATCHconvert; #endif @@ -6201,9 +6233,6 @@ Type *TypeInstance::semantic(Loc loc, Scope *sc) if (!t) { -#ifdef DEBUG - printf("2: "); -#endif error(loc, "%s is used as a type", toChars()); t = terror; } @@ -7380,15 +7409,37 @@ int TypeStruct::needsDestruction() int TypeStruct::isAssignable() { + int assignable = TRUE; + unsigned offset; + /* If any of the fields are const or invariant, * then one cannot assign this struct. */ for (size_t i = 0; i < sym->fields.dim; i++) { VarDeclaration *v = (VarDeclaration *)sym->fields.data[i]; - if (v->isConst() || v->isImmutable()) - return FALSE; + //printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind()); + if (i == 0) + ; + else if (v->offset == offset) + { + /* If any fields of anonymous union are assignable, + * then regard union as assignable. + * This is to support unsafe things like Rebindable templates. + */ + if (assignable) + continue; + } + else + { + if (!assignable) + return FALSE; + } + assignable = v->type->isMutable() && v->type->isAssignable(); + offset = v->offset; + //printf(" -> assignable = %d\n", assignable); } - return TRUE; + + return assignable; } int TypeStruct::hasPointers() @@ -8442,12 +8493,7 @@ void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *argu if (arg->defaultArg) { argbuf.writestring(" = "); - unsigned o = argbuf.offset; arg->defaultArg->toCBuffer(&argbuf, hgs); - if (hgs->ddoc) - { - escapeDdocString(&argbuf, o); - } } buf->write(&argbuf); } diff --git a/dmd2/mtype.h b/dmd2/mtype.h index db060aff..ed0c96cc 100644 --- a/dmd2/mtype.h +++ b/dmd2/mtype.h @@ -96,7 +96,6 @@ enum ENUMTY Tcomplex64, Tcomplex80, - Tbit, Tbool, Tchar, Twchar, @@ -175,7 +174,6 @@ struct Type : Object #define tcomplex64 basic[Tcomplex64] #define tcomplex80 basic[Tcomplex80] - #define tbit basic[Tbit] #define tbool basic[Tbool] #define tchar basic[Tchar] #define twchar basic[Twchar] @@ -412,7 +410,6 @@ struct TypeBasic : Type void toCppMangle(OutBuffer *buf, CppMangleState *cms); #endif int isintegral(); - int isbit(); int isfloating(); int isreal(); int isimaginary(); @@ -630,11 +627,13 @@ struct TypeFunction : TypeNext void purityLevel(); void toDecoBuffer(OutBuffer *buf, int flag, bool mangle); void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); + void toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td); void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void attributesToCBuffer(OutBuffer *buf, int mod); MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); TypeInfoDeclaration *getTypeInfoDeclaration(); Type *reliesOnTident(); + bool hasLazyParameters(); #if CPP_MANGLE void toCppMangle(OutBuffer *buf, CppMangleState *cms); #endif @@ -649,7 +648,11 @@ struct TypeFunction : TypeNext #if IN_DMD unsigned totym(); -#elif IN_LLVM +#endif + + Expression *defaultInit(Loc loc); + +#if IN_LLVM // LDC IrFuncTy fty; @@ -857,7 +860,6 @@ struct TypeTypedef : Type void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); Expression *getProperty(Loc loc, Identifier *ident); - int isbit(); int isintegral(); int isfloating(); int isreal(); diff --git a/dmd2/opover.c b/dmd2/opover.c index 68dded02..ae402ee8 100644 --- a/dmd2/opover.c +++ b/dmd2/opover.c @@ -327,7 +327,7 @@ Expression *UnaExp::op_overload(Scope *sc) else { // Rewrite +e1 as e1.add() - return build_overload(loc, sc, e1, NULL, fd->ident); + return build_overload(loc, sc, e1, NULL, fd); } } } @@ -418,7 +418,7 @@ Expression *CastExp::op_overload(Scope *sc) #if 1 // Backwards compatibility with D1 if opCast is a function, not a template if (fd->isFuncDeclaration()) { // Rewrite as: e1.opCast() - return build_overload(loc, sc, e1, NULL, fd->ident); + return build_overload(loc, sc, e1, NULL, fd); } #endif Objects *targsi = new Objects(); @@ -475,8 +475,10 @@ Expression *BinExp::op_overload(Scope *sc) Objects *targsi = NULL; #if DMDV2 - if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign) - { /* Try the new D2 scheme, opBinary and opBinaryRight + if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && + op != TOKplusplus && op != TOKminusminus) + { + /* Try the new D2 scheme, opBinary and opBinaryRight */ if (ad1) s = search_function(ad1, Id::opBinary); @@ -520,7 +522,7 @@ Expression *BinExp::op_overload(Scope *sc) } else { TemplateDeclaration *td = s->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args2); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } @@ -535,7 +537,7 @@ Expression *BinExp::op_overload(Scope *sc) } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args1); + templateResolve(&m, td, sc, loc, targsi, e2, &args1); } } @@ -560,13 +562,13 @@ Expression *BinExp::op_overload(Scope *sc) // as unary, but it's implemented as a binary. // Rewrite (e1 ++ e2) as e1.postinc() // Rewrite (e1 -- e2) as e1.postdec() - e = build_overload(loc, sc, e1, NULL, id); - else if (lastf && m.lastf == lastf || m.last == MATCHnomatch) + e = build_overload(loc, sc, e1, NULL, m.lastf ? m.lastf : s); + else if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) - e = build_overload(loc, sc, e1, e2, id, targsi); + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else // Rewrite (e1 op e2) as e2.opfunc_r(e1) - e = build_overload(loc, sc, e2, e1, id_r, targsi); + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); return e; } @@ -613,7 +615,7 @@ L1: } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args2); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } FuncDeclaration *lastf = m.lastf; @@ -627,7 +629,7 @@ L1: } else { TemplateDeclaration *td = s->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args1); + templateResolve(&m, td, sc, loc, targsi, e2, &args1); } } @@ -645,13 +647,12 @@ L1: } Expression *e; - if (lastf && m.lastf == lastf || - id_r && m.last == MATCHnomatch) + if (lastf && m.lastf == lastf || !s && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc_r(e2) - e = build_overload(loc, sc, e1, e2, id_r, targsi); + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); else // Rewrite (e1 op e2) as e2.opfunc(e1) - e = build_overload(loc, sc, e2, e1, id, targsi); + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); // When reversing operands of comparison operators, // need to reverse the sense of the op @@ -825,12 +826,12 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) } Expression *e; - if (lastf && m.lastf == lastf || m.last == MATCHnomatch) + if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) - e = build_overload(loc, sc, e1, e2, id, targsi); + e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else { // Rewrite (e1 op e2) as e2.opfunc_r(e1) - e = build_overload(loc, sc, e2, e1, id, targsi); + e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); // When reversing operands of comparison operators, // need to reverse the sense of the op @@ -1011,6 +1012,7 @@ Expression *BinAssignExp::op_overload(Scope *sc) #endif BinExp::semantic(sc); + e1 = resolveProperties(sc, e1); e2 = resolveProperties(sc, e2); Identifier *id = opId(); @@ -1067,7 +1069,7 @@ Expression *BinAssignExp::op_overload(Scope *sc) } else { TemplateDeclaration *td = s->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args2); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } @@ -1086,10 +1088,8 @@ Expression *BinAssignExp::op_overload(Scope *sc) goto L1; } - Expression *e; // Rewrite (e1 op e2) as e1.opOpAssign(e2) - e = build_overload(loc, sc, e1, e2, id, targsi); - return e; + return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); } L1: @@ -1130,22 +1130,20 @@ L1: */ Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, - Identifier *id, Objects *targsi) + Dsymbol *d) { + assert(d); Expression *e; //printf("build_overload(id = '%s')\n", id->toChars()); //earg->print(); //earg->type->print(); - if (targsi) - e = new DotTemplateInstanceExp(loc, ethis, id, targsi); + Declaration *decl = d->isDeclaration(); + if (decl) + e = new DotVarExp(loc, ethis, decl, 0); else - e = new DotIdExp(loc, ethis, id); - - if (earg) - e = new CallExp(loc, e, earg); - else - e = new CallExp(loc, e); + e = new DotIdExp(loc, ethis, d->ident); + e = new CallExp(loc, e, earg); e = e->semantic(sc); return e; diff --git a/dmd2/parse.c b/dmd2/parse.c index 6c66b9bc..c1eb0fff 100644 --- a/dmd2/parse.c +++ b/dmd2/parse.c @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -427,8 +427,10 @@ Dsymbols *Parser::parseDeclDefs(int once) if (token.value == TOKidentifier && (tk = peek(&token))->value == TOKlparen && skipParens(tk, &tk) && - (peek(tk)->value == TOKlparen || - peek(tk)->value == TOKlcurly) + ((tk = peek(tk)), 1) && + skipAttributes(tk, &tk) && + (tk->value == TOKlparen || + tk->value == TOKlcurly) ) { a = parseDeclarations(storageClass, comment); @@ -971,7 +973,8 @@ Dsymbol *Parser::parseCtor() Expression *constraint = tpl ? parseConstraint() : NULL; - CtorDeclaration *f = new CtorDeclaration(loc, 0, parameters, varargs, stc); + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); parseContracts(f); // Wrap a template around it @@ -987,7 +990,8 @@ Dsymbol *Parser::parseCtor() int varargs; Parameters *parameters = parseParameters(&varargs); StorageClass stc = parsePostfix(); - CtorDeclaration *f = new CtorDeclaration(loc, 0, parameters, varargs, stc); + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); parseContracts(f); return f; } @@ -2074,6 +2078,7 @@ Objects *Parser::parseTemplateArgument() case TOKstring: case TOKfile: case TOKline: + case TOKthis: { // Template argument is an expression Expression *ea = parsePrimaryExp(); tiargs->push(ea); @@ -2473,7 +2478,7 @@ Type *Parser::parseBasicType2(Type *t) return NULL; } -Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl) +Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl, StorageClass storage_class) { Type *ts; //printf("parseDeclarator(tpl = %p)\n", tpl); @@ -2602,6 +2607,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * /* Parse const/immutable/shared/inout/nothrow/pure postfix */ StorageClass stc = parsePostfix(); + stc |= storage_class; // merge prefix storage classes Type *tf = new TypeFunction(arguments, t, varargs, linkage, stc); if (stc & STCconst) @@ -2816,7 +2822,7 @@ L2: TemplateParameters *tpl = NULL; ident = NULL; - t = parseDeclarator(ts, &ident, &tpl); + t = parseDeclarator(ts, &ident, &tpl, storage_class); assert(t); if (!tfirst) tfirst = t; @@ -2879,6 +2885,9 @@ L2: tpl = new TemplateParameters(); } #endif + + //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class); + FuncDeclaration *f = new FuncDeclaration(loc, 0, ident, storage_class, t); addComment(f, comment); @@ -3184,6 +3193,8 @@ Initializer *Parser::parseInitializer() break; default: + if (comma == 1) + error("comma expected separating field initializers"); value = parseInitializer(); is->addInit(NULL, value); comma = 1; @@ -3427,9 +3438,8 @@ Statement *Parser::parseStatement(int flags) case TOKstatic: { // Look ahead to see if it's static assert() or static if() - Token *t; - t = peek(&token); + Token *t = peek(&token); if (t->value == TOKassert) { nextToken(); @@ -3452,6 +3462,13 @@ Statement *Parser::parseStatement(int flags) s = new ScopeStatement(loc, s); break; } + if (t->value == TOKimport) + { nextToken(); + Dsymbols *imports = new Dsymbols(); + parseImport(imports, 1); // static import ... + s = new ImportStatement(loc, imports); + break; + } goto Ldeclaration; } @@ -3477,6 +3494,7 @@ Statement *Parser::parseStatement(int flags) case TOKwild: case TOKnothrow: case TOKpure: + case TOKref: case TOKtls: case TOKgshared: case TOKat: @@ -4233,6 +4251,13 @@ Statement *Parser::parseStatement(int flags) break; } + case TOKimport: + { Dsymbols *imports = new Dsymbols(); + parseImport(imports, 0); + s = new ImportStatement(loc, imports); + break; + } + default: error("found '%s' instead of statement", token.toChars()); goto Lerror; @@ -4892,7 +4917,6 @@ int Parser::skipParens(Token *t, Token **pt) break; case TOKeof: - case TOKsemicolon: goto Lfalse; default: @@ -4910,6 +4934,61 @@ int Parser::skipParens(Token *t, Token **pt) return 0; } +/******************************************* + * Skip attributes. + * Input: + * t is on a candidate attribute + * Output: + * *pt is set to first non-attribute token on success + * Returns: + * !=0 successful + * 0 some parsing error + */ + +int Parser::skipAttributes(Token *t, Token **pt) +{ + while (1) + { + switch (t->value) + { + case TOKconst: + case TOKinvariant: + case TOKimmutable: + case TOKshared: + case TOKwild: + case TOKfinal: + case TOKauto: + case TOKscope: + case TOKoverride: + case TOKabstract: + case TOKsynchronized: + case TOKdeprecated: + case TOKnothrow: + case TOKpure: + case TOKref: + case TOKtls: + case TOKgshared: + //case TOKmanifest: + break; + case TOKat: + if (parseAttribute() == STCundefined) + break; + goto Lerror; + default: + goto Ldone; + } + t = peek(t); + } + + Ldone: + if (*pt) + *pt = t; + return 1; + + Lerror: + return 0; +} + /********************************* Expression Parser ***************************/ Expression *Parser::parsePrimaryExp() diff --git a/dmd2/parse.h b/dmd2/parse.h index bfcddd57..9319503a 100644 --- a/dmd2/parse.h +++ b/dmd2/parse.h @@ -107,7 +107,7 @@ struct Parser : Lexer Type *parseType(Identifier **pident = NULL, TemplateParameters **tpl = NULL); Type *parseBasicType(); Type *parseBasicType2(Type *t); - Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL); + Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL, StorageClass storage_class = 0); Dsymbols *parseDeclarations(StorageClass storage_class, unsigned char *comment); void parseContracts(FuncDeclaration *f); Statement *parseStatement(int flags); @@ -124,6 +124,7 @@ struct Parser : Lexer int isExpression(Token **pt); int isTemplateInstance(Token *t, Token **pt); int skipParens(Token *t, Token **pt); + int skipAttributes(Token *t, Token **pt); Expression *parseExpression(); Expression *parsePrimaryExp(); diff --git a/dmd2/root/dchar.c b/dmd2/root/dchar.c index c9462197..0b11a8a4 100644 --- a/dmd2/root/dchar.c +++ b/dmd2/root/dchar.c @@ -327,7 +327,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) case 2: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += *(const uint16_t *)str; #else hash += str[0] * 256 + str[1]; @@ -336,7 +336,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) case 3: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += (*(const uint16_t *)str << 8) + ((const uint8_t *)str)[2]; #else @@ -346,7 +346,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) default: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += *(const uint32_t *)str; #else hash += ((str[0] * 256 + str[1]) * 256 + str[2]) * 256 + str[3]; @@ -379,7 +379,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) case 2: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += *(const uint16_t *)str; #else hash += str[0] * 256 + str[1]; @@ -388,7 +388,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) case 3: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += (*(const uint16_t *)str << 8) + ((const uint8_t *)str)[2]; #else @@ -398,7 +398,7 @@ hash_t Dchar::calcHash(const dchar *str, size_t len) default: hash *= 37; -#if __I86__ +#if LITTLE_ENDIAN hash += *(const uint32_t *)str; #else hash += ((str[0] * 256 + str[1]) * 256 + str[2]) * 256 + str[3]; diff --git a/dmd2/root/man.c b/dmd2/root/man.c index ad0d3c74..3770aa96 100644 --- a/dmd2/root/man.c +++ b/dmd2/root/man.c @@ -26,7 +26,7 @@ void browse(const char *url) #endif -#if linux || __FreeBSD__ || __sun&&__SVR4 +#if linux || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 #include #include diff --git a/dmd2/root/port.c b/dmd2/root/port.c index 2bba4ef2..b7beef5c 100644 --- a/dmd2/root/port.c +++ b/dmd2/root/port.c @@ -1,5 +1,5 @@ -// Copyright (c) 1999-2009 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -70,6 +70,11 @@ double Port::pow(double x, double y) return ::pow(x, y); } +long double Port::fmodl(long double x, long double y) +{ + return ::fmodl(x, y); +} + unsigned long long Port::strtoull(const char *p, char **pend, int base) { return ::strtoull(p, pend, base); @@ -201,6 +206,11 @@ double Port::pow(double x, double y) return ::pow(x, y); } +long double Port::fmodl(long double x, long double y) +{ + return ::fmodl(x, y); +} + unsigned _int64 Port::strtoull(const char *p, char **pend, int base) { unsigned _int64 number = 0; @@ -315,7 +325,7 @@ char *Port::strupr(char *s) #endif -#if linux || __APPLE__ || __FreeBSD__ || __HAIKU__ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __HAIKU__ #include #if linux @@ -332,6 +342,7 @@ char *Port::strupr(char *s) #include #include #include +#include static double zero = 0; double Port::nan = NAN; @@ -369,15 +380,15 @@ PortInitializer::PortInitializer() } #ifndef __HAIKU__ -#undef isnan #endif int Port::isNan(double r) { #if __APPLE__ return __inline_isnan(r); -#elif defined __HAIKU__ +#elif defined __HAIKU__ || __OpenBSD__ return isnan(r); #else + #undef isnan return ::isnan(r); #endif } @@ -386,9 +397,10 @@ int Port::isNan(long double r) { #if __APPLE__ return __inline_isnan(r); -#elif defined __HAIKU__ +#elif defined __HAIKU__ || __OpenBSD__ return isnan(r); #else + #undef isnan return ::isnan(r); #endif } @@ -416,15 +428,15 @@ int Port::isFinite(double r) } #if !defined __HAIKU__ -#undef isinf #endif int Port::isInfinity(double r) { #if __APPLE__ return fpclassify(r) == FP_INFINITE; -#elif defined __HAIKU__ +#elif defined __HAIKU__ || __OpenBSD__ return isinf(r); #else + #undef isinf return ::isinf(r); #endif } @@ -447,6 +459,15 @@ double Port::pow(double x, double y) return ::pow(x, y); } +long double Port::fmodl(long double x, long double y) +{ +#if __FreeBSD__ || __OpenBSD__ + return ::fmod(x, y); // hack for now, fix later +#else + return ::fmodl(x, y); +#endif +} + unsigned long long Port::strtoull(const char *p, char **pend, int base) { return ::strtoull(p, pend, base); @@ -460,7 +481,11 @@ char *Port::ull_to_string(char *buffer, ulonglong ull) wchar_t *Port::ull_to_string(wchar_t *buffer, ulonglong ull) { +#if __OpenBSD__ + assert(0); +#else swprintf(buffer, sizeof(ulonglong) * 3 + 1, L"%llu", ull); +#endif return buffer; } diff --git a/dmd2/root/port.h b/dmd2/root/port.h index b4e7fbb7..d915991d 100644 --- a/dmd2/root/port.h +++ b/dmd2/root/port.h @@ -40,7 +40,8 @@ struct Port static double dbl_min; static long double ldbl_max; -#if __GNUC__ && !defined __HAIKU__ +#if !defined __HAIKU__ || __OpenBSD__ +#elif __GNUC__ // These conflict with macros in math.h, should rename them #undef isnan #undef isfinite @@ -60,6 +61,8 @@ struct Port static double floor(double); static double pow(double x, double y); + static long double fmodl(long double x, long double y); + static ulonglong strtoull(const char *p, char **pend, int base); static char *ull_to_string(char *buffer, ulonglong ull); diff --git a/dmd2/root/response.c b/dmd2/root/response.c index ff085199..56c350af 100644 --- a/dmd2/root/response.c +++ b/dmd2/root/response.c @@ -19,7 +19,7 @@ #include #endif -#if linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4 +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 #include #include #include diff --git a/dmd2/root/root.c b/dmd2/root/root.c index 6cd0ea64..5513e195 100644 --- a/dmd2/root/root.c +++ b/dmd2/root/root.c @@ -8,7 +8,7 @@ // See the included readme.txt for details. #ifndef POSIX -#define POSIX (linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4) +#define POSIX (linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) #endif #include diff --git a/dmd2/statement.c b/dmd2/statement.c index 1a0c8bba..d11a8e96 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -53,11 +53,9 @@ extern int os_critsecsize64(); Statement::Statement(Loc loc) : loc(loc) { -#ifdef _DH // If this is an in{} contract scope statement (skip for determining // inlineStatus of a function body for header content) incontract = 0; -#endif } Statement *Statement::syntaxCopy() @@ -178,6 +176,11 @@ int Statement::isEmpty() return FALSE; } +Statement *Statement::last() +{ + return this; +} + /**************************************** * If this statement has code that needs to run in a finally clause * at the end of the current scope, return that code in the form of @@ -616,6 +619,22 @@ ReturnStatement *CompoundStatement::isReturnStatement() return rs; } +Statement *CompoundStatement::last() +{ + Statement *s = NULL; + + for (size_t i = statements->dim; i; --i) + { s = (Statement *) statements->data[i - 1]; + if (s) + { + s = s->last(); + if (s) + break; + } + } + return s; +} + void CompoundStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { for (int i = 0; i < statements->dim; i++) @@ -639,12 +658,31 @@ int CompoundStatement::blockExit(bool mustNotThrow) { //printf("CompoundStatement::blockExit(%p) %d\n", this, statements->dim); int result = BEfallthru; + Statement *slast = NULL; for (size_t i = 0; i < statements->dim; i++) { Statement *s = (Statement *) statements->data[i]; if (s) { -//printf("result = x%x\n", result); -//printf("%s\n", s->toChars()); + //printf("result = x%x\n", result); + //printf("%s\n", s->toChars()); + if (global.params.warnings && result & BEfallthru && slast) + { + slast = slast->last(); + if (slast && (s->isCaseStatement() || s->isDefaultStatement())) + { + // Allow if last case/default was empty + CaseStatement *sc = slast->isCaseStatement(); + DefaultStatement *sd = slast->isDefaultStatement(); + if (sc && sc->statement->isEmpty()) + ; + else if (sd && sd->statement->isEmpty()) + ; + else + s->error("switch case fallthrough - use 'goto %s;' if intended", + s->isCaseStatement() ? "case" : "default"); + } + } + if (!(result & BEfallthru) && !s->comeFrom()) { if (s->blockExit(mustNotThrow) != BEhalt && !s->isEmpty()) @@ -655,6 +693,7 @@ int CompoundStatement::blockExit(bool mustNotThrow) result &= ~BEfallthru; result |= s->blockExit(mustNotThrow); } + slast = s; } } return result; @@ -1725,7 +1764,7 @@ Lagain: // __r.next e = new VarExp(loc, r); - Expression *increment = new DotIdExp(loc, e, idnext); + Expression *increment = new CallExp(loc, new DotIdExp(loc, e, idnext)); /* Declaration statement for e: * auto e = __r.idhead; @@ -2399,9 +2438,6 @@ Statement *IfStatement::syntaxCopy() Statement *IfStatement::semantic(Scope *sc) { - condition = condition->semantic(sc); - condition = resolveProperties(sc, condition); - // Evaluate at runtime unsigned cs0 = sc->callSuper; unsigned cs1; @@ -2415,24 +2451,29 @@ Statement *IfStatement::semantic(Scope *sc) sym->parent = sc->scopesym; scd = sc->push(sym); - Type *t = arg->type ? arg->type : condition->type; - match = new VarDeclaration(loc, t, arg->ident, NULL); - match->noscope = 1; - match->semantic(scd); - if (!scd->insert(match)) - assert(0); + match = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, condition)); match->parent = sc->func; - /* Generate: - * ((arg = condition), arg) - */ - VarExp *v = new VarExp(0, match); - condition = new AssignExp(loc, v, condition); - condition = new CommaExp(loc, condition, v); + DeclarationExp *de = new DeclarationExp(loc, match); + VarExp *ve = new VarExp(0, match); + condition = new CommaExp(loc, de, ve); condition = condition->semantic(scd); + + if (match->edtor) + { + Statement *sdtor = new ExpStatement(loc, match->edtor); + sdtor = new OnScopeStatement(loc, TOKon_scope_exit, sdtor); + ifbody = new CompoundStatement(loc, sdtor, ifbody); + match->noscope = 1; + } } else + { + condition = condition->semantic(sc); + condition = condition->addDtorHook(sc); + condition = resolveProperties(sc, condition); scd = sc->push(); + } // Convert to boolean after declaring arg so this works: // if (S arg = S()) {} @@ -2913,7 +2954,8 @@ Statement *SwitchStatement::semantic(Scope *sc) if (!sc->sw->sdefault && !isFinal) { hasNoDefault = 1; - warning("switch statement has no default"); + if (!global.params.useDeprecated) + error("non-final switch statement without a default is deprecated"); // Generate runtime error if the default is hit Statements *a = new Statements(); @@ -3007,7 +3049,7 @@ int SwitchStatement::blockExit(bool mustNotThrow) void SwitchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - buf->writestring("switch ("); + buf->writestring(isFinal ? "final switch (" : "switch ("); condition->toCBuffer(buf, hgs); buf->writebyte(')'); buf->writenl(); @@ -3510,7 +3552,8 @@ Statement *ReturnStatement::semantic(Scope *sc) exp = exp->semantic(sc); exp = resolveProperties(sc, exp); - exp = exp->optimize(WANTvalue); + if (!((TypeFunction *)fd->type)->isref) + exp = exp->optimize(WANTvalue); if (fd->nrvo_can && exp->op == TOKvar) { VarExp *ve = (VarExp *)exp; @@ -3592,7 +3635,8 @@ Statement *ReturnStatement::semantic(Scope *sc) else if (tbret->ty != Tvoid) { exp = exp->implicitCastTo(sc, tret); - exp = exp->optimize(WANTvalue); + if (!((TypeFunction *)fd->type)->isref) + exp = exp->optimize(WANTvalue); } } else if (fd->inferRetType) @@ -3745,6 +3789,17 @@ Statement *ReturnStatement::semantic(Scope *sc) return new CompoundStatement(loc, s, this); } + if (exp) + { if (exp->op == TOKcall) + valueNoDtor(exp); + else + { + Expression *e = exp->isTemp(); + if (e) + exp = e; // don't need temporary + } + } + return this; } @@ -4331,8 +4386,7 @@ int TryCatchStatement::blockExit(bool mustNotThrow) /* If we're catching Object, then there is no throwing */ Identifier *id = c->type->toBasetype()->isClassHandle()->ident; - if (i == 0 && - (id == Id::Object || id == Id::Throwable || id == Id::Exception)) + if (id == Id::Object || id == Id::Throwable || id == Id::Exception) { result &= ~BEthrow; } @@ -4412,6 +4466,15 @@ void Catch::semantic(Scope *sc) type = Type::terror; } } + else if (sc->func && + !sc->intypeof && + cd != ClassDeclaration::exception && + !ClassDeclaration::exception->isBaseOf(cd, NULL) && + sc->func->setUnsafe()) + { + error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", type->toChars()); + type = Type::terror; + } else if (ident) { var = new VarDeclaration(loc, type, ident, NULL); @@ -5014,3 +5077,50 @@ void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } #endif + +/************************ ImportStatement ***************************************/ + +ImportStatement::ImportStatement(Loc loc, Dsymbols *imports) + : Statement(loc) +{ + this->imports = imports; +} + +Statement *ImportStatement::syntaxCopy() +{ + Dsymbols *m = new Dsymbols(); + m->setDim(imports->dim); + for (int i = 0; i < imports->dim; i++) + { Dsymbol *s = (Dsymbol *)imports->data[i]; + m->data[i] = (void *)s->syntaxCopy(NULL); + } + return new ImportStatement(loc, m); +} + +Statement *ImportStatement::semantic(Scope *sc) +{ + for (int i = 0; i < imports->dim; i++) + { Dsymbol *s = (Dsymbol *)imports->data[i]; + s->semantic(sc); + sc->insert(s); + } + return this; +} + +int ImportStatement::blockExit(bool mustNotThrow) +{ + return BEfallthru; +} + +int ImportStatement::isEmpty() +{ + return TRUE; +} + +void ImportStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + for (int i = 0; i < imports->dim; i++) + { Dsymbol *s = (Dsymbol *)imports->data[i]; + s->toCBuffer(buf, hgs); + } +} diff --git a/dmd2/statement.h b/dmd2/statement.h index d96c3f67..a6f43ac7 100644 --- a/dmd2/statement.h +++ b/dmd2/statement.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -48,6 +48,9 @@ struct GotoStatement; struct ScopeStatement; struct TryCatchStatement; struct TryFinallyStatement; +struct CaseStatement; +struct DefaultStatement; +struct LabelStatement; struct HdrGenState; struct InterState; #if IN_LLVM @@ -114,12 +117,7 @@ struct Statement : Object void warning(const char *format, ...) IS_PRINTF(2); virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); virtual AsmBlockStatement *isAsmBlockStatement() { return NULL; } -#if IN_LLVM - virtual LabelStatement *isLabelStatement() { return NULL; } -#endif -#ifdef _DH int incontract; -#endif virtual ScopeStatement *isScopeStatement() { return NULL; } virtual Statement *semantic(Scope *sc); Statement *semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue); @@ -133,6 +131,7 @@ struct Statement : Object virtual Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); virtual Statements *flatten(Scope *sc); virtual Expression *interpret(InterState *istate); + virtual Statement *last(); virtual int inlineCost(InlineCostState *ics); virtual Expression *doInline(InlineDoState *ids); @@ -146,7 +145,9 @@ struct Statement : Object virtual CompoundStatement *isCompoundStatement() { return NULL; } virtual ReturnStatement *isReturnStatement() { return NULL; } virtual IfStatement *isIfStatement() { return NULL; } - virtual CaseStatement* isCaseStatement() { return NULL; } + virtual CaseStatement *isCaseStatement() { return NULL; } + virtual DefaultStatement *isDefaultStatement() { return NULL; } + virtual LabelStatement *isLabelStatement() { return NULL; } #if IN_LLVM virtual void toNakedIR(IRState *irs); @@ -216,6 +217,7 @@ struct CompoundStatement : Statement virtual Statements *flatten(Scope *sc); ReturnStatement *isReturnStatement(); Expression *interpret(InterState *istate); + Statement *last(); int inlineCost(InlineCostState *ics); Expression *doInline(InlineDoState *ids); @@ -534,13 +536,12 @@ struct CaseStatement : Statement int comeFrom(); Expression *interpret(InterState *istate); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + CaseStatement *isCaseStatement() { return this; } Statement *inlineScan(InlineScanState *iss); void toIR(IRState *irs); - CaseStatement* isCaseStatement() { return this; } - #if IN_LLVM llvm::BasicBlock* bodyBB; llvm::Value* llvmIdx; @@ -582,6 +583,7 @@ struct DefaultStatement : Statement int comeFrom(); Expression *interpret(InterState *istate); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + DefaultStatement *isDefaultStatement() { return this; } Statement *inlineScan(InlineScanState *iss); @@ -885,13 +887,13 @@ struct LabelStatement : Statement void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *inlineScan(InlineScanState *iss); + LabelStatement *isLabelStatement() { return this; } void toIR(IRState *irs); #if IN_LLVM bool asmLabel; // for labels inside inline assembler void toNakedIR(IRState *irs); - virtual LabelStatement *isLabelStatement() { return this; } #endif }; @@ -925,8 +927,8 @@ struct AsmStatement : Statement void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toIR(IRState *irs); - -#if IN_LLVM + + #if IN_LLVM // non-zero if this is a branch, contains the target labels identifier Identifier* isBranchToLabel; @@ -934,6 +936,25 @@ struct AsmStatement : Statement #endif }; +struct ImportStatement : Statement +{ + Dsymbols *imports; // Array of Import's + + ImportStatement(Loc loc, Dsymbols *imports); + Statement *syntaxCopy(); + Statement *semantic(Scope *sc); + int blockExit(bool mustNotThrow); + int isEmpty(); + Expression *interpret(InterState *istate); + + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + + int inlineCost(InlineCostState *ics); + Expression *doInline(InlineDoState *ids); + + //void toIR(IRState *irs); +}; + struct AsmBlockStatement : CompoundStatement { TryFinallyStatement* enclosingFinally; diff --git a/dmd2/staticassert.h b/dmd2/staticassert.h index 632c1e90..2f7e0906 100644 --- a/dmd2/staticassert.h +++ b/dmd2/staticassert.h @@ -18,9 +18,7 @@ #include "dsymbol.h" struct Expression; -#ifdef _DH struct HdrGenState; -#endif struct StaticAssert : Dsymbol { diff --git a/dmd2/struct.c b/dmd2/struct.c index c045b279..92c8c418 100644 --- a/dmd2/struct.c +++ b/dmd2/struct.c @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -38,6 +38,7 @@ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id) structalign = 0; // struct member alignment in effect hasUnions = 0; sizeok = 0; // size not determined yet + deferred = NULL; isdeprecated = 0; inv = NULL; aggNew = NULL; @@ -348,7 +349,7 @@ void StructDeclaration::semantic(Scope *sc) { Scope *sc2; - //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", this, toChars(), sizeok); + //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); @@ -675,6 +676,11 @@ void StructDeclaration::semantic(Scope *sc) semantic2(sc); semantic3(sc); } + if (deferred) + { + deferred->semantic2(sc); + deferred->semantic3(sc); + } } Dsymbol *StructDeclaration::search(Loc loc, Identifier *ident, int flags) @@ -730,6 +736,7 @@ const char *StructDeclaration::kind() UnionDeclaration::UnionDeclaration(Loc loc, Identifier *id) : StructDeclaration(loc, id) { + hasUnions = 1; } Dsymbol *UnionDeclaration::syntaxCopy(Dsymbol *s) diff --git a/dmd2/template.c b/dmd2/template.c index 8a9b01c2..d48d432c 100644 --- a/dmd2/template.c +++ b/dmd2/template.c @@ -376,6 +376,20 @@ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, this->literal = 0; this->ismixin = ismixin; this->previous = NULL; + + // Compute in advance for Ddoc's use + if (members) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s)) + { + if (s && s->ident && s->ident->equals(ident)) + { + onemember = s; + s->parent = this; + } + } + } } Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) @@ -500,6 +514,8 @@ void TemplateDeclaration::semantic(Scope *sc) paramscope->pop(); + // Compute again + onemember = NULL; if (members) { Dsymbol *s; @@ -1650,11 +1666,14 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { -#if 0 // Should handle template functions +#if 0 // Should handle template functions for doc generation if (onemember && onemember->isFuncDeclaration()) buf->writestring("foo "); #endif - buf->writestring(kind()); + if (hgs->ddoc) + buf->writestring(kind()); + else + buf->writestring("template"); buf->writeByte(' '); buf->writestring(ident->toChars()); buf->writeByte('('); @@ -4126,7 +4145,13 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) { Dsymbol *sd = (Dsymbol *)Module::deferred.data[i]; if (sd->parent == this) + { + //printf("deferred %s %s\n", sd->parent->toChars(), sd->toChars()); + AggregateDeclaration *ad = sd->isAggregateDeclaration(); + if (ad) + ad->deferred = this; goto Laftersemantic; + } } /* The problem is when to parse the initializer for a variable. @@ -4730,6 +4755,12 @@ Identifier *TemplateInstance::genIdent(Objects *args) ea = NULL; goto Lsa; } + if (ea->op == TOKthis) + { + sa = ((ThisExp *)ea)->var; + ea = NULL; + goto Lsa; + } if (ea->op == TOKfunction) { sa = ((FuncExp *)ea)->fd; diff --git a/dmd2/traits.c b/dmd2/traits.c index 0753ad23..b085b0ed 100644 --- a/dmd2/traits.c +++ b/dmd2/traits.c @@ -163,7 +163,7 @@ Expression *TraitsExp::semantic(Scope *sc) #if DMDV2 else if (ident == Id::isStaticFunction) { - ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis()) + ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis() && !f->isNested()) } else if (ident == Id::isRef) { diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 9c5a78ad..0dc9716e 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -162,7 +162,7 @@ Statement *AsmStatement::semantic(Scope *sc) //puts(toChars()); - sc->func->inlineAsm = 1; + sc->func->inlineAsm = true; sc->func->inlineStatus = ILSno; // %% not sure // %% need to set DECL_UNINLINABLE too? sc->func->hasReturnExp = 1; // %% DMD does this, apparently...