// { dg-do run  }
//
// Purpose: Check the lifetime of the temporaries.
//
// Lifetime of temporaries:
// egcs 2.92 performs cleanup for temporaries inside new expressions
// after the new is complete, not at the end of the full expression.
//
// In GCC, the operands are computed first.  If no exception is raised, then
// the result should be "ctor, new, func, dtor".  If the new operator throws
// the exception, then the result should be "ctor, new, dtor".  If the
// constructor of the temporary throws the exception, then the result should
// be "ctor".
//
// In Clang, the new operator is called first.  If no exception is raised,
// then the result should be "new, ctor, func, dtor".  If the new operator
// throws the exception, then the result should be "new".  If the constructor
// of the temporary throws the exception, then the result should be
// "new, ctor, delete".
//
// Both of them are allowed by the C++ language specification, so we are using
// #ifdef for different compilers.

#include <new>
#include <cstdlib>
#include <cstdio>

bool new_throws;
bool ctor_throws;

int new_done;
int ctor_done;
int func_done;
int dtor_done;
int delete_done;

int count;

void init()
{
  new_throws = ctor_throws = false;
  new_done = ctor_done = func_done = dtor_done = delete_done = count = 0;
}

struct line_error{
  int line;
  line_error(int i):line(i){}
};

#define CHECK(cond)  if(!(cond))throw line_error(__LINE__);

struct A{
  A(int){
    ctor_done = ++count;
    if(ctor_throws)
      throw 1;
  }
  A(const A&){
    CHECK(false); //no copy constructors in this code
  }
  ~A(){
    dtor_done = ++count;
  }
  A* addr(){return this;}
};

struct B{
  B(A*){}
  void* operator new(size_t s){
    new_done = ++count;
    if(new_throws)
      throw 1;
    return malloc(s);
  }
  void operator delete(void *){
    delete_done = ++count;
  }
};

void func(B* )
{
  func_done = ++count;
}

void test1()
{
  init();
  try{
    func(new B(A(10).addr()));
  }catch(int){
  }
#if defined(__clang__)
  CHECK(new_done==1);
  CHECK(ctor_done==2);
  CHECK(func_done==3);
  CHECK(dtor_done==4);
  CHECK(delete_done==0);
#elif defined(__GNUC__)
  CHECK(ctor_done==1);
  CHECK(new_done==2);
  CHECK(func_done==3);
  CHECK(dtor_done==4);
  CHECK(delete_done==0);
#else
#error "Unknown compiler"
#endif
}

void test2()
{
  init();
  new_throws = true;
  try{
    func(new B(A(10).addr()));
  }catch(int){
  }
#if defined(__clang__)
  CHECK(new_done==1);
  CHECK(ctor_done==0);
  CHECK(func_done==0);
  CHECK(dtor_done==0);
  CHECK(delete_done==0);
#elif defined(__GNUC__)
  CHECK(ctor_done==1);
  CHECK(new_done==2);
  CHECK(func_done==0);
  CHECK(dtor_done==3);
  CHECK(delete_done==0);
#else
#error "Unknown compiler"
#endif
}

void test3()
{
  init();
  ctor_throws = true;
  try{
    func(new B(A(10).addr()));
  }catch(int){
  }
#if defined(__clang__)
  CHECK(new_done==1);
  CHECK(ctor_done==2);
  CHECK(func_done==0);
  CHECK(dtor_done==0);
  CHECK(delete_done==3);
#elif defined(__GNUC__)
  CHECK(new_done==0);
  CHECK(ctor_done==1);
  CHECK(func_done==0);
  CHECK(dtor_done==0);
  CHECK(delete_done==0);
#else
#error "Unknown compiler"
#endif
}

int main()
{
  try{
    test1();
    test2();
    test3();
  }catch(line_error e){
    printf("Got error in line %d\n",e.line);
    return 1;
  }
  return 0;
}