import std.stdio; //version=ASSIGNMENT_OPERATOR1; version=ASSIGNMENT_OPERATOR2; extern(C) void memcpy(void* dst, const void* src, size_t cnt); // keep track of all living objects bool[int*] s_living; // canonical reference count dummy implementation struct Count { int* count; int magic = 0x2f7d9a65; this(bool){ count = new int; *count = 1; assert(count !in s_living); s_living[count] = true; } this(this){ assert(magic == 0x2f7d9a65); if( count ){ assert(*count > 0); (*count)++; } } ~this(){ assert(magic == 0x2f7d9a65); if( count ){ assert(*count > 0); if( --(*count) == 0){ assert(count in s_living); s_living.remove(count); count = null; } } // reset just to catch uninitialized-errors better count = null; magic = 0; } version(ASSIGNMENT_OPERATOR1) { void opAssign(Count c) { assert(&this != &c); // note: c is passed by value assert(magic == 0x2f7d9a65); assert(c.magic == 0x2f7d9a65); typeid(Count).destroy(&this); memcpy(&this, &c, Count.sizeof); typeid(Count).postblit(&this); } } version(ASSIGNMENT_OPERATOR2) { void opAssign(Count c) { auto tmp = count; count = c.count; c.count = tmp; } } } void test_con_ass() { Count c; { c = Count(true); } assert(c.count && *c.count == 1); { c = Count(); } assert(c.count == null); } void test_con_sae_ass() { Count c[1]; { c[0] = Count(true); } assert(*c[0].count == 1); { c[0] = Count(); } assert(c[0].count == null); } void test_con_ae_ass1() { Count c[] = new Count[1]; { c[0] = Count(true); } assert(c[0].count && *c[0].count == 1); { c[0] = Count(); } assert(c[0].count == null); } void test_con_ae_ass2() { Count c[]; { c ~= Count(true); } assert(c[0].count && *c[0].count == 1); { c[0] = Count(); } assert(c[0].count == null); { c ~= Count(true); } assert(c[0].count && *c[0].count == 1); Count tmp = c[0]; assert(c[0].count && *c[0].count == 2); //{ c.length = 0; } { c[0] = Count(); c.length = 0; } assert(c[0].count && *c[0].count == 1); tmp = Count(); assert(c[0].count == null); } void test_con_aa_ass1() // DMD 6178? { Count[int] c; { auto cs = Count(true); c[0] = cs; } assert(c[0].count && *c[0].count == 1); { Count cs; c[0] = cs; } assert(c[0].count == null); } void test_con_aa_ass2() // DMD 6178? { Count[int] c; { c[0] = Count(true); } assert(c[0].count && *c[0].count == 1); { c[0] = Count(); } assert(c[0].count == null); } void test_con_sm_ass() { struct S { Count c; } S s; { s.c = Count(true); } assert(s.c.count && *s.c.count == 1); { s.c = Count(); } assert(s.c.count == null); } void test_con_cm_ass() { class S { Count c; } S s = new S; { s.c = Count(true); } assert(s.c.count && *s.c.count == 1); { s.c = Count(); } assert(s.c.count == null); } void test_con_delp() { Count c; void f(Count c_){ c = c_; } { f(Count(true)); } assert(c.count && *c.count == 1); { f(Count()); } assert(c.count == null); } void test_con_refp() { Count c; static void f(ref Count cd, Count cs){ cd = cs; } { f(c, Count(true)); } assert(c.count && *c.count == 1); { f(c, Count()); } assert(c.count == null); } void test_con_refp2() { Count c; static void f(ref Count cd, ref Count cs){ cd = cs; } { auto cs = Count(true); f(c, cs); } assert(c.count && *c.count == 1); { Count cs; f(c, cs); } assert(c.count == null); } void test_con_ptrp() { Count c; static void f(Count* cd, Count cs){ *cd = cs; } { f(&c, Count(true)); } assert(c.count && *c.count == 1); { f(&c, Count()); } assert(c.count == null); } void test_con_ptrp2() { Count c; static void f(Count* cd, Count* cs){ *cd = *cs; } { auto cs = Count(true); f(&c, &cs); } assert(c.count && *c.count == 1); { Count cs; f(&c, &cs); } assert(c.count == null); } void test_con_outp() { Count c; static void f(out Count cd, Count cs){ cd = cs; } { f(c, Count(true)); } assert(c.count && *c.count == 1); { f(c, Count()); } assert(c.count == null); } void test_con_ret() { Count c; static Count a(){ return Count(true); } static Count b(){ return Count(); } { c = a(); } assert(c.count && *c.count == 1); { c = b(); } assert(c.count == null); } void test_var_ret() { Count c; static Count a(bool x = true){ static Count cs; if( x && cs.count == null ) cs = Count(true); else if( !x && cs.count != null ) cs = Count(); return cs; } static Count b(){ return a(); } { c = a(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); { c = b(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); a(false); // frees the static reference } void test_sae_ret() // DMD { Count c; static Count a(bool x = true){ static Count[1] cs; if( x && cs[0].count == null ) cs[0] = Count(true); else if( !x && cs[0].count != null ) cs[0] = Count(); return cs[0]; } { c = a(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); a(false); // frees the static reference } void test_ae_ret() // DMD 7530 { Count c; static Count a(bool x = true){ static Count[] cs; if( x && cs.length == 0 ) cs ~= Count(true); else if( !x && cs.length > 0 ){ cs[0] = Count(); cs.length = 0; } return cs[0]; } { c = a(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); a(false); // frees the static reference } void test_aae_ret() { Count c; static Count a(bool x = true){ static Count[int] cs; if( x && 0 !in cs ) cs[0] = Count(true); else if( !x && 0 in cs ) cs.remove(0); return cs[0]; } { c = a(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); a(false); // frees the static reference } void test_sm_ret() { Count c; struct S { Count c; } static Count a(bool x = true){ static S cs; if( x && cs.c.count == null ) cs.c = Count(true); else if( !x && cs.c.count != null ) cs.c = Count(); return cs.c; } { c = a(); } assert(c.count && *c.count == 2); { c = Count(); } assert(c.count && *c.count == 1); a(false); // frees the static reference } void test_tern_ret() // DMD 7516 { Count c; static Count a(bool x, bool y = true){ static Count cs; if( y && cs.count == null ) cs = Count(true); else if( !y && cs.count != null ) cs = Count(); return x ? cs : Count(0); } static Count b(bool x){ return x ? a(x) : Count(); } { c = a(true); } assert(c.count && *c.count == 2); { c = a(false); } assert(c.count && *c.count == 1); { c = b(true); } assert(c.count && *c.count == 2); { c = b(false); } assert(c.count && *c.count == 1); a(false, false); // frees the static reference } void test_str_ass() { struct S { Count c; } S s, t; { s.c = Count(true); } assert(s.c.count && *s.c.count == 1); { t = s; } assert(s.c.count && *s.c.count == 2); assert(t.c.count && *t.c.count == 2); { t.c = Count(); } assert(s.c.count && *s.c.count == 1); { s = t; } assert(s.c.count == null); assert(t.c.count == null); } void test_str_sa_ass() { struct S { Count[2] c; } S s, t; { s.c[0] = Count(true); } assert(s.c[0].count && *s.c[0].count == 1); { t = s; } assert(s.c[0].count && *s.c[0].count == 2); assert(t.c[0].count && *t.c[0].count == 2); { t.c[0] = Count(); } assert(s.c[0].count && *s.c[0].count == 1); { s = t; } assert(s.c[0].count == null); assert(t.c[0].count == null); } @property void test(alias F)() { import std.stdio; writef("Test %s: ", F.stringof); version(BAILOUT){ F(); assert(s_living.length == 0); } else { try { F(); if( s_living.length > 0 ){ writeln("FAIL (leak)"); } else writeln("OK"); s_living = null; } catch(Throwable th){ writeln("FAIL"); } } } int main() { test!test_con_ass; test!test_con_sae_ass; test!test_con_ae_ass1; test!test_con_ae_ass2; test!test_con_aa_ass1; test!test_con_aa_ass2; test!test_con_sm_ass; test!test_con_cm_ass; test!test_con_delp; test!test_con_refp; test!test_con_refp2; test!test_con_ptrp; test!test_con_ptrp2; test!test_con_outp; test!test_con_ret; test!test_var_ret; test!test_sae_ret; test!test_aae_ret; test!test_sm_ret; test!test_tern_ret; test!test_str_ass; test!test_str_sa_ass; return 1; }