import std.stdio;
// 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){
if( count ){
assert(magic == 0x2f7d9a65);
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;
}
}
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; }
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()
{
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.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;
}