diff --git a/src/cast.c b/src/cast.c index 2c466d688c25..8687348d5aff 100644 --- a/src/cast.c +++ b/src/cast.c @@ -1734,8 +1734,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression } else if (t1->ty == Tclass || t2->ty == Tclass) { - if (t1->mod != t2->mod) - { unsigned char mod = MODmerge(t1->mod, t2->mod); + if (t1->head()->mod != t2->head()->mod) + { unsigned char mod = MODmerge(t1->head()->mod, t2->head()->mod); t1 = t1->castMod(mod); t2 = t2->castMod(mod); t = t1; diff --git a/src/declaration.c b/src/declaration.c index 3d8bbbeadcd6..55c6534023cc 100644 --- a/src/declaration.c +++ b/src/declaration.c @@ -91,7 +91,7 @@ void Declaration::checkModify(Loc loc, Scope *sc, Type *t) if (sc->incontract && isResult()) error(loc, "cannot modify result '%s' in contract", toChars()); - if (isCtorinit() && !t->isMutable()) + if (isCtorinit() && !t->head()->isMutable()) { // It's only modifiable if inside the right constructor Dsymbol *s = sc->func; while (1) @@ -904,16 +904,17 @@ void VarDeclaration::semantic(Scope *sc) /* Adjust storage class to reflect type */ - if (type->isConst()) + Type *thead = type->head(); + if (thead->isConst()) { storage_class |= STCconst; - if (type->isShared()) + if (thead->isShared()) storage_class |= STCshared; } - else if (type->isImmutable()) + else if (thead->isImmutable()) storage_class |= STCimmutable; - else if (type->isShared()) + else if (thead->isShared()) storage_class |= STCshared; - else if (type->isWild()) + else if (thead->isWild()) storage_class |= STCwild; if (isSynchronized()) diff --git a/src/expression.c b/src/expression.c index 2d9ac0049ae9..6392a36bb4f0 100644 --- a/src/expression.c +++ b/src/expression.c @@ -1164,7 +1164,7 @@ Expression *Expression::modifiableLvalue(Scope *sc, Expression *e) // See if this expression is a modifiable lvalue (i.e. not const) #if DMDV2 - if (type && (!type->isMutable() || !type->isAssignable())) + if (type && (!type->head()->isMutable() || !type->isAssignable())) error("%s is not mutable", e->toChars()); #endif return toLvalue(sc, e); @@ -6548,9 +6548,9 @@ Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e) Type *t1 = e1->type->toBasetype(); - if (!t1->isMutable() || - (t1->ty == Tpointer && !t1->nextOf()->isMutable()) || - !var->type->isMutable() || + if (!t1->head()->isMutable() || + (t1->ty == Tpointer && !t1->nextOf()->head()->isMutable()) || + !var->type->head()->isMutable() || !var->type->isAssignable() || var->storage_class & STCmanifest ) @@ -9118,7 +9118,7 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) modifiable = 1; if (e1->op == TOKstring) error("string literals are immutable"); - if (type && (!type->isMutable() || !type->isAssignable())) + if (type && (!type->head()->isMutable() || !type->head()->isAssignable())) error("%s isn't mutable", e->toChars()); Type *t1 = e1->type->toBasetype(); if (t1->ty == Taarray) @@ -9580,7 +9580,7 @@ Expression *AssignExp::semantic(Scope *sc) else if (e1->op == TOKslice) { Type *tn = e1->type->nextOf(); - if (op == TOKassign && tn && (!tn->isMutable() || !tn->isAssignable())) + if (op == TOKassign && tn && (!tn->head()->isMutable() || !tn->head()->isAssignable())) { error("slice %s is not mutable", e1->toChars()); return new ErrorExp(); } diff --git a/src/func.c b/src/func.c index b18905ad5e30..6c0ef3bfa71b 100644 --- a/src/func.c +++ b/src/func.c @@ -1315,7 +1315,7 @@ void FuncDeclaration::semantic3(Scope *sc) for (int i = 0; i < cd->fields.dim; i++) { VarDeclaration *v = (VarDeclaration *)cd->fields.data[i]; - if (v->ctorinit == 0 && v->isCtorinit() && !v->type->isMutable()) + if (v->ctorinit == 0 && v->isCtorinit() && !v->type->head()->isMutable()) error("missing initializer for final field %s", v->toChars()); } } diff --git a/src/mtype.c b/src/mtype.c index b854808fee0e..4b4d392f7970 100644 --- a/src/mtype.c +++ b/src/mtype.c @@ -122,6 +122,7 @@ Type::Type(TY ty) this->ty = ty; this->mod = 0; this->deco = NULL; + this->thead = NULL; #if DMDV2 this->cto = NULL; this->ito = NULL; @@ -197,6 +198,7 @@ void Type::init() mangleChar[Taarray] = 'H'; mangleChar[Tpointer] = 'P'; mangleChar[Treference] = 'R'; + mangleChar[Trefsuffix] = 'X'; mangleChar[Tfunction] = 'F'; mangleChar[Tident] = 'I'; mangleChar[Tclass] = 'C'; @@ -344,7 +346,7 @@ MATCH Type::constConv(Type *to) { if (equals(to)) return MATCHexact; - if (ty == to->ty && MODimplicitConv(mod, to->mod)) + if (ty == to->ty && MODimplicitConv(head()->mod, to->head()->mod)) return MATCHconst; return MATCHnomatch; } @@ -356,10 +358,10 @@ MATCH Type::constConv(Type *to) Type *Type::constOf() { //printf("Type::constOf() %p %s\n", this, toChars()); - if (mod == MODconst) + if (head()->mod == MODconst) return this; if (cto) - { assert(cto->mod == MODconst); + { assert(cto->head()->mod == MODconst); return cto; } Type *t = makeConst(); @@ -376,13 +378,13 @@ Type *Type::constOf() Type *Type::invariantOf() { //printf("Type::invariantOf() %p %s\n", this, toChars()); - if (isImmutable()) + if (head()->isImmutable()) { return this; } if (ito) { - assert(ito->isImmutable()); + assert(ito->head()->isImmutable()); return ito; } Type *t = makeInvariant(); @@ -400,24 +402,24 @@ Type *Type::mutableOf() { //printf("Type::mutableOf() %p, %s\n", this, toChars()); Type *t = this; - if (isConst()) - { if (isShared()) + if (head()->isConst()) + { if (head()->isShared()) t = sto; // shared const => shared else t = cto; // const => naked - assert(!t || t->isMutable()); + assert(!t || t->head()->isMutable()); } - else if (isImmutable()) + else if (head()->isImmutable()) { t = ito; // immutable => naked - assert(!t || (t->isMutable() && !t->isShared())); + assert(!t || (t->head()->isMutable() && !t->head()->isShared())); } - else if (isWild()) + else if (head()->isWild()) { - if (isShared()) + if (head()->isShared()) t = sto; // shared wild => shared else t = wto; // wild => naked - assert(!t || t->isMutable()); + assert(!t || t->head()->isMutable()); } if (!t) { @@ -425,20 +427,20 @@ Type *Type::mutableOf() t = t->merge(); t->fixTo(this); } - assert(t->isMutable()); + assert(t->head()->isMutable()); return t; } Type *Type::sharedOf() { //printf("Type::sharedOf() %p, %s\n", this, toChars()); - if (mod == MODshared) + if (head()->mod == MODshared) { return this; } if (sto) { - assert(sto->isShared()); + assert(sto->head()->isShared()); return sto; } Type *t = makeShared(); @@ -451,13 +453,13 @@ Type *Type::sharedOf() Type *Type::sharedConstOf() { //printf("Type::sharedConstOf() %p, %s\n", this, toChars()); - if (mod == (MODshared | MODconst)) + if (head()->mod == (MODshared | MODconst)) { return this; } if (scto) { - assert(scto->mod == (MODshared | MODconst)); + assert(scto->head()->mod == (MODshared | MODconst)); return scto; } Type *t = makeSharedConst(); @@ -484,39 +486,25 @@ Type *Type::unSharedOf() //printf("Type::unSharedOf() %p, %s\n", this, toChars()); Type *t = this; - if (isShared()) + if (head()->isShared()) { - if (isConst()) + if (head()->isConst()) t = cto; // shared const => const - else if (isWild()) + else if (head()->isWild()) t = wto; // shared wild => wild else t = sto; - assert(!t || !t->isShared()); + assert(!t || !t->head()->isShared()); } if (!t) { - unsigned sz = sizeTy[ty]; - t = (Type *)mem.malloc(sz); - memcpy(t, this, sz); - t->mod = mod & ~MODshared; - t->deco = NULL; - t->arrayof = NULL; - t->pto = NULL; - t->rto = NULL; - t->cto = NULL; - t->ito = NULL; - t->sto = NULL; - t->scto = NULL; - t->wto = NULL; - t->swto = NULL; - t->vtinfo = NULL; + t = makeUnShared(); t = t->merge(); t->fixTo(this); } - assert(!t->isShared()); + assert(!t->head()->isShared()); return t; } @@ -527,13 +515,13 @@ Type *Type::unSharedOf() Type *Type::wildOf() { //printf("Type::wildOf() %p %s\n", this, toChars()); - if (mod == MODwild) + if (head()->mod == MODwild) { return this; } if (wto) { - assert(wto->isWild()); + assert(wto->head()->isWild()); return wto; } Type *t = makeWild(); @@ -546,13 +534,13 @@ Type *Type::wildOf() Type *Type::sharedWildOf() { //printf("Type::sharedWildOf() %p, %s\n", this, toChars()); - if (mod == (MODshared | MODwild)) + if (head()->mod == (MODshared | MODwild)) { return this; } if (swto) { - assert(swto->mod == (MODshared | MODwild)); + assert(swto->head()->mod == (MODshared | MODwild)); return swto; } Type *t = makeSharedWild(); @@ -580,9 +568,9 @@ void Type::fixTo(Type *t) scto = t->scto; #endif - assert(mod != t->mod); + assert(head()->mod != t->head()->mod); #define X(m, n) (((m) << 4) | (n)) - switch (X(mod, t->mod)) + switch (X(head()->mod, t->head()->mod)) { case X(0, MODconst): cto = t; @@ -792,69 +780,69 @@ void Type::fixTo(Type *t) void Type::check() { - switch (mod) + switch (head()->mod) { case 0: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODconst: - if (cto) assert(cto->mod == 0); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == 0); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODimmutable: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == 0); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == 0); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODshared: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == 0); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == 0); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODshared | MODconst: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == 0); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == 0); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODwild: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == 0); - if (swto) assert(swto->mod == (MODshared | MODwild)); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == 0); + if (swto) assert(swto->head()->mod == (MODshared | MODwild)); break; case MODshared | MODwild: - if (cto) assert(cto->mod == MODconst); - if (ito) assert(ito->mod == MODimmutable); - if (sto) assert(sto->mod == MODshared); - if (scto) assert(scto->mod == (MODshared | MODconst)); - if (wto) assert(wto->mod == MODwild); - if (swto) assert(swto->mod == 0); + if (cto) assert(cto->head()->mod == MODconst); + if (ito) assert(ito->head()->mod == MODimmutable); + if (sto) assert(sto->head()->mod == MODshared); + if (scto) assert(scto->head()->mod == (MODshared | MODconst)); + if (wto) assert(wto->head()->mod == MODwild); + if (swto) assert(swto->head()->mod == 0); break; default: @@ -990,6 +978,26 @@ Type *Type::makeSharedConst() return t; } +Type *Type::makeUnShared() +{ + unsigned sz = sizeTy[ty]; + Type *t = (Type *)mem.malloc(sz); + memcpy(t, this, sz); + t->mod = mod & ~MODshared; + t->deco = NULL; + t->arrayof = NULL; + t->pto = NULL; + t->rto = NULL; + t->cto = NULL; + t->ito = NULL; + t->sto = NULL; + t->scto = NULL; + t->wto = NULL; + t->swto = NULL; + t->vtinfo = NULL; + return t; +} + Type *Type::makeWild() { if (wto) @@ -1108,7 +1116,7 @@ Type *Type::addMod(unsigned mod) /* Add anything to immutable, and it remains immutable */ - if (!t->isImmutable()) + if (!t->head()->isImmutable()) { //printf("addMod(%x) %s\n", mod, toChars()); switch (mod) @@ -1117,7 +1125,7 @@ Type *Type::addMod(unsigned mod) break; case MODconst: - if (isShared()) + if (head()->isShared()) t = sharedConstOf(); else t = constOf(); @@ -1128,9 +1136,9 @@ Type *Type::addMod(unsigned mod) break; case MODshared: - if (isConst()) + if (head()->isConst()) t = sharedConstOf(); - else if (isWild()) + else if (head()->isWild()) t = sharedWildOf(); else t = sharedOf(); @@ -1141,9 +1149,9 @@ Type *Type::addMod(unsigned mod) break; case MODwild: - if (isConst()) + if (head()->isConst()) ; - else if (isShared()) + else if (head()->isShared()) t = sharedWildOf(); else t = wildOf(); @@ -4381,6 +4389,75 @@ int TypeReference::isZeroInit(Loc loc) } + +/***************************** TypeRefSuffix *****************************/ + +TypeRefSuffix::TypeRefSuffix(Type *t) + : TypeNext(Trefsuffix, t) +{ + // BUG: what about references to static arrays? +} + +Type *TypeRefSuffix::syntaxCopy() +{ + Type *t = next->syntaxCopy(); + if (t == next) + t = this; + else + { t = new TypeRefSuffix(t); + t->mod = mod; + } + return t; +} + +Type *TypeRefSuffix::semantic(Loc loc, Scope *sc) +{ + //printf("TypeReference::semantic()\n"); + Type *n = next->semantic(loc, sc); + if (next->ty == Trefsuffix) + { error(loc, "double ref suffix"); + return n; + } + if (n->ty != Tclass) + { if (sc->parameterSpecialization) + return this; + error(loc, "ref suffix is only valid for class types"); + return n; + } + next = n; + transitive(); + if (next->mod != mod) + { /* apply ref suffix modifiers. + */ + return next->castMod(mod); + } + return next; +} + +void TypeRefSuffix::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +{ + if (mod != this->mod) + { toCBuffer3(buf, hgs, mod); + return; + } + next->toCBuffer2(buf, hgs, this->mod); + // skip the space if last character in buffer is a closing parenthesis + if (buf->offset && buf->data[buf->offset-1] != ')') + buf->writeByte(' '); + buf->writestring("ref"); +} + +Expression *TypeRefSuffix::dotExp(Scope *sc, Expression *e, Identifier *ident) +{ +#if LOGDOTEXP + printf("TypeRefSuffix::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); +#endif + + // References just forward things along + return next->dotExp(sc, e, ident); +} + + /***************************** TypeFunction *****************************/ TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, enum LINK linkage, StorageClass stc) @@ -7281,6 +7358,80 @@ TypeClass::TypeClass(ClassDeclaration *sym) this->sym = sym; } +TypeClass::TypeClass(TypeClass* tc, unsigned char headmod) + : Type(Tclass) +{ + this->sym = tc->sym; + this->mod = tc->mod; + + // Apply head modifiers to class (transitivity) + if (headmod != 0) { + if (this->mod == 0) + this->mod = headmod; + else if (headmod == MODimmutable || this->mod == MODimmutable) + this->mod = MODimmutable; + else + { + // remaining cases combining shared, const, and wild + #define X(m, n) (((m) << 4) | (n)) + switch (X(headmod, this->mod)) + { + case X(MODshared, MODshared): + this->mod = MODshared; + break; + + case X(MODconst, MODwild): + case X(MODconst, MODconst): + case X(MODwild, MODconst): + this->mod = MODconst; + break; + + case X(MODconst, MODshared): + case X(MODconst | MODshared, MODshared): + case X(MODconst | MODshared, MODwild): + case X(MODshared, MODconst): + case X(MODconst | MODshared, MODconst): + case X(MODwild | MODshared, MODconst): + case X(MODconst, MODwild | MODshared): + case X(MODconst | MODshared, MODwild | MODshared): + case X(MODshared, MODconst | MODshared): + case X(MODconst, MODconst | MODshared): + case X(MODconst | MODshared, MODconst | MODshared): + case X(MODwild, MODconst | MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + this->mod = MODconst | MODshared; + break; + + case X(MODwild, MODwild): + this->mod = MODwild; + break; + + case X(MODwild, MODshared): + case X(MODwild | MODshared, MODshared): + case X(MODshared, MODwild): + case X(MODwild | MODshared, MODwild): + case X(MODshared, MODwild | MODshared): + case X(MODwild, MODwild | MODshared): + case X(MODwild | MODshared, MODwild | MODshared): + this->mod = MODwild | MODshared; + break; + + default: + assert(0); + } + } + } + + // Create type to hold head-modifiers (if they are different) + if (headmod != this->mod) + { + thead = new TypeRefSuffix(this); + thead->mod = headmod; + ((TypeRefSuffix *)thead)->next = this; + thead->check(); + } +} + char *TypeClass::toChars() { if (mod) @@ -7314,6 +7465,17 @@ Dsymbol *TypeClass::toDsymbol(Scope *sc) void TypeClass::toDecoBuffer(OutBuffer *buf, int flag) { + if (thead && thead->mod != this->mod) + { /* Only print thead's deco if thead->mod differs from this->mod. + * Avoiding infinite recurtion by temporarily detatching ref member. + */ + assert(thead->ty == Trefsuffix && ((TypeRefSuffix *)thead)->next == this); + Type *savedhead = thead; + thead = NULL; + savedhead->toDecoBuffer(buf, flag); + thead = savedhead; + return; + } const char *name = sym->mangle(); //printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name); Type::toDecoBuffer(buf, flag); @@ -7322,6 +7484,17 @@ void TypeClass::toDecoBuffer(OutBuffer *buf, int flag) void TypeClass::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) { + if (thead && thead->mod != this->mod) + { /* Only print 'ref' marker if thead->mod differs from this->mod. + * Avoiding infinite recurtion by temporarily detatching ref member. + */ + assert(thead->ty == Trefsuffix && ((TypeRefSuffix *)thead)->next == this); + Type *savedhead = thead; + thead = NULL; + savedhead->toCBuffer2(buf, hgs, mod); + thead = savedhead; + return; + } if (mod != this->mod) { toCBuffer3(buf, hgs, mod); return; @@ -7682,9 +7855,50 @@ MATCH TypeClass::constConv(Type *to) return MATCHnomatch; } +Type *TypeClass::makeConst() +{ + return new TypeClass(this, MODconst); +} + +Type *TypeClass::makeInvariant() +{ + return new TypeClass(this, MODimmutable); +} + +Type *TypeClass::makeShared() +{ + return new TypeClass(this, MODshared); +} + +Type *TypeClass::makeSharedConst() +{ + return new TypeClass(this, MODshared | MODconst); +} +Type *TypeClass::makeWild() +{ + return new TypeClass(this, MODwild); +} + +Type *TypeClass::makeSharedWild() +{ + return new TypeClass(this, MODshared | MODwild); +} + +Type *TypeClass::makeUnShared() +{ + return new TypeClass(this, head()->mod & ~MODshared); +} + +Type *TypeClass::makeMutable() +{ + return new TypeClass(this, head()->mod & MODshared); +} + Type *TypeClass::toHeadMutable() { - return this; + if (!head()->mod) + return this; + return mutableOf(); } Expression *TypeClass::defaultInit(Loc loc) diff --git a/src/mtype.h b/src/mtype.h index c2b312b8eac4..9a9180592ad8 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -57,6 +57,7 @@ enum ENUMTY Taarray, // associative array, aka T[type] Tpointer, Treference, + Trefsuffix, // explicit ref suffix, aka Object ref Tfunction, Tident, Tclass, @@ -119,6 +120,11 @@ struct Type : Object #define MODwild 8 // type is wild #define MODmutable 0x10 // type is mutable (only used in wildcard matching) char *deco; + + /* Head type which holds reference modifiers for class type. + * Points to 'this' for other types. + */ + Type *thead; /* These are cached values that are lazily evaluated by constOf(), invariantOf(), etc. * They should not be referenced by anybody but mtype.c. @@ -218,6 +224,7 @@ struct Type : Object virtual Type *syntaxCopy(); int equals(Object *o); int dyncast() { return DYNCAST_TYPE; } // kludge for template.isType() + Type *head() { return thead ? thead : this; } int covariant(Type *t); char *toChars(); static char needThisPrefix(); @@ -277,6 +284,7 @@ struct Type : Object virtual Type *makeInvariant(); virtual Type *makeShared(); virtual Type *makeSharedConst(); + virtual Type *makeUnShared(); virtual Type *makeWild(); virtual Type *makeSharedWild(); virtual Type *makeMutable(); @@ -536,6 +544,15 @@ struct TypeReference : TypeNext #endif }; +struct TypeRefSuffix : TypeNext +{ + TypeRefSuffix(Type *t); + Type *syntaxCopy(); + Type *semantic(Loc loc, Scope *sc); + void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); +}; + enum RET { RETregs = 1, // returned in registers @@ -826,10 +843,19 @@ struct TypeClass : Type ClassDeclaration *sym; TypeClass(ClassDeclaration *sym); + TypeClass(TypeClass* tc, unsigned char headmod); d_uns64 size(Loc loc); char *toChars(); Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); + Type *makeConst(); + Type *makeInvariant(); + Type *makeShared(); + Type *makeSharedConst(); + Type *makeUnShared(); + Type *makeWild(); + Type *makeSharedWild(); + Type *makeMutable(); Dsymbol *toDsymbol(Scope *sc); void toDecoBuffer(OutBuffer *buf, int flag); void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); diff --git a/src/parse.c b/src/parse.c index fd7a2f62be57..9b2e664c73bf 100644 --- a/src/parse.c +++ b/src/parse.c @@ -2391,6 +2391,7 @@ Type *Parser::parseBasicType() * t [expression .. expression] * t function * t delegate + * t ref */ Type *Parser::parseBasicType2(Type *t) @@ -2404,6 +2405,14 @@ Type *Parser::parseBasicType2(Type *t) t = new TypePointer(t); nextToken(); continue; + + case TOKref: + // handle explicit reference marker for class: + // Object ref + // const(Object)ref + t = new TypeRefSuffix(t); + nextToken(); + continue; case TOKlbracket: // Handle []. Make sure things like @@ -4495,6 +4504,10 @@ int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok) //case TOKand: t = peek(t); continue; + + case TOKref: + t = peek(t); + continue; case TOKlbracket: t = peek(t); diff --git a/src/statement.c b/src/statement.c index 452331b7bceb..6e7b247a0167 100644 --- a/src/statement.c +++ b/src/statement.c @@ -1569,7 +1569,7 @@ Statement *ForeachStatement::semantic(Scope *sc) value = var; /* Reference to immutable data should be marked as const */ - if (var->storage_class & STCref && !tn->isMutable()) + if (var->storage_class & STCref && !tn->head()->isMutable()) { var->storage_class |= STCconst; } @@ -3615,7 +3615,7 @@ Statement *ReturnStatement::semantic(Scope *sc) if (((TypeFunction *)fd->type)->isref && !fd->isCtorDeclaration()) { // Function returns a reference - if (tbret->isMutable()) + if (tbret->head()->isMutable()) exp = exp->modifiableLvalue(sc, exp); else exp = exp->toLvalue(sc, exp); diff --git a/src/template.c b/src/template.c index 80c8b021f374..da28b4b20c7e 100644 --- a/src/template.c +++ b/src/template.c @@ -2607,6 +2607,22 @@ void deduceBaseClassParameters(BaseClass *b, MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeClass::deduceType(this = %s)\n", toChars()); + + /* If we have an explicit 'ref' suffix, we need to check + * that our head modifiers are compatible with those of that + * suffix. In the abscence of an explicit 'ref' suffix, we let + * normal type deduction choose what the ref's mod should + * be and thus don't have to check anything. + */ + if (tparam->ty == Trefsuffix) + { + TypeRefSuffix *refsuffix = (TypeRefSuffix *)tparam; + if (head()->mod != refsuffix->mod && !MODimplicitConv(head()->mod, refsuffix->mod)) + return MATCHnomatch; + + // now skip ref suffix + tparam = refsuffix->next; + } /* If this class is a template class, and we're matching * it against a template instance, convert the class type @@ -2697,6 +2713,7 @@ MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramet //printf("\t%d\n", (MATCH) implicitConvTo(tp)); return implicitConvTo(tp); } + return Type::deduceType(sc, tparam, parameters, dedtypes); } diff --git a/src/typinf.c b/src/typinf.c index 013bbfe1be88..6cccdf835a23 100644 --- a/src/typinf.c +++ b/src/typinf.c @@ -239,7 +239,11 @@ void TypeInfoConstDeclaration::toDt(dt_t **pdt) //printf("TypeInfoConstDeclaration::toDt() %s\n", toChars()); dtxoff(pdt, Type::typeinfoconst->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Const dtsize_t(pdt, 0); // monitor - Type *tm = tinfo->mutableOf(); + Type *tm; + if (tinfo->ty == Tclass) + tm = ((TypeClass *)tinfo)->sym->type; + else + tm = tinfo->mutableOf(); tm = tm->merge(); tm->getTypeInfo(NULL); dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); @@ -250,7 +254,11 @@ void TypeInfoInvariantDeclaration::toDt(dt_t **pdt) //printf("TypeInfoInvariantDeclaration::toDt() %s\n", toChars()); dtxoff(pdt, Type::typeinfoinvariant->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Invariant dtsize_t(pdt, 0); // monitor - Type *tm = tinfo->mutableOf(); + Type *tm; + if (tinfo->ty == Tclass) + tm = ((TypeClass *)tinfo)->sym->type; + else + tm = tinfo->mutableOf(); tm = tm->merge(); tm->getTypeInfo(NULL); dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); @@ -261,7 +269,13 @@ void TypeInfoSharedDeclaration::toDt(dt_t **pdt) //printf("TypeInfoSharedDeclaration::toDt() %s\n", toChars()); dtxoff(pdt, Type::typeinfoshared->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Shared dtsize_t(pdt, 0); // monitor - Type *tm = tinfo->unSharedOf(); + Type *tm; + if (tinfo->ty == Tclass) + { tm = ((TypeClass *)tinfo)->sym->type; + tm = tm->addMod(tinfo->mod & ~MODshared); // propagate other modifiers + } + else + tm = tinfo->unSharedOf(); tm = tm->merge(); tm->getTypeInfo(NULL); dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); @@ -272,7 +286,11 @@ void TypeInfoWildDeclaration::toDt(dt_t **pdt) //printf("TypeInfoWildDeclaration::toDt() %s\n", toChars()); dtxoff(pdt, Type::typeinfowild->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Wild dtsize_t(pdt, 0); // monitor - Type *tm = tinfo->mutableOf(); + Type *tm; + if (tinfo->ty == Tclass) + tm = ((TypeClass *)tinfo)->sym->type; + else + tm = tinfo->mutableOf(); tm = tm->merge(); tm->getTypeInfo(NULL); dtxoff(pdt, tm->vtinfo->toSymbol(), 0, TYnptr); diff --git a/test/compilable/objconst1.d b/test/compilable/objconst1.d new file mode 100644 index 000000000000..bfa54b5bd6cc --- /dev/null +++ b/test/compilable/objconst1.d @@ -0,0 +1,7 @@ + +void main() +{ + const(Object)ref copy = new const(Object)ref; + const(Object) value = new const(Object)ref; + copy = value; +} \ No newline at end of file diff --git a/test/compilable/objconst10.d b/test/compilable/objconst10.d new file mode 100644 index 000000000000..5252cfda9b28 --- /dev/null +++ b/test/compilable/objconst10.d @@ -0,0 +1,43 @@ + +class C { + + const(C) constTest() const { return this; } + immutable(C) immutableTest() immutable { return this; } + shared(C) sharedTest() shared { return this; } + shared(const(C)) sharedConstTest() shared const { return this; } + inout(C) inoutTest(inout(C) a) { return a; } + + const(C)ref constTestRef() const { return this; } + immutable(C)ref immutableTestRef() immutable { return this; } + shared(C)ref sharedTestRef() shared { return this; } + shared(const(C))ref sharedConstTestRef() shared const { return this; } + inout(C)ref inoutTestRef(inout(C)ref a) { return a; } + +} + +void main() { + C a = new C; + const(C)ref ac; + ac = a.constTest(); + ac = a.constTestRef(); +// a = a.inoutTest(a); +// a = a.inoutTestRef(a); + + immutable(C)ref b = new immutable(C); + const(C)ref bc; + bc = b.constTest(); + bc = b.constTestRef(); +// b = b.inoutTest(b); +// b = b.inoutTestRef(b); + b = b.immutableTest(); + b = b.immutableTestRef(); + + shared(C)ref c = new shared(C); + shared(const(C))ref cc; + cc = c.sharedConstTest(); + cc = c.sharedConstTestRef(); +// c = c.inoutTest(c); +// c = c.inoutTestRef(c); + c = c.sharedTest(); + c = c.sharedTestRef(); +} diff --git a/test/compilable/objconst11unqual.d b/test/compilable/objconst11unqual.d new file mode 100644 index 000000000000..be664855d1ed --- /dev/null +++ b/test/compilable/objconst11unqual.d @@ -0,0 +1,15 @@ + +template Unqual(T) +{ + static if (is(T U == shared(const U))) alias U Unqual; + else static if (is(T U == const U )) alias U Unqual; + else static if (is(T U == immutable U )) alias U Unqual; + else static if (is(T U == shared U )) alias U Unqual; + else alias T Unqual; +} + +static assert(is(Unqual!(const(Object)) == const(Object)ref)); +static assert(is(Unqual!(const(Object ref)) == const(Object)ref)); +static assert(is(Unqual!(const(immutable(Object)ref)) == immutable(Object)ref)); +static assert(!is(Unqual!(const(Object)) == const(Object))); +static assert(!is(Unqual!(const(Object)) == Object)); diff --git a/test/compilable/objconst2.d b/test/compilable/objconst2.d new file mode 100644 index 000000000000..b29181859a33 --- /dev/null +++ b/test/compilable/objconst2.d @@ -0,0 +1,7 @@ + +void main() +{ + const(Object)ref[] copy = new const(Object)ref[12]; + const(Object)[] value = new const(Object)ref[12]; + copy[] = value[]; +} \ No newline at end of file diff --git a/test/compilable/objconst3.d b/test/compilable/objconst3.d new file mode 100644 index 000000000000..77ecc74752e9 --- /dev/null +++ b/test/compilable/objconst3.d @@ -0,0 +1,10 @@ + +void main() +{ + const(Object)ref[] objects = new const(Object)ref[12]; + foreach (ref object; objects) + { + static assert(is(typeof(object) == const(Object)ref)); + object = new Object; + } +} \ No newline at end of file diff --git a/test/compilable/objconst4.d b/test/compilable/objconst4.d new file mode 100644 index 000000000000..c987ce0ffd40 --- /dev/null +++ b/test/compilable/objconst4.d @@ -0,0 +1,11 @@ + +struct S +{ + const(Object)ref o; +} + +void main() +{ + S s; + s.o = new Object; +} \ No newline at end of file diff --git a/test/compilable/objconst5.d b/test/compilable/objconst5.d new file mode 100644 index 000000000000..45435910d7d8 --- /dev/null +++ b/test/compilable/objconst5.d @@ -0,0 +1,6 @@ + +void main() +{ + auto o = cast(const(Object)ref)(new const(Object)); + static assert(is(typeof(o) == const(Object)ref)); +} \ No newline at end of file diff --git a/test/compilable/objconst6.d b/test/compilable/objconst6.d new file mode 100644 index 000000000000..3d23917e34fb --- /dev/null +++ b/test/compilable/objconst6.d @@ -0,0 +1,10 @@ + +const(Object)ref test() { + return new Object; +} + +void main() { + auto obj = test(); + static assert(is(typeof(obj) == const(Object)ref)); + static assert(!is(typeof(obj) == const(Object))); +} \ No newline at end of file diff --git a/test/compilable/objconst7mangleof.d b/test/compilable/objconst7mangleof.d new file mode 100644 index 000000000000..38142d058dbd --- /dev/null +++ b/test/compilable/objconst7mangleof.d @@ -0,0 +1,19 @@ + +// Mangling with no 'ref' suffix is be preserved +static assert((shared(Object)).mangleof == "OC6Object"); +static assert((const(Object)).mangleof == "xC6Object"); +static assert((immutable(Object)).mangleof == "yC6Object"); +static assert((shared(const(Object))).mangleof == "OxC6Object"); +static assert((inout(Object)).mangleof == "NgC6Object"); +static assert((shared(inout(Object))).mangleof == "ONgC6Object"); + +// Mangling with 'ref' suffix: the 'X' represents the suffix and is only present +// when the reference's modifiers is different from those of the class. +static assert((const(Object)ref).mangleof == "XxC6Object"); +static assert((const(immutable(Object)ref)).mangleof == "xXyC6Object"); +static assert((shared(inout(Object)ref)).mangleof == "OXOxC6Object"); + +// Reference suffix is not mangled when it has the same modifiers +static assert((const(Object ref)).mangleof == "xC6Object"); +static assert((inout(Object ref)).mangleof == "NgC6Object"); + diff --git a/test/compilable/objconst8ptr.d b/test/compilable/objconst8ptr.d new file mode 100644 index 000000000000..cf4ed123dc39 --- /dev/null +++ b/test/compilable/objconst8ptr.d @@ -0,0 +1,6 @@ + +void main() +{ + const(Object)ref* p = (new const(Object)ref[1]).ptr; // hard to get a new "const(Object)ref*" + *p = new Object; +} \ No newline at end of file diff --git a/test/compilable/objconst9tmpl.d b/test/compilable/objconst9tmpl.d new file mode 100644 index 000000000000..3450c56f25ea --- /dev/null +++ b/test/compilable/objconst9tmpl.d @@ -0,0 +1,156 @@ + +// Template matching + +template a(T : Object) { + enum a = 1; +} + +static assert(a!(Object)); +static assert(!is(typeof(a!(const(Object))))); // no match +static assert(!is(typeof(a!(const(Object)ref)))); // no match +static assert(!is(typeof(a!(immutable(Object))))); // no match +static assert(!is(typeof(a!(immutable(Object)ref)))); // no match +static assert(!is(typeof(a!(shared(Object))))); // no match +static assert(!is(typeof(a!(shared(Object)ref)))); // no match + +template ap(T : int*) { + enum ap = 1; +} + +static assert(ap!(int*)); +static assert(ap!(const(int*))); // FIXME: should not match +static assert(ap!(const(int)*)); // FIXME: should not match +static assert(ap!(immutable(int*))); // FIXME: should not match +static assert(ap!(immutable(int)*)); // FIXME: should not match +static assert(ap!(shared(int*))); // FIXME: should not match +static assert(ap!(shared(int)*)); // FIXME: should not match + + +template b(T : const(Object)) { + enum b = 1; +} + +static assert(b!(Object)); +static assert(b!(const(Object))); +static assert(b!(const(Object)ref)); +static assert(b!(immutable(Object))); +static assert(b!(immutable(Object)ref)); +static assert(!is(typeof(b!(shared(Object))))); // no match +static assert(!is(typeof(b!(shared(Object)ref)))); // no match + +template bp(T : const(int*)) { + enum bp = 1; +} + +static assert(bp!(int*)); +static assert(bp!(const(int*))); +static assert(bp!(const(int)*)); +static assert(bp!(immutable(int*))); +static assert(bp!(immutable(int)*)); +static assert(bp!(shared(int*))); // FIXME: should not match +static assert(bp!(shared(int)*)); // FIXME: should not match + + +template c(T : const(Object)ref) { + enum c = 1; +} + +static assert(c!(Object)); +static assert(c!(const(Object))); // FIXME: should not match (const ref) +static assert(c!(const(Object)ref)); +static assert(c!(immutable(Object))); // FIXME: should not match (immutable ref) +static assert(c!(immutable(Object)ref)); +static assert(!is(typeof(c!(shared(Object))))); // no match +static assert(!is(typeof(c!(shared(Object)ref)))); // no match + +template cp(T : const(int)*) { + enum cp = 1; +} + +static assert(cp!(int*)); +static assert(cp!(const(int*))); // FIXME: should not match (const ptr) +static assert(cp!(const(int)*)); +static assert(cp!(immutable(int*))); // FIXME: should not match (immutable ptr) +static assert(cp!(immutable(int)*)); +static assert(cp!(shared(int*))); // FIXME: should not match +static assert(cp!(shared(int)*)); // FIXME: should not match + + +// Type deduction + +template d(T : U ref, U) { + alias U d; +} + +static assert(is(d!(Object) == Object)); +static assert(!is(d!(const(Object)))); // no match (const ref) +static assert(is(d!(const(Object)ref) == const(Object)ref)); +static assert(!is(d!(immutable(Object)))); // no match (immutable ref) +static assert(is(d!(immutable(Object)ref) == immutable(Object)ref)); +static assert(!is(d!(shared(Object)))); // no match (shared ref) +static assert(is(d!(shared(Object)ref) == shared(Object)ref)); + +static assert(!is(d!(int))); // no match: 'ref' prevents matching non-class + +template dp(T : U*, U) { + alias U dp; +} + +static assert(is(dp!(int*) == int)); +static assert(is(dp!(const(int*)) == const(int))); // FIXME: should not match (const ptr) +static assert(is(dp!(const(int)*) == const(int))); +static assert(is(dp!(immutable(int*)) == immutable(int))); // FIXME: should not match (immutable ptr) +static assert(is(dp!(immutable(int)*) == immutable(int))); +static assert(is(dp!(shared(int*)) == shared(int))); // FIXME: should not match (shared ptr) +static assert(is(dp!(shared(int)*) == shared(int))); + + +template e(T : const(U), U) { + alias U e; +} + +static assert(is(e!(Object) == Object)); +static assert(is(e!(const(Object)) == const(Object)ref)); // FIXME: should == Object +static assert(is(e!(const(Object)ref) == const(Object)ref)); // FIXME: should == Object +static assert(is(e!(immutable(Object)) == immutable(Object)ref)); // FIXME: should == Object +static assert(is(e!(immutable(Object)ref) == immutable(Object)ref)); // FIXME: should == Object +static assert(!is(e!(shared(Object)))); // no match +static assert(!is(e!(shared(Object)ref))); // no match + +template ep(T : const(U*), U) { + alias U ep; +} + +static assert(is(ep!(int*) == int)); +static assert(is(ep!(const(int*)) == int)); +static assert(is(ep!(const(int)*) == int)); +static assert(is(ep!(immutable(int*)) == int)); +static assert(is(ep!(immutable(int)*) == int)); +static assert(!is(ep!(shared(int*)))); // no match +static assert(!is(ep!(shared(int)*))); // no match + + +template f(T : const(U)ref, U) { + alias U f; +} + +static assert(is(f!(Object) == Object)); +static assert(!is(f!(const(Object)))); // no match (const ref) +static assert(is(f!(const(Object)ref) == const(Object)ref)); // FIXME: should == Object +static assert(!is(f!(immutable(Object)))); // no match (immutable ref) +static assert(is(f!(immutable(Object)ref) == immutable(Object)ref)); // FIXME: should == Object +static assert(!is(f!(shared(Object)))); // no match +static assert(!is(f!(shared(Object)ref))); // no match + +template fp(T : const(U)*, U) { + alias U fp; +} + +static assert(is(fp!(int*) == int)); +static assert(is(fp!(const(int*)) == int)); // FIXME: should not match (const ptr) +static assert(is(fp!(const(int)*) == int)); +static assert(is(fp!(immutable(int*)) == int)); // FIXME: should not match (immutable ref) +static assert(is(fp!(immutable(int)*) == int)); +static assert(!is(fp!(shared(int*)))); // no match +static assert(!is(fp!(shared(int)*))); // no match + diff --git a/test/fail_compilation/objconst1fail.d b/test/fail_compilation/objconst1fail.d new file mode 100644 index 000000000000..5a8d4cb97133 --- /dev/null +++ b/test/fail_compilation/objconst1fail.d @@ -0,0 +1,7 @@ + +void main() +{ + const(Object) copy; + const(Object) value; + copy = value; +} \ No newline at end of file diff --git a/test/fail_compilation/objconst2fail.d b/test/fail_compilation/objconst2fail.d new file mode 100644 index 000000000000..ffe0901411f4 --- /dev/null +++ b/test/fail_compilation/objconst2fail.d @@ -0,0 +1,7 @@ + +void main() +{ + const(Object)[] copy = new const(Object)ref[12]; + const(Object)[] value = new const(Object)ref[12]; + copy[] = value[]; +} \ No newline at end of file diff --git a/test/runnable/objconst1stringof.d b/test/runnable/objconst1stringof.d new file mode 100644 index 000000000000..ba9a856411d7 --- /dev/null +++ b/test/runnable/objconst1stringof.d @@ -0,0 +1,7 @@ + +void main() +{ + assert((const(Object)ref).stringof == "const(Object)ref"); + assert((const(Object ref)).stringof == "const(Object)"); + assert((const(Object)).stringof == "const(Object)"); +} \ No newline at end of file diff --git a/test/runnable/objconst2typeinfo.d b/test/runnable/objconst2typeinfo.d new file mode 100644 index 000000000000..c89fc3e46130 --- /dev/null +++ b/test/runnable/objconst2typeinfo.d @@ -0,0 +1,14 @@ + +void main() +{ + // This tests wehther typeinfo was generated correctly. + typeid(const(Object)ref).toString(); + typeid(immutable(Object)ref).toString(); + typeid(shared(Object)ref).toString(); + typeid(shared(const(Object))ref).toString(); + typeid(inout(Object)ref).toString(); + typeid(inout(shared(Object))ref).toString(); + typeid(inout(shared(Object)ref)).toString(); + typeid(const(shared(Object)ref)).toString(); + typeid(shared(immutable(Object)ref)).toString(); +} \ No newline at end of file diff --git a/test/runnable/testaa.d b/test/runnable/testaa.d index b3d1fc1f555a..99b79900d971 100644 --- a/test/runnable/testaa.d +++ b/test/runnable/testaa.d @@ -173,7 +173,7 @@ void test10() { auto key = new immutable(A10)[2]; - cast()(key[0]) = new A10(); + cast()(key[0]) = new immutable(A10)(); foo10[key] = 0; assert(key in foo10); assert(!(key !in foo10)); diff --git a/test/runnable/testsafe.d b/test/runnable/testsafe.d index dd17e35e4e7a..55a431c72717 100644 --- a/test/runnable/testsafe.d +++ b/test/runnable/testsafe.d @@ -237,11 +237,11 @@ void sharedcast() shared(Object) xshared; immutable(Object) ishared; - static assert(!__traits(compiles, cast()xshared)); - static assert(!__traits(compiles, cast(shared)local)); + static assert(!__traits(compiles, cast(Object)xshared)); + static assert(!__traits(compiles, cast(shared(Object)ref)local)); - static assert(!__traits(compiles, cast(immutable)xshared)); - static assert(!__traits(compiles, cast(shared)ishared)); + static assert(!__traits(compiles, cast(immutable(Object)ref)xshared)); + static assert(!__traits(compiles, cast(shared(Object)ref)ishared)); } int threadlocalvar;