diff --git a/src/ddmd/clone.d b/src/ddmd/clone.d index 9b22142bf33c..964791c4efc3 100644 --- a/src/ddmd/clone.d +++ b/src/ddmd/clone.d @@ -766,6 +766,7 @@ extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) * all the members. * Note the close similarity with AggregateDeclaration::buildDtor(), * and the ordering changes (runs forward instead of backwards). + * buildInv() should be run first. */ extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) { @@ -1088,6 +1089,13 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) switch (ad.dtors.dim) { case 0: + /* If running invariant, need a destructor to hang it on, + * but only do for root modules, as ones in the library may not + * have been compiled with useInvariants + */ + if (global.params.useInvariants && ad.inv && sc._module.isRoot()) + goto Ldefault; + break; case 1: @@ -1095,6 +1103,7 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) break; default: + Ldefault: e = null; stc = STCsafe | STCnothrow | STCpure | STCnogc; for (size_t i = 0; i < ad.dtors.dim; i++) diff --git a/src/ddmd/dclass.d b/src/ddmd/dclass.d index b89f85e15b97..4b1931441a08 100644 --- a/src/ddmd/dclass.d +++ b/src/ddmd/dclass.d @@ -950,6 +950,29 @@ extern (C++) class ClassDeclaration : AggregateDeclaration } } + version (none) // Controversial - see Bugzilla 519 + { + /* If class has no constructor, but does have an invariant, create a default constructor merely + * as a place where the invariant call will be added. + */ + if (!ctor && global.params.useInvariants && inv && !isCPPclass()) + { + auto tf = new TypeFunction(null, null, 0, LINKd, inv.storage_class); + auto btf = cast(TypeFunction)inv.type; + tf.purity = btf.purity; + tf.isnothrow = btf.isnothrow; + tf.isnogc = btf.isnogc; + tf.trust = btf.trust; + auto ctor = new CtorDeclaration(loc, Loc(), 0, tf); + ctor.fbody = new CompoundStatement(Loc(), new Statements()); + members.push(ctor); + ctor.addMember(sc, this, 1); + ctor.semantic(sc2); + this.ctor = ctor; + defaultCtor = ctor; + } + } + dtor = buildDtor(this, sc2); if (auto f = hasIdentityOpAssign(this, sc2)) diff --git a/src/ddmd/dstruct.d b/src/ddmd/dstruct.d index 2c6808d35830..74a2b8da5219 100644 --- a/src/ddmd/dstruct.d +++ b/src/ddmd/dstruct.d @@ -403,6 +403,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration */ aggNew = cast(NewDeclaration)search(Loc(), Id.classNew); aggDelete = cast(DeleteDeclaration)search(Loc(), Id.classDelete); + inv = buildInv(this, sc2); // Look for the constructor ctor = searchCtor(); @@ -417,8 +418,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration xcmp = buildXopCmp(this, sc2); xhash = buildXtoHash(this, sc2); - inv = buildInv(this, sc2); - Module.dprogress++; semanticRun = PASSsemanticdone; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); diff --git a/src/ddmd/e2ir.d b/src/ddmd/e2ir.d index 47290e78a3f4..625b1ba5e7ad 100644 --- a/src/ddmd/e2ir.d +++ b/src/ddmd/e2ir.d @@ -5686,6 +5686,18 @@ private elem *toElemStructLit(StructLiteralExp sle, IRState *irs, TOK op, Symbol e = el_combine(e, e1); } + if (global.params.useInvariants && sle.sd.inv) + { + /* If there are no arguments, then this is default initialization and shouldn't call invariant. + */ + if (dim) + { + FuncDeclaration inv = sle.sd.inv; + elem *einv = callfunc(sle.loc, irs, 1, inv.type.nextOf(), el_ptr(stmp), sle.sd.type.pointerTo(), inv, inv.type, null, null); + e = el_combine(e, einv); + } + } + elem *ev = el_var(stmp); ev.ET = Type_toCtype(sle.sd.type); e = el_combine(e, ev); diff --git a/test/runnable/testdstress.d b/test/runnable/testdstress.d index ee36d345b1d8..f6160116409b 100644 --- a/test/runnable/testdstress.d +++ b/test/runnable/testdstress.d @@ -809,10 +809,12 @@ class Parent40{ invariant() { - assert(!checked40); + if (!checked40) + { checked40=true; // even number assert((x&1u)==0); + } } } diff --git a/test/runnable/testinvariant.d b/test/runnable/testinvariant.d index 93d66efd21ae..19cb4f3757e6 100644 --- a/test/runnable/testinvariant.d +++ b/test/runnable/testinvariant.d @@ -177,6 +177,75 @@ void test13147() } +/***************************************************/ + +class C519 { + invariant { + ++x; + } + __gshared int x; +} + +void test519a() { + auto s = new C519(); + assert(C519.x == 0); + delete s; + assert(C519.x == 1); +} + +/***************************************************/ + +struct S519 { + invariant() { + printf("S519.invariant\n"); + ++x; + } + __gshared int x; +} + +void test519b() { + { + auto foo = new S519(); + printf("lifetime of foo\n"); + delete foo; + assert(S519.x == 1); + } + printf("test2\n"); + { + auto foo = S519(); + printf("lifetime of foo\n"); + } + assert(S519.x == 2); +} + + +/***************************************************/ + +struct S519c { + invariant() { + printf("S519c.invariant\n"); + ++x; + } + __gshared int x; + int y; +} + +void test519c() { + { + auto foo = new S519c(1); + printf("lifetime of foo\n"); + delete foo; + assert(S519c.x == 2); + } + printf("test2\n"); + { + auto foo = S519c(1); + printf("lifetime of foo\n"); + } + assert(S519c.x == 4); +} + + /***************************************************/ void main() @@ -185,4 +254,7 @@ void main() test6453(); test13113(); test13147(); + test519a(); + test519b(); + test519c(); }