/* This testcase is part of GDB, the GNU debugger.

   Copyright 2019-2022 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

class ByVal {
public:
  ByVal (void);

  int x;
};

ByVal::ByVal (void)
{
  x = 2;
}

class ByRef {
public:
  ByRef (void);

  ByRef (const ByRef &rhs);

  int x;
};

ByRef::ByRef (void)
{
  x = 2;
}

ByRef::ByRef (const ByRef &rhs)
{
  x = 3; /* ByRef-cctor */
}

class ArrayContainerByVal {
public:
  ByVal items[2];
};

int
cbvArrayContainerByVal (ArrayContainerByVal arg)
{
  arg.items[0].x += 4;  // intentionally modify
  return arg.items[0].x;
}

class ArrayContainerByRef {
public:
  ByRef items[2];
};

int
cbvArrayContainerByRef (ArrayContainerByRef arg)
{
  arg.items[0].x += 4;  // intentionally modify
  return arg.items[0].x;
}

class DynamicBase {
public:
  DynamicBase (void);

  virtual int get (void);

  int x;
};

DynamicBase::DynamicBase (void)
{
  x = 2;
}

int
DynamicBase::get (void)
{
  return 42;
}

class Dynamic : public DynamicBase {
public:
  virtual int get (void);
};

int
Dynamic::get (void)
{
  return 9999;
}

int
cbvDynamic (DynamicBase arg)
{
  arg.x += 4;  // intentionally modify
  return arg.x + arg.get ();
}

class Inlined {
public:
  Inlined (void);

  __attribute__((always_inline))
  Inlined (const Inlined &rhs)
  {
    x = 3;
  }

  int x;
};

Inlined::Inlined (void)
{
  x = 2;
}

int
cbvInlined (Inlined arg)
{
  arg.x += 4;  // intentionally modify
  return arg.x;
}

class DtorDel {
public:
  DtorDel (void);

  ~DtorDel (void) = delete;

  int x;
};

DtorDel::DtorDel (void)
{
  x = 2;
}

int
cbvDtorDel (DtorDel arg)
{
  // Calling this method should be rejected
  return arg.x;
}

class FourCCtor {
public:
  FourCCtor (void);

  FourCCtor (FourCCtor &rhs);
  FourCCtor (const FourCCtor &rhs);
  FourCCtor (volatile FourCCtor &rhs);
  FourCCtor (const volatile FourCCtor &rhs);

  int x;
};

FourCCtor::FourCCtor (void)
{
  x = 2;
}

FourCCtor::FourCCtor (FourCCtor &rhs)
{
  x = 3;
}

FourCCtor::FourCCtor (const FourCCtor &rhs)
{
  x = 4;
}

FourCCtor::FourCCtor (volatile FourCCtor &rhs)
{
  x = 5;
}

FourCCtor::FourCCtor (const volatile FourCCtor &rhs)
{
  x = 6;
}

int
cbvFourCCtor (FourCCtor arg)
{
  arg.x += 10;  // intentionally modify
  return arg.x;
}

class TwoMCtor {
public:
  TwoMCtor (void);

  /* Even though one move ctor is defaulted, the other
     is explicit.  */
  TwoMCtor (const TwoMCtor &&rhs);
  TwoMCtor (TwoMCtor &&rhs) = default;

  int x;
};

TwoMCtor::TwoMCtor (void)
{
  x = 2;
}

TwoMCtor::TwoMCtor (const TwoMCtor &&rhs)
{
  x = 3;
}

int
cbvTwoMCtor (TwoMCtor arg)
{
  arg.x += 10;  // intentionally modify
  return arg.x;
}

class TwoMCtorAndCCtor {
public:
  TwoMCtorAndCCtor (void);

  TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default;

  /* Even though one move ctor is defaulted, the other
     is explicit.  This makes the type pass-by-ref.  */
  TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs);
  TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default;

  int x;
};

TwoMCtorAndCCtor::TwoMCtorAndCCtor (void)
{
  x = 2;
}

TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs)
{
  x = 4;
}

int
cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg)
{
  arg.x += 10;  // intentionally modify
  return arg.x;
}

ArrayContainerByVal arrayContainerByVal;
ArrayContainerByRef arrayContainerByRef;
Dynamic dynamic;
Inlined inlined;
// Cannot stack-allocate DtorDel
DtorDel *dtorDel;
FourCCtor fourCctor_c0v0;
const FourCCtor fourCctor_c1v0;
volatile FourCCtor fourCctor_c0v1;
const volatile FourCCtor fourCctor_c1v1;
TwoMCtor twoMctor;
TwoMCtorAndCCtor twoMctorAndCctor;

int
main (void)
{
  int v;
  dtorDel = new DtorDel;
  /* Explicitly call the cbv function to make sure the compiler
     will not omit any code in the binary.  */
  v = cbvArrayContainerByVal (arrayContainerByVal);
  v = cbvArrayContainerByRef (arrayContainerByRef);
  v = cbvDynamic (dynamic);
  v = cbvInlined (inlined);
  v = cbvFourCCtor (fourCctor_c0v0);
  v = cbvFourCCtor (fourCctor_c1v0);
  v = cbvFourCCtor (fourCctor_c0v1);
  v = cbvFourCCtor (fourCctor_c1v1);
  /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted
  v = cbvTwoMCtorAndCCtor (twoMctorAndCctor);

  /* stop here */

  return 0;
}
