// test that contracts are matched on friend decls when the type is complete
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }

struct T;

int both(int x, T *t) [[ pre: x > 0 ]] { return 0; }
int both2(int x, T *t) [[ pre: x > 0 ]];

template<typename Z>
int fn(int x, Z *z) [[ pre: x > 0 ]];

template<typename Z>
int fn2(int x, Z *z);

template<typename Z>
int fn3(int x, Z *z) [[ pre: x > 0 ]];

template<>
int fn3<T>(int x, T *z) [[ pre: x > 1 ]];

struct T
{
  friend int both2(int x, T *t) [[ pre: x > 1 ]] // { dg-error "mismatched" }
  {
    return 0;
  }

  friend int hidden(int x, T *t)
    [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]]
  {
    return x;
  }

  /* cannot define friend spec, so we never get to matching contracts
  friend int fn<T>(int x, T *t)
    [[ pre: t->pri > 0 ]] { return 0; } // error defining explicit spec friend
    */

  // bad, general contracts must match general
  template<typename Z>
  friend int fn(int x, Z *z)
    [[ pre: z->pri > 1 ]] { return 0; } // { dg-error "mismatched" }

  // fine, can add contracts
  template<typename Z>
  friend int fn2(int x, Z *z)
    [[ pre: z->pri > 1 ]] { return 0; } // { dg-bogus "mismatched" }

  /* cannot declare without definition, so dup friend can't occur:
  friend int dup(int x, T *t)
    [[ pre: t->pri > 0 ]]; // error non-defining friend with contracts
  friend int dup(int x, T *t)
    [[ pre: t->pri > 1 ]]; // error non-defining friend with contracts
    */

  int x{1};
  private:
    int pri{-10};
};

int hidden(int x, T *t)
  [[ pre: x > 0 ]] [[ pre: t->pri > 1 ]]; // { dg-error "mismatched" }