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

   Copyright 1998-2024 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/>.  */

extern "C" {
#include <stdio.h>
}


class A {
public:
  A();
  int foo (int x);
  int bar (int y);
  virtual int baz (int z);
  char c;
  int  j;
  int  jj;
  static int s;
};

class B {
public:
  static int s;
};

int A::s = 10;
int B::s = 20;

A::A()
{
  c = 'x';
  j = 5;
}

int A::foo (int dummy)
{
  j += 3;
  return j + dummy;
}

int A::bar (int dummy)
{
  int r;
  j += 13;
  r = this->foo(15);
  return r + j + 2 * dummy;
}

int A::baz (int dummy)
{
  int r;
  j += 15;
  r = this->foo(15);
  return r + j + 12 * dummy;
}

int fum (int dummy)
{
  return 2 + 13 * dummy;
}

typedef int (A::*PMF)(int);

typedef int A::*PMI;

/* This class is in front of the other base classes of Diamond, so
   that we can detect if the offset for Left or the first Base is
   added twice - otherwise it would be 2 * 0 == 0.  */
class Padding
{
public:
  int spacer;
  virtual int vspacer();
};

int Padding::vspacer()
{
  return this->spacer;
}

class Base
{
public:
  int x;
  int get_x();
  virtual int vget_base ();
};

int Base::get_x ()
{
  return this->x;
}

int Base::vget_base ()
{
  return this->x + 1000;
}

class Left : public Base {
public:
  virtual int vget ();
};

int Left::vget ()
{
  return this->x + 100;
}

class Right : public Base {
public:
  virtual int vget ();
};

int Right::vget ()
{
  return this->x + 200;
}

class Diamond : public Padding, public Left, public Right
{
public:
  virtual int vget_base ();
  int (*func_ptr) (int);
};

int Diamond::vget_base ()
{
  return this->Left::x + 2000;
}

struct Container
{
  PMI member;
};

int
func (int x)
{
  return 19 + x;
}

int main ()
{
  A a;
  A * a_p;
  PMF pmf;

  PMF * pmf_p;
  PMI pmi;

  Diamond diamond;
  int (Diamond::*left_pmf) ();
  int (Diamond::*right_pmf) ();
  int (Diamond::*left_vpmf) ();
  int (Diamond::*left_base_vpmf) ();
  int (Diamond::*right_vpmf) ();
  int (Base::*base_vpmf) ();
  int Diamond::*diamond_pmi;
  int (* Diamond::*diamond_pfunc_ptr) (int);

  PMI null_pmi;
  PMF null_pmf;

  a.j = 121;
  a.jj = 1331;
  
  int k;

  a_p = &a;

  pmi = &A::j;
  pmf = &A::bar;
  pmf_p = &pmf;

  diamond.Left::x = 77;
  diamond.Right::x = 88;
  diamond.func_ptr = func;

  /* Some valid pointer to members from a base class.  */
  left_pmf = (int (Diamond::*) ()) (int (Left::*) ()) (&Base::get_x);
  right_pmf = (int (Diamond::*) ()) (int (Right::*) ()) (&Base::get_x);
  left_vpmf = &Left::vget;
  left_base_vpmf = (int (Diamond::*) ()) (int (Left::*) ()) (&Base::vget_base);
  right_vpmf = &Right::vget;

  /* An unspecified, value preserving pointer to member cast.  */
  base_vpmf = (int (Base::*) ()) (int (Left::*) ()) &Diamond::vget_base;

  /* A pointer to data member from a base class.  */
  diamond_pmi = (int Diamond::*) (int Left::*) &Base::x;

  /* A pointer to data member, where the member is itself a pointer to
     a function.  */
  diamond_pfunc_ptr = (int (* Diamond::*) (int)) &Diamond::func_ptr;

  null_pmi = NULL;
  null_pmf = NULL;

  Container contain;
  contain.member = &A::j;

  pmi = NULL; /* Breakpoint 1 here.  */

  (diamond.*diamond_pfunc_ptr) (20);

  k = (a.*pmf)(3);

  pmi = &A::jj;
  pmf = &A::foo;
  pmf_p = &pmf;

  k = (a.*pmf)(4);

  k = (a.**pmf_p)(5);

  k = a.*pmi;
  

  k = a.bar(2);

  k += fum (4);

  B b;

  k += b.s;
  
  return 0;
}
