/* Handle the hair of processing (but not expanding) inline functions.
   Also manage function and varaible name overloading.
   Copyright (C) 1987 Free Software Foundation, Inc.
   Contributed by Michael Tiemann (tiemann@mcc.com)

   This file is part of GNU CC.
   
GNU CC 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 1, or (at your option)
any later version.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */


/* Handle method declarations.  */
#include <stdio.h>
#include "config.h"
#include "tree.h"
#include "cplus-tree.h"
#include "assert.h"

/* TREE_LIST of the current inline functions that need to be
   processed.  */
struct pending_inline *pending_inlines;

# define INLINE_BUF_SIZE 8188
# define OB_INIT() (inline_bufp = inline_buffer)
# define OB_PUTC(C) (*inline_bufp++ = (C))
# define OB_PUTC2(C1,C2) (OB_PUTC (C1), OB_PUTC (C2))
# define OB_PUTS(S) (strcpy (inline_bufp, S), inline_bufp += sizeof (S) - 1)
# define OB_PUTCP(S) (strcpy (inline_bufp, S), inline_bufp += strlen (S))
# define OB_FINISH() (*inline_bufp++ = '\0')

/* Counter to help build parameter names in case they were omitted.  */
static int dummy_name = 0;
static int in_parmlist = 0;
/* Just a pointer into INLINE_BUFFER.  */
static char *inline_bufp;
/* Also a pointer into INLINE_BUFFER.  This points to a safe place to
   cut back to if we assign it 0, in case of error.  */
static char *inline_errp;
static char inline_buffer [INLINE_BUF_SIZE];
static void dump_type (), dump_decl ();
static void dump_init (), dump_unary_op (), dump_binary_op ();

tree wrapper_name, wrapper_pred_name, anti_wrapper_name;

#ifdef NO_AUTO_OVERLOAD
int is_overloaded ();
#endif

void
init_method ()
{
  char buf[sizeof (ANTI_WRAPPER_NAME_FORMAT) + 8];
  sprintf (buf, WRAPPER_NAME_FORMAT, "");
  wrapper_name = get_identifier (buf);
  sprintf (buf, WRAPPER_PRED_NAME_FORMAT, "");
  wrapper_pred_name = get_identifier (buf);
  sprintf (buf, ANTI_WRAPPER_NAME_FORMAT, "");
  anti_wrapper_name = get_identifier (buf);
}

/* Return a pointer to the end of the new text in INLINE_BUFFER.
   We cannot use `fatal' or `error' in here because that
   might cause an infinite loop.  */
static char *
new_text_len (s)
     char *s;
{
  while (*s++) ;

  if (s >= inline_buffer + INLINE_BUF_SIZE)
    {
      fprintf (stderr, "recompile c++ with larger INLINE_BUF_SIZE (%d)", INLINE_BUF_SIZE);
      abort ();
    }
  return s - 1;
}

/* Check that we have not overflowed INLINE_BUFFER.
   We cannot use `fatal' or `error' in here because that
   might cause an infinite loop.  */
static void
check_text_len (s)
     char *s;
{
  if (s >= inline_buffer + INLINE_BUF_SIZE)
    {
      fprintf (stderr, "recompile c++ with larger INLINE_BUF_SIZE (%d)", INLINE_BUF_SIZE);
      abort ();
    }
}

tree
make_anon_parm_name ()
{
  char buf[32];

  sprintf (buf, ANON_PARMNAME_FORMAT, dummy_name++);
  return get_identifier (buf);
}

void
clear_anon_parm_name ()
{
  /* recycle these names.  */
  dummy_name = 0;
}

static void
dump_type_prefix (t, p)
     tree t;
     int *p;
{
  int old_p = 0;
  tree name;

  if (t == NULL_TREE)
    return;

  if (TREE_READONLY (t))
    OB_PUTS ("const ");
  if (TREE_VOLATILE (t))
    OB_PUTS ("volatile ");

  switch (TREE_CODE (t))
    {
    case ERROR_MARK:
      sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
      break;

    case UNKNOWN_TYPE:
      OB_PUTS ("<unknown type>");
      return;

    case TREE_LIST:
      dump_type (TREE_VALUE (t), &old_p);
      if (TREE_CHAIN (t))
	{
	  if (TREE_VALUE (TREE_CHAIN (t)) != void_type_node)
	    {
	      OB_PUTC (',');
	      dump_type (TREE_CHAIN (t), &old_p);
	    }
	}
      else OB_PUTS ("...");
      return;

    case POINTER_TYPE:
      *p += 1;
      dump_type_prefix (TREE_TYPE (t), p);
      while (*p)
	{
	  OB_PUTC ('*');
	  *p -= 1;
	}
      return;

    case OFFSET_TYPE:
      {
	tree type = TREE_TYPE (t);
	if (TREE_CODE (type) == FUNCTION_TYPE)
	  {
	    type = TREE_TYPE (type);
	    if (in_parmlist)
	      OB_PUTS ("auto ");
	  }

	dump_type_prefix (type, &old_p);

	OB_PUTC ('(');
	dump_type (TYPE_OFFSET_BASETYPE (t), &old_p);
	OB_PUTC2 (':', ':');
	while (*p)
	  {
	    OB_PUTC ('*');
	    *p -= 1;
	  }
	return;
      }

    case METHOD_TYPE:
      {
	tree type = TREE_TYPE (t);
	if (in_parmlist)
	  OB_PUTS ("auto ");

	dump_type_prefix (type, &old_p);

	OB_PUTC ('(');
	dump_type (TYPE_METHOD_BASETYPE (t), &old_p);
	OB_PUTC2 (':', ':');
	while (*p)
	  {
	    OB_PUTC ('*');
	    *p -= 1;
	  }
	return;
      }

    case REFERENCE_TYPE:
      dump_type_prefix (TREE_TYPE (t), p);
      OB_PUTC ('&');
      return;

    case ARRAY_TYPE:
      dump_type_prefix (TREE_TYPE (t), p);
      return;

    case FUNCTION_TYPE:
      if (in_parmlist)
	OB_PUTS ("auto ");
      dump_type_prefix (TREE_TYPE (t), &old_p);
      OB_PUTC ('(');
      while (*p)
	{
	  OB_PUTC ('*');
	  *p -= 1;
	}
      return;

    case IDENTIFIER_NODE:
      sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
      break;

    case RECORD_TYPE:
      name = TYPE_NAME (t);
      if (TREE_CODE (name) == TYPE_DECL)
	name = DECL_NAME (name);
      sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (name));
      break;

    case UNION_TYPE:
      name = TYPE_NAME (t);
      if (TREE_CODE (name) == TYPE_DECL)
	name = DECL_NAME (name);
      sprintf (inline_bufp, "union %s ", IDENTIFIER_POINTER (name));
      break;

    case ENUMERAL_TYPE:
      name = TYPE_NAME (t);
      if (TREE_CODE (name) == TYPE_DECL)
	name = DECL_NAME (name);
      sprintf (inline_bufp, "enum %s ", IDENTIFIER_POINTER (name));
      break;

    case TYPE_DECL:
      sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
      break;

    case INTEGER_TYPE:
      /* Normally, `unsigned' is part of the deal.  Not so if it comes
	 with `const' or `volatile'.  */
      if (TREE_UNSIGNED (t)
	  && (TREE_READONLY (t) || TREE_VOLATILE (t)))
	OB_PUTS ("unsigned ");
      /* fall through.  */
    case REAL_TYPE:
    case VOID_TYPE:
      sprintf (inline_bufp, "%s ", TYPE_NAME_STRING (t));
      break;

    default:
      abort ();
    }
  inline_bufp = new_text_len (inline_bufp);
}

static void
dump_type_suffix (t, p)
     tree t;
     int *p;
{
  int old_p = 0;

  if (t == NULL_TREE)
    return;

  switch (TREE_CODE (t))
    {
    case ERROR_MARK:
      sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
      break;

    case UNKNOWN_TYPE:
      return;

    case POINTER_TYPE:
      dump_type_suffix (TREE_TYPE (t), p);
      return;

    case OFFSET_TYPE:
      {
	tree type = TREE_TYPE (t);

	OB_PUTC (')');
	if (TREE_CODE (type) == FUNCTION_TYPE)
	  {
#if 0
	    tree next_arg = TREE_CHAIN (TYPE_ARG_TYPES (type));
	    OB_PUTC ('(');
	    if (next_arg)
	      {
		if (TREE_VALUE (next_arg) != void_type_node)
		  {
		    in_parmlist++;
		    dump_type (next_arg, &old_p);
		    in_parmlist--;
		  }
	      }
	    else OB_PUTS ("...");
	    OB_PUTC (')');
	    dump_type_suffix (TREE_TYPE (type), p);
#else
	    abort ();
#endif
	  }
	return;
      }

    case METHOD_TYPE:
      {
	tree next_arg;
	OB_PUTC (')');
	next_arg = TREE_CHAIN (TYPE_ARG_TYPES (t));
	OB_PUTC ('(');
	if (next_arg)
	  {
	    if (TREE_VALUE (next_arg) != void_type_node)
	      {
		in_parmlist++;
		dump_type (next_arg, &old_p);
		in_parmlist--;
	      }
	  }
	else OB_PUTS ("...");
	OB_PUTC (')');
	dump_type_suffix (TREE_TYPE (t), p);
	return;
      }

    case REFERENCE_TYPE:
      dump_type_suffix (TREE_TYPE (t), p);
      return;

    case ARRAY_TYPE:
      dump_type_suffix (TREE_TYPE (t), p);
      OB_PUTC2 ('[', ']');
      return;

    case FUNCTION_TYPE:
      OB_PUTC2 (')', '(');
      if (TYPE_ARG_TYPES (t) && TREE_VALUE (TYPE_ARG_TYPES (t)) != void_type_node)
	{
	  in_parmlist++;
	  dump_type (TYPE_ARG_TYPES (t), &old_p);
	  in_parmlist--;
	}
      OB_PUTC (')');
      dump_type_suffix (TREE_TYPE (t), p);
      return;

    case IDENTIFIER_NODE:
    case RECORD_TYPE:
    case UNION_TYPE:
    case ENUMERAL_TYPE:
    case TYPE_DECL:
    case INTEGER_TYPE:
    case REAL_TYPE:
    case VOID_TYPE:
      return;

    default:
      abort ();
    }
  inline_bufp = new_text_len (inline_bufp);
}

static void
dump_readonly_or_volatile (t)
     tree t;
{
  if (TREE_READONLY (t))
    OB_PUTS ("const ");
  if (TREE_VOLATILE (t))
    OB_PUTS ("volatile ");
}

static void
dump_type (t, p)
     tree t;
     int *p;
{
  int old_p = 0;

  if (t == NULL_TREE)
    return;

  switch (TREE_CODE (t))
    {
    case ERROR_MARK:
      sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
      break;

    case UNKNOWN_TYPE:
      OB_PUTS ("<unknown type>");
      return;

    case TREE_LIST:
      dump_type (TREE_VALUE (t), &old_p);
      if (TREE_CHAIN (t))
	{
	  if (TREE_VALUE (TREE_CHAIN (t)) != void_type_node)
	    {
	      OB_PUTC (',');
	      dump_type (TREE_CHAIN (t), &old_p);
	    }
	}
      else OB_PUTS ("...");
      return;

    case POINTER_TYPE:
      if (TREE_READONLY (t) | TREE_VOLATILE (t))
	dump_readonly_or_volatile (t);
      *p += 1;
      dump_type (TREE_TYPE (t), p);
      while (*p)
	{
	  OB_PUTC ('*');
	  *p -= 1;
	}
      return;

    case REFERENCE_TYPE:
      if (TREE_READONLY (t) | TREE_VOLATILE (t))
	dump_readonly_or_volatile (t);
      dump_type (TREE_TYPE (t), p);
      OB_PUTC ('&');
      return;

    case ARRAY_TYPE:
      if (TREE_READONLY (t) | TREE_VOLATILE (t))
	dump_readonly_or_volatile (t);
      dump_type (TREE_TYPE (t), p);
      OB_PUTC2 ('[', ']');
      return;

    case OFFSET_TYPE:
    case METHOD_TYPE:
    case FUNCTION_TYPE:
      dump_type_prefix (t, p);
      dump_type_suffix (t, p);
      return;

    case IDENTIFIER_NODE:
      sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
      break;

    case RECORD_TYPE:
      {
	if (TREE_READONLY (t) | TREE_VOLATILE (t))
	  dump_readonly_or_volatile (t);
	t = TYPE_NAME (t);
	if (TREE_CODE (t) == TYPE_DECL)
	  t = DECL_NAME (t);
	sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (t));
	break;
      }

    case UNION_TYPE:
      {
	if (TREE_READONLY (t) | TREE_VOLATILE (t))
	  dump_readonly_or_volatile (t);
	t = TYPE_NAME (t);
	if (TREE_CODE (t) == TYPE_DECL)
	  t = DECL_NAME (t);
	sprintf (inline_bufp, "union %s ", IDENTIFIER_POINTER (t));
      }
      break;

    case ENUMERAL_TYPE:
      {
	if (TREE_READONLY (t) | TREE_VOLATILE (t))
	  dump_readonly_or_volatile (t);
	t = TYPE_NAME (t);
	if (TREE_CODE (t) == TYPE_DECL)
	  t = DECL_NAME (t);
	sprintf (inline_bufp, "enum %s ", IDENTIFIER_POINTER (t));
      }
      break;

    case TYPE_DECL:
      if (TREE_READONLY (t) | TREE_VOLATILE (t))
	dump_readonly_or_volatile (t);
      sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
      break;

    case INTEGER_TYPE:
      /* Normally, `unsigned' is part of the deal.  Not so if it comes
	 with `const' or `volatile'.  */
      if (TREE_READONLY (t) | TREE_VOLATILE (t))
	dump_readonly_or_volatile (t);
      if (TREE_UNSIGNED (t)
	  && (TREE_READONLY (t) | TREE_VOLATILE (t)))
	OB_PUTS ("unsigned ");
      /* fall through.  */
    case REAL_TYPE:
    case VOID_TYPE:
      sprintf (inline_bufp, "%s ", TYPE_NAME_STRING (t));
      break;

    default:
      abort ();
    }
  inline_bufp = new_text_len (inline_bufp);
}

static void
dump_decl (t)
     tree t;
{
  int p = 0;

  if (t == NULL_TREE)
    return;

  switch (TREE_CODE (t))
    {
    case ERROR_MARK:
      strcpy (inline_bufp, " /* decl error */ ");
      break;

    case PARM_DECL:
      dump_type_prefix (TREE_TYPE (t), &p);
      if (DECL_NAME (t))
	dump_decl (DECL_NAME (t));
      else
	{
	  sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
	  break;
	}
      dump_type_suffix (TREE_TYPE (t), &p);
      return;

    case CALL_EXPR:
      dump_decl (TREE_OPERAND (t, 0));
      OB_PUTC ('(');
      in_parmlist++;
      dump_decl (TREE_OPERAND (t, 1));
      in_parmlist--;
      t = tree_last (TYPE_ARG_TYPES (TREE_TYPE (t)));
      if (!t || TREE_VALUE (t) != void_type_node)
	OB_PUTS ("...");
      OB_PUTC (')');
      return;

    case ARRAY_REF:
      dump_decl (TREE_OPERAND (t, 0));
      OB_PUTC ('[');
      dump_decl (TREE_OPERAND (t, 1));
      OB_PUTC (']');
      return;

    case TYPE_DECL:
      sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
      break;

    case TYPE_EXPR:
      abort ();
      break;

    case IDENTIFIER_NODE:
      if (OPERATOR_NAME_P (t))
	sprintf (inline_bufp, "operator %s ", operator_name_string (t));
      else if (OPERATOR_TYPENAME_P (t))
	{
	  OB_PUTS ("operator ");
	  dump_type (TREE_TYPE (t), &p);
	  return;
	}
      else
	sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
      break;

    case BIT_NOT_EXPR:
      OB_PUTC2 ('~', ' ');
      dump_decl (TREE_OPERAND (t, 0));
      return;

    case SCOPE_REF:
      sprintf (inline_bufp, "%s :: ", IDENTIFIER_POINTER (TREE_OPERAND (t, 0)));
      inline_bufp += sizeof ("%s :: ") + IDENTIFIER_LENGTH (TREE_OPERAND (t, 0));
      dump_decl (TREE_OPERAND (t, 1));
      return;

    case INDIRECT_REF:
      OB_PUTC ('*');
      dump_decl (TREE_OPERAND (t, 0));
      return;

    case ADDR_EXPR:
      OB_PUTC ('&');
      dump_decl (TREE_OPERAND (t, 0));
      return;

    default:
      abort ();
    }
  inline_bufp = new_text_len (inline_bufp);
}

static void
dump_init_list (l)
     tree l;
{
  while (l)
    {
      dump_init (TREE_VALUE (l));
      if (TREE_CHAIN (l))
	OB_PUTC (',');
      l = TREE_CHAIN (l);
    }
}

static void
dump_init (t)
     tree t;
{
  extern char *opname_tab[]; /* in cplus-lex.c  */
  int dummy;

  switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case PARM_DECL:
      sprintf (inline_bufp, " %s ", IDENTIFIER_POINTER (DECL_NAME (t)));
      break;

    case FUNCTION_DECL:
      {
	tree name = DECL_NAME (t);

	if (DESTRUCTOR_NAME_P (name))
	  sprintf (inline_bufp, " ~%s ",
		   IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
	else if (OPERATOR_NAME_P (name))
	  sprintf (inline_bufp, "operator %s ", operator_name_string (name));
	else if (OPERATOR_TYPENAME_P (name))
	  {
	    dummy = 0;
	    OB_PUTS ("operator ");
	    dump_type (TREE_TYPE (name), &dummy);
	  }
#if 0
	else if (WRAPPER_NAME_P (name))
	  sprintf (inline_bufp, " ()%s ",
		   IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
	else if (WRAPPER_PRED_NAME_P (name))
	  sprintf (inline_bufp, " ()?%s ",
		   IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
	else if (ANTI_WRAPPER_NAME_P (name))
	  sprintf (inline_bufp, " ~()%s ",
		   IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
#endif
	else sprintf (inline_bufp, " %s ",
		      IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
      }
      break;

    case CONST_DECL:
      dummy = 0;
      OB_PUTC2 ('(', '(');
      dump_type (TREE_TYPE (t), &dummy);
      OB_PUTC (')');
      dump_init (DECL_INITIAL (t));
      OB_PUTC (')');
      return;

    case INTEGER_CST:
      sprintf (inline_bufp, " %d ", TREE_INT_CST_LOW (t));
      break;

    case REAL_CST:
      sprintf (inline_bufp, " %g ", TREE_REAL_CST (t));
      break;

    case STRING_CST:
      {
	char *p = TREE_STRING_POINTER (t);
	int len = TREE_STRING_LENGTH (t) - 1;
	int i;

	check_text_len (inline_bufp + len + 2);
	OB_PUTC ('\"');
	for (i = 0; i < len; i++)
	  {
	    register char c = p[i];
	    if (c == '\"' || c == '\\')
	      OB_PUTC ('\\');
	    if (c >= ' ' && c < 0177)
	      OB_PUTC (c);
	    else
	      {
		sprintf (inline_bufp, "\\%03o", c);
		inline_bufp = new_text_len (inline_bufp);
	      }
	  }
	OB_PUTC ('\"');
      }
      return;

    case COMPOUND_EXPR:
      dump_binary_op (",", t, 1);
      break;

    case COND_EXPR:
      OB_PUTC ('(');
      dump_init (TREE_OPERAND (t, 0));
      OB_PUTS (" ? ");
      dump_init (TREE_OPERAND (t, 1));
      OB_PUTS (" : ");
      dump_init (TREE_OPERAND (t, 2));
      OB_PUTC (')');
      return;

    case NEW_EXPR:
      strcpy (inline_bufp, TYPE_NAME_STRING (TREE_TYPE (t)));
      inline_bufp = new_text_len (inline_bufp);
      OB_PUTC ('(');
      dump_init_list (TREE_CHAIN (TREE_OPERAND (t, 1)));
      OB_PUTC (')');
      return;

    case CALL_EXPR:
      OB_PUTC ('(');
      dump_init (TREE_OPERAND (t, 0));
      dump_init_list (TREE_OPERAND (t, 1));
      OB_PUTC (')');
      return;

    case MODIFY_EXPR:
    case PLUS_EXPR:
    case MINUS_EXPR:
    case MULT_EXPR:
    case TRUNC_DIV_EXPR:
    case TRUNC_MOD_EXPR:
    case MIN_EXPR:
    case MAX_EXPR:
    case LSHIFT_EXPR:
    case RSHIFT_EXPR:
    case BIT_IOR_EXPR:
    case BIT_XOR_EXPR:
    case BIT_AND_EXPR:
    case BIT_ANDTC_EXPR:
    case TRUTH_ANDIF_EXPR:
    case TRUTH_ORIF_EXPR:
    case LT_EXPR:
    case LE_EXPR:
    case GT_EXPR:
    case GE_EXPR:
    case EQ_EXPR:
    case NE_EXPR:
      dump_binary_op (opname_tab[(int) TREE_CODE (t)], t,
		      strlen (opname_tab[(int) TREE_CODE (t)]));
      return;

    case CEIL_DIV_EXPR:
    case FLOOR_DIV_EXPR:
    case ROUND_DIV_EXPR:
      dump_binary_op ("/", t, 1);
      return;

    case CEIL_MOD_EXPR:
    case FLOOR_MOD_EXPR:
    case ROUND_MOD_EXPR:
      dump_binary_op ("%", t, 1);
      return;

    case COMPONENT_REF:
      dump_binary_op (".", t, 1);
      return;

    case CONVERT_EXPR:
      dump_unary_op ("+", t, 1);
      return;

    case ADDR_EXPR:
      if (TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
	  || TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
	dump_init (TREE_OPERAND (t, 0));
      else
	dump_unary_op ("&", t, 1);
      return;

    case INDIRECT_REF:
      dump_unary_op ("*", t, 1);
      return;

    case NEGATE_EXPR:
    case BIT_NOT_EXPR:
    case TRUTH_NOT_EXPR:
    case PREDECREMENT_EXPR:
    case PREINCREMENT_EXPR:
      dump_unary_op (opname_tab [(int)TREE_CODE (t)], t,
		     strlen (opname_tab[(int) TREE_CODE (t)]));
      return;

    case POSTDECREMENT_EXPR:
    case POSTINCREMENT_EXPR:
      OB_PUTC ('(');
      dump_init (TREE_OPERAND (t, 0));
      OB_PUTCP (opname_tab[(int)TREE_CODE (t)]);
      OB_PUTC (')');
      return;

    case NOP_EXPR:
      dummy = 0;
      OB_PUTC2 ('(', '(');
      dump_type (TREE_TYPE (t), &dummy);
      OB_PUTC (')');
      dump_init (TREE_OPERAND (t, 0));
      OB_PUTC (')');
      return;

      /*  This list is incomplete, but should suffice for now.
	  It is very important that `sorry' does not call
	  `report_error_function'.  That could cause an infinite loop.  */
    default:
      sorry ("that operation not supported for default parameters");

      /* fall through to ERROR_MARK...  */
    case ERROR_MARK:
      *inline_errp = '\0';
      inline_bufp = inline_errp + 1;
      return;
    }
  inline_bufp = new_text_len (inline_bufp);
}

static void
dump_binary_op (opstring, t, len)
     char *opstring;
     tree t;
     int len;
{
  OB_PUTC ('(');
  dump_init (TREE_OPERAND (t, 0));
  sprintf (inline_bufp, " %s ", opstring);
  inline_bufp += len + 2;
  dump_init (TREE_OPERAND (t, 1));
  OB_PUTC (')');
  check_text_len (inline_bufp);
}

static void
dump_unary_op (opstring, t, len)
     char *opstring;
     tree t;
     int len;
{
  OB_PUTC ('(');
  sprintf (inline_bufp, " %s ", opstring);
  inline_bufp += len + 2;
  dump_init (TREE_OPERAND (t, 0));
  OB_PUTC (')');
  check_text_len (inline_bufp);
}


/* Process the currently pending inline function definitions.
   This entails:
   (1) Creating a temporary file which contains return type,
       delarator name, and argment names and types of the
       function to be inlined.
   (2) Reading that file into a buffer which can then be
       made to look line another piece of inline code to
       process, stuffing that on the top of the inline
       stack, then letting the lexer and parser read from those
       two.
*/

static struct pending_inline *
stash_inline_prefix (cname, field)
     tree cname, field;
{
  extern int lineno;
  struct pending_inline *t;
  tree name, fndecl, fntype;
  int p = 0;

  dummy_name = 0;
  name = DECL_ORIGINAL_NAME (field);
  /* We still don't do friends right.  */
  fndecl = field;
  fntype = TREE_TYPE (fndecl);

  if (TREE_INLINE (fndecl))
    strcpy (inline_buffer, "inline ");
  else
    strcpy (inline_buffer, "static ");
  inline_bufp = inline_buffer + strlen (inline_buffer);
  if (! OPERATOR_TYPENAME_P (name))
    dump_type_prefix (TREE_TYPE (fntype), &p);
  if (TREE_CODE (TREE_TYPE (field)) == METHOD_TYPE)
    {
      dump_type (cname, &p);
      inline_bufp[-1] = ':';
      *inline_bufp++ = ':';
      if (DESTRUCTOR_NAME_P (DECL_NAME (fndecl)))
	OB_PUTC ('~');
#if 0
      else if (WRAPPER_NAME_P (DECL_NAME (fndecl)))
	OB_PUTC2 ('(', ')');
      else if (WRAPPER_PRED_NAME_P (DECL_NAME (fndecl)))
	OB_PUTS ("()?");
      else if (ANTI_WRAPPER_NAME_P (DECL_NAME (fndecl)))
	OB_PUTS ("~()");
#endif
    }
  dump_decl (name);
  OB_PUTC ('(');
  if (! DESTRUCTOR_NAME_P (DECL_NAME (fndecl)))
    {
      tree parmlist = DECL_ARGUMENTS (fndecl);
      tree typelist = TYPE_ARG_TYPES (fntype);

      if (TREE_CODE (field) == FIELD_DECL)
	{
	  parmlist = TREE_CHAIN (parmlist);
	  typelist = TREE_CHAIN (typelist);
	}

      in_parmlist++;
      while (parmlist)
	{
	  dump_decl (parmlist);
#if 0
	  if (TREE_PURPOSE (typelist))
	    {
	      inline_errp = inline_bufp;
	      OB_PUTS (" = (");
	      dump_init (TREE_PURPOSE (typelist));
	      OB_PUTC (')');
	      if (*inline_errp == '\0')
		inline_bufp = inline_errp;
	    }
#endif
	  if (TREE_CHAIN (parmlist))
	    OB_PUTC (',');
	  parmlist = TREE_CHAIN (parmlist);
	  typelist = TREE_CHAIN (typelist);
	}
      in_parmlist--;
      if (!typelist || TREE_VALUE (typelist) != void_type_node)
	OB_PUTS ("...");
    }
  OB_PUTC (')');

  if (! OPERATOR_TYPENAME_P (name))
    dump_type_suffix (TREE_TYPE (fntype), &p);

  {
    extern tree value_identifier;

    if (DECL_RESULT (fndecl) != value_identifier)
      {
	tree result = DECL_RESULT (fndecl);

	OB_PUTS ("return ");
	OB_PUTS (DECL_NAME (result));
	if (DECL_INITIAL (result))
	  {
	    OB_PUTC ('=');
	    dump_init (DECL_INITIAL (result));
	    OB_PUTC (';');
	  }
      }
  }
  OB_FINISH ();
  check_text_len (inline_bufp);

  t = (struct pending_inline *)xmalloc (sizeof (struct pending_inline));
  t->len = inline_bufp - inline_buffer;
  t->buf = (char *)xmalloc (t->len);
  bcopy (inline_buffer, t->buf, t->len);
  t->lineno = lineno;
  t->filename = input_filename;
  t->token = 0;
  return t;
}

#define OVERLOAD_MAX_LEN 1024

/* Pretty printing for announce_function.  If BUF is nonzero, then
   the text is written there.  The buffer is assued to be of size
   OVERLOAD_MAX_LEN.  CNAME is the name of the class that FNDECL
   belongs to, if we could not figure that out from FNDECL
   itself.  FNDECL is the declaration of the function we
   are interested in seeing.  PRINT_RET_TYPE_P is non-zero if
   we should print the type that this function returns.  */
char *
fndecl_as_string (buf, cname, fndecl, print_ret_type_p)
     char *buf;
     tree cname, fndecl;
     int print_ret_type_p;
{
  tree name = DECL_NAME (fndecl);
  tree fntype = TREE_TYPE (fndecl);
  tree parmtypes = TYPE_ARG_TYPES (fntype);
  int p = 0;
  int spaces = 0;

  OB_INIT ();

  if (DECL_STATIC_FUNCTION_P (fndecl))
    cname = TYPE_NAME (DECL_STATIC_CONTEXT (fndecl));
  else if (! cname && TREE_CODE (fntype) == METHOD_TYPE)
    cname = TYPE_NAME (TYPE_METHOD_BASETYPE (fntype));

  if (print_ret_type_p && ! OPERATOR_TYPENAME_P (name))
    dump_type_prefix (TREE_TYPE (fntype), &p);
  if (DECL_STATIC_FUNCTION_P (fndecl))
      OB_PUTS ("static ");
    
  if (cname)
    {
      dump_type (cname, &p);
      inline_bufp[-1] = ':';
      *inline_bufp++ = ':';
      parmtypes = TREE_CHAIN (parmtypes);
    }

  if (DESTRUCTOR_NAME_P (name))
    {
      OB_PUTC ('~');
      parmtypes = TREE_CHAIN (parmtypes);
      dump_decl (DECL_ORIGINAL_NAME (fndecl));
    }
  else if (OPERATOR_NAME_P (name))
    {
      sprintf (inline_bufp, "operator %s ", operator_name_string (name));
      inline_bufp += strlen (inline_bufp);
    }
  else if (OPERATOR_TYPENAME_P (name))
    {
      /* This cannot use the hack that the operator's return
	 type is stashed off of its name because it may be
	 used for error reporting.  In the case of conflicting
	 declarations, both will have the same name, yet
	 the types will be different, hence the TREE_TYPE field
	 of the first name will be clobbered by the second.  */
      OB_PUTS ("operator ");
      dump_type (TREE_TYPE (TREE_TYPE (fndecl)), &p);
    }
  else if (DECL_CONSTRUCTOR_P (fndecl))
    {
#ifdef SOS
      if (TYPE_DYNAMIC (TREE_TYPE (TREE_TYPE (DECL_ORIGINAL_NAME (fndecl)))))
	{
	  OB_PUTS ("dynamic ");
	  parmtypes = TREE_CHAIN (parmtypes);
	}
#endif
      dump_decl (DECL_ORIGINAL_NAME (fndecl));
    }
  else
    {
#if 0
      if (WRAPPER_NAME_P (name))
	OB_PUTC2 ('(', ')');
      if (WRAPPER_PRED_NAME_P (name))
	OB_PUTS ("()?");
      else if (ANTI_WRAPPER_NAME_P (name))
	OB_PUTS ("~()");
#endif
      dump_decl (DECL_ORIGINAL_NAME (fndecl));
    }

  OB_PUTC ('(');
  if (parmtypes)
    {
      in_parmlist++;
      if (TREE_VALUE (parmtypes) != void_type_node)
	spaces = 2;
      while (parmtypes && TREE_VALUE (parmtypes) != void_type_node)
	{
	  dump_type (TREE_VALUE (parmtypes), &p);
	  while (inline_bufp[-1] == ' ')
	    inline_bufp--;
	  if (TREE_PURPOSE (parmtypes))
	    {
	      inline_errp = inline_bufp;
	      OB_PUTS (" (= ");
	      dump_init (TREE_PURPOSE (parmtypes));
	      OB_PUTC (')');
	    }
	  OB_PUTC2 (',', ' ');
	  parmtypes = TREE_CHAIN (parmtypes);
	}
      in_parmlist--;
    }
  
  if (parmtypes)
    inline_bufp -= spaces;
  else
    OB_PUTS ("...");

  OB_PUTC (')');

  if (print_ret_type_p && ! OPERATOR_TYPENAME_P (name))
    dump_type_suffix (TREE_TYPE (fntype), &p);

  OB_FINISH ();
  check_text_len (inline_bufp);
  
  if (buf == 0)
    return inline_buffer;

  if (strlen (inline_buffer) >= OVERLOAD_MAX_LEN)
    {
      fprintf (stderr, "fndecl_as_string returns something too large");
      abort ();
    }
  strcpy (buf, inline_buffer);
  return buf;
}

/* Move inline function defintions out of structure so that they
   can be processed normally.  CNAME is the name of the class
   we are working from, METHOD_LIST is the list of method lists
   of the structure.  We delete friend methods here, after
   saving away their inline function definitions (if any).  */

/* Subroutine of `do_inline_function_hair'.  */
static void
prepare_inline (cname, fndecl)
     tree cname, fndecl;
{
  if (DECL_PENDING_INLINE_INFO (fndecl))
    {
      struct pending_inline *t1, *t2;

      t2 = DECL_PENDING_INLINE_INFO (fndecl);
      t2->next = pending_inlines;
      t2->fndecl = fndecl;
#ifdef DO_METHODS_THE_OLD_WAY
      t1 = stash_inline_prefix (cname, methods);
      t1->next = t2;
#else
      t1 = t2;
#endif
      pending_inlines = t1;

      /* Allow this decl to be seen in global scope */
      IDENTIFIER_GLOBAL_VALUE (DECL_NAME (fndecl)) = fndecl;
    }
}

void
do_inline_function_hair (cname, method_list, friend_list)
     tree cname, method_list, friend_list;
{
  while (method_list)
    {
      /* Do inline member functions.  */
      tree methods = TREE_VALUE (method_list);
      while (methods)
	{
	  prepare_inline (cname, methods);
	  methods = TREE_CHAIN (methods);
	}
      method_list = TREE_CHAIN (method_list);
    }
  while (friend_list)
    {
      prepare_inline (NULL_TREE, TREE_VALUE (friend_list));
      friend_list = TREE_CHAIN (friend_list);
    }
}

/* Report a argument type mismatch between the best declared function
   we could find and the current argument list that we have.  */
void
report_type_mismatch (cp, parmtypes, name_kind, err_name)
     struct candidate *cp;
     tree parmtypes;
     char *name_kind, *err_name;
{
  char buf[OVERLOAD_MAX_LEN];
  int i = cp->u.bad_arg;
  tree ttf, tta;

  if (i == -2)
    {
      error ("too few arguments for %s `%s'", name_kind, err_name);
      return;
    }
  else if (i == -1)
    {
      error ("too many arguments for %s `%s'", name_kind, err_name);
      return;
    }
  if (i == 0)
    {
      if (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE)
	{
	  /* Happens when we have an ambiguous base class.  */
	  if (get_base_type (DECL_CONTEXT (cp->function), TREE_TYPE (TREE_TYPE (TREE_VALUE (parmtypes))), 1) != error_mark_node)
	    abort ();
	  return;
	}
      i = 1;
    }
  ttf = TYPE_ARG_TYPES (TREE_TYPE (cp->function));
  tta = parmtypes;

  while (i-- > 0)
    {
      ttf = TREE_CHAIN (ttf);
      tta = TREE_CHAIN (tta);
    }
  fndecl_as_string (buf, 0, cp->function, 0);
  inline_bufp = inline_buffer;

  /* Reset `i' so that type printing routines do the right thing.  */
  if (tta)
    {
      i = (TREE_CODE (TREE_TYPE (TREE_VALUE (tta))) == FUNCTION_TYPE
	   || TREE_CODE (TREE_TYPE (TREE_VALUE (tta))) == METHOD_TYPE);
      dump_type (TREE_TYPE (TREE_VALUE (tta)), &i);
    }
  else OB_PUTS ("void");

  OB_FINISH ();
  sprintf (inline_bufp, "bad argument %d for function `%s' (type was %s)",
	   cp->u.bad_arg - (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE), buf, inline_buffer);
  strcpy (buf, inline_bufp);
  error (buf);
}

/* Here is where overload code start.  */

#define NULL 0

#define OVERLOAD_MAX_LEN 1024

static char *build_overload_name ();

/* Change the name of a function definition so that it may be
   overloaded. NAME is the name of the function to overload,
   PARMS is the parameter list (which determines what name the
   final function obtains).  */
tree
build_decl_overload (name, parms)
     char *name;
     tree parms;
{
  int tmp;
  char tname[OVERLOAD_MAX_LEN];

  name = (char *)strcpy (tname, name);
  tmp = strlen (name);
  name[tmp++] = '_';
  if (parms && TREE_VALUE (parms) != void_type_node)
    {
      char *name_end;
      name_end = build_overload_name (parms, name+tmp, &tname[OVERLOAD_MAX_LEN]);
      name_end[-1] = '\0';	/* cancel trailing '_' */
    }
  else
    {
      strcpy (name + tmp, "VOID");
    }
  return get_identifier (name);
}

/* Build an overload name for the type expression TYPE.  CTYPE is
   the class we are building this for.  */
tree
build_typename_overload (type)
     tree type;
{
  int tmp;
  char tname[OVERLOAD_MAX_LEN], *name_end;

  sprintf (tname, OPERATOR_TYPENAME_FORMAT);
  tname[sizeof (OPERATOR_TYPENAME_FORMAT) - 1] = '_';
  name_end = build_overload_name (type,
				  tname + sizeof (OPERATOR_TYPENAME_FORMAT),
				  &tname[OVERLOAD_MAX_LEN]);
  name_end[-1] = '\0';
  return get_identifier (tname);
}

/* A function has just been referenced that needs to be overloaded.
   The name of this function comes through NAME.  The name depends
   on PARMS.

   Note that this function must handle simple `C' promotions,
   as well as variable numbers of arguments (...), and
   default arguments to boot.

   If the overloading is successful, we return a treenode which
   contains the call to the function.

   If overloading produces candidates which are probabe, but not definite,
   we hold these candidates.  If FINAL_CP is non-zero, then we are free
   to assume that final_cp points to enough storage for all candidates that
   this function might generate.

   Note that the DECL_RTL of FUNCTION must be made to agree with this
   function's new name.  */

tree
do_actual_overload (fnname, parms, complain, final_cp)
     tree fnname, parms;
     int complain;
     struct candidate *final_cp;
{
  /* must check for overloading here */
  tree decl, functions, function, parm;
  tree parmtypes;
  int tmp;
  char name[OVERLOAD_MAX_LEN];
  int length;
  int parmlength = list_length (parms);

  struct candidate *candidates, *cp;
  int rank_for_overload ();

  if (final_cp)
    {
      final_cp[0].evil = 0;
      final_cp[0].user = 0;
      final_cp[0].b_or_d = 0;
      final_cp[0].easy = 0;
      final_cp[0].function = 0;
      /* end marker.  */
      final_cp[1].evil = 1;
    }

  strcpy (name, IDENTIFIER_POINTER (fnname));
  tmp = IDENTIFIER_LENGTH (fnname);
  name[tmp++] = '_';
  parmtypes = NULL_TREE;
  for (parm = parms; parm; parm = TREE_CHAIN (parm))
    {
      register tree t = TREE_TYPE (TREE_VALUE (parm));

      if (t == error_mark_node)
	return error_mark_node;
      if (TREE_CODE (t) == ARRAY_TYPE || TREE_CODE (t) == OFFSET_TYPE)
	{
	  /* Perform the conversion from ARRAY_TYPE to POINTER_TYPE in place.
	     Also convert OFFSET_TYPE entities to their normal selves.
	     This eliminates needless calls to `compute_conversion_costs'.  */
	  TREE_VALUE (parm) = default_conversion (TREE_VALUE (parm));
	  t = TREE_TYPE (TREE_VALUE (parm));
	}
      parmtypes = chainon (parmtypes, build_tree_list (NULL_TREE, t));
    }
  if (parmtypes && TREE_VALUE (parmtypes) != void_type_node)
    {
      char *name_end;
      name_end = build_overload_name (parmtypes, name+tmp, &name[OVERLOAD_MAX_LEN]);
      name_end[-1] = '\0';	/* cancel trailing '_' */
    }
  else
    {
      strcpy (name + tmp, "VOID");
    }

  decl = get_identifier (name);

  /* Now check to see whether or not we can win.
     Note that if we are called from `build_method_call',
     then we cannot have a mis-match, because we would have
     already found such a winning case.  */

  if (IDENTIFIER_GLOBAL_VALUE (decl))
    if (TREE_CODE (IDENTIFIER_GLOBAL_VALUE (decl)) != TREE_LIST)
      return build_function_call (DECL_MAIN_VARIANT (IDENTIFIER_GLOBAL_VALUE (decl)), parms);

  functions = IDENTIFIER_GLOBAL_VALUE (fnname);

  if (functions == NULL_TREE)
    {
      if (complain)
	error ("only member functions apply");
      if (final_cp)
	final_cp->evil = 1;
      return error_mark_node;
    }

  if (TREE_CODE (functions) == FUNCTION_DECL)
    {
      functions = DECL_MAIN_VARIANT (functions);
      if (final_cp)
	{
	  /* We are just curious whether this is a viable alternative or not.  */
	  final_cp->harshness
	    = (unsigned short *)alloca ((parmlength+1) * sizeof (short));
	  compute_conversion_costs (functions, parms, final_cp, parmlength);
	  return functions;
	}
      else
	return build_function_call (functions, parms);
    }

  if (TREE_VALUE (functions) == NULL_TREE)
    {
      if (complain)
	error ("function `%s' declared overloaded, but no instances of that function declared",
	       IDENTIFIER_POINTER (TREE_PURPOSE (functions)));
      return error_mark_node;
    }

  if (TREE_CODE (TREE_VALUE (functions)) == TREE_LIST)
    {
      register tree outer;
      length = 0;

      /* The list-of-lists should only occur for class things.  */
      if (functions != IDENTIFIER_CLASS_VALUE (fnname))
	abort ();

      for (outer = functions; outer; outer = TREE_CHAIN (outer))
	{
	  /* member functions.  */
	  length += list_length (TREE_VALUE (TREE_VALUE (outer)));
	  /* friend functions.  */
	  length += list_length (TREE_TYPE (TREE_VALUE (outer)));
	}
    }
  else
    {
      length = list_length (functions);
    }

  if (final_cp)
    candidates = final_cp;
  else
    candidates = (struct candidate *)alloca ((length+1) * sizeof (struct candidate));

  cp = candidates;

  if (TREE_CODE (TREE_VALUE (functions)) == TREE_LIST)
    {
      /* OUTER is a list of method lists.  The PURPOSE and VALUE fields
	 are the owning class and the method list, respectively.  */
      register tree outer = functions;

      /* Should not get here, since things in class scope
	 don't come through here.  */
      abort ();

      while (outer)
	{
	  /* INNER is the chain of FUNCTION_DECLS.  */
	  register tree inner = TREE_VALUE (TREE_VALUE (outer));
	  while (inner)
	    {
	      /* Here, inner is a FUNCTION_DECL.  */
	      function = DECL_MAIN_VARIANT (inner);
	      cp->harshness
		= (unsigned short *)alloca ((parmlength+1) * sizeof (short));
	      compute_conversion_costs (function, parms, cp, parmlength);
	      if (cp->evil == 0)
		{
		  if (cp->user == 0 && cp->b_or_d == 0
		      && cp->easy <= 1)
		    if (final_cp)
		      {
			final_cp->easy = cp->easy;
			return function;
		      }
		    else return build_function_call (function, parms);
		  cp++;
		}
	      inner = TREE_CHAIN (inner);
	    }
	  outer = TREE_CHAIN (outer);
	}
    }
  else
    {
      /* OUTER is the list of FUNCTION_DECLS, in a TREE_LIST.  */
      register tree outer;

      for (outer = functions; outer; outer = TREE_CHAIN (outer))
	{
	  function = TREE_VALUE (outer);
	  if (TREE_CODE (function) != FUNCTION_DECL)
	    {
	      if (TREE_CODE (function) == CONST_DECL)
		error_with_decl (function, "enumeral value `%s' conflicts with function of same name");
	      else if (TREE_CODE (function) == VAR_DECL)
		error_with_decl (function, "variable `%s' conflicts with function of same name");
	      else if (TREE_CODE (function) == TYPE_DECL)
		;
	      else abort ();
	      error ("at this point in file");
	      continue;
	    }
	  function = DECL_MAIN_VARIANT (function);
	  cp[0].harshness
	    = (unsigned short *)alloca ((parmlength+1) * sizeof (short));
	  compute_conversion_costs (function, parms, cp, parmlength);
	  if (cp[0].evil == 0)
	    {
	      cp[1].evil = 1;
	      if (final_cp
		  && cp[0].user == 0 && cp[0].b_or_d == 0
		  && cp[0].easy <= 1)
		{
		  final_cp[0].easy = cp[0].easy;
		  return function;
		}
	      cp++;
	    }
	}
    }

  if (cp - candidates)
    {
      tree rval = error_mark_node;

      /* Leave marker.  */
      cp[0].evil = 1;
      if (cp - candidates > 1)
	{
	  struct candidate *best_cp
	    = ideal_candidate (NULL_TREE, candidates,
			       cp - candidates, parms, parmlength);
	  if (best_cp == 0)
	    {
	      if (complain)
		error ("call of overloaded `%s' is ambiguous", IDENTIFIER_POINTER (fnname));
	      return error_mark_node;
	    }
	  else
	    rval = best_cp->function;
	}
      else
	{
	  cp -= 1;
	  if (cp->evil > 1)
	    {
	      if (complain)
		error ("type conversion ambiguous");
	    }
	  else
	    rval = cp->function;
	}

      if (final_cp)
	return rval;

      return build_function_call (rval, parms);
    }
  else if (complain)
    {
      tree name;
      char *err_name;
      /* Initialize name for error reporting.  */
      if (TREE_CODE (functions) == TREE_LIST)
	name = TREE_PURPOSE (functions);
      else
	name = DECL_ORIGINAL_NAME (functions);

      if (OPERATOR_NAME_P (name))
	{
	  err_name = (char *)alloca (OVERLOAD_MAX_LEN);
	  sprintf (err_name, "operator %s", operator_name_string (name));
	}
      else
	err_name = IDENTIFIER_POINTER (name);

      report_type_mismatch (cp, parms, "function", err_name);
    }
  return error_mark_node;
}

/* Top-level interface to explicit overload requests. Allow NAME
   to be overloaded. Error if NAME is already declared for the current
   scope. Warning if function is redundanly overloaded. */

void
declare_overloaded (name)
     tree name;
{
#ifdef NO_AUTO_OVERLOAD
  if (is_overloaded (name))
    warning ("function `%s' already declared overloaded",
	     IDENTIFIER_POINTER (name));
  else if (IDENTIFIER_GLOBAL_VALUE (name))
    error ("overloading function `%s' that is already defined",
	   IDENTIFIER_POINTER (name));
  else
    {
      TREE_OVERLOADED (name) = 1;
      IDENTIFIER_GLOBAL_VALUE (name) = build_tree_list (name, NULL_TREE);
      TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)) = unknown_type_node;
    }
#else
  if (current_lang_name == lang_name_cplusplus)
    {
      if (0)
	warning ("functions are implicitly overloaded in C++");
    }
  else if (current_lang_name == lang_name_c)
    error ("overloading function `%s' cannot be done in C language context");
  else
    abort ();
#endif
}

#ifdef NO_AUTO_OVERLOAD
/* Check to see if NAME is overloaded. For first approximation,
   check to see if its TREE_OVERLOADED is set.  This is used on
   IDENTIFIER nodes.  */
int
is_overloaded (name)
     tree name;
{
  /* @@ */
  return (TREE_OVERLOADED (name)
	  && (! IDENTIFIER_CLASS_VALUE (name) || current_class_type == 0)
	  && ! IDENTIFIER_LOCAL_VALUE (name));
}
#endif

/* Given a list of parameters in PARMS, and a buffer in TEXT, of
   length LEN bytes, create an unambiguous overload string. Should
   distinguish any type that C (or C++) can distinguish. I.e.,
   pointers to functions are treated correctly.

   Any default conversions must take place before this function
   is called.  */

static char *
build_overload_name (parms, text, text_end)
     tree parms;
     char *text, *text_end;
{
  extern char *mode_name[];	/* rtl.h */
  char *textp = text;
  int just_one;
  tree parm;

  if (parms)
    {
      if (just_one = (TREE_CODE (parms) != TREE_LIST)) {
	parm = parms;
	goto only_one;
      }

      while (parms) {
	if (text_end - text < 4)
	  fatal ("Out of string space in build_overload_name!");
	parm = TREE_VALUE (parms);

      only_one:

	switch (TREE_CODE (parm))
	  {
	  case OFFSET_TYPE:
	    *textp++ = 'O';
	    goto more;
	  case METHOD_TYPE:
	    *textp++ = 'M';
	    goto more;

	    /* Reference types don't show up as having
	       different names.  This is because functions
	       which take both a type `T' and `T&' are
	       present a compile-time ambiguity problem
	       at that point of declaration.  */
	  case REFERENCE_TYPE:
#if 0
	    *textp++ = 'R';
#endif
	    goto more;

	  case ARRAY_TYPE:
#ifdef PARM_CAN_BE_ARRAY_TYPE
	    *textp++ = 'A';
	    goto more;
#else
	    *textp++ = 'P';
	    goto more;
#endif
	  case POINTER_TYPE:
	    *textp++ = 'P';
	    if (TREE_TYPE (parm) == void_type_node)
	      {
		*textp++ = 'V';
		break;
	      }
	  more:
	    /* ignore trailing '_' */
	    textp = build_overload_name (TREE_TYPE (parm), textp, text_end) - 1;
	    break;

	  case FUNCTION_TYPE:
	    /* @@ It may be possible to pass a function type in
	       which is not preceded by a 'P'.  */
	    *textp++ = 'F';
	    if (TREE_CODE (TREE_TYPE (parm)) == VOID_TYPE)
	      *textp++ = 'V';
	    else
	      textp = build_overload_name (TREE_TYPE (parm), textp, text_end) - 1;
	    *textp++ = '$';
	    /* keep extra '_' for arg visibility */
	    textp = build_overload_name (TYPE_ARG_TYPES (parm), textp, text_end);
	    break;

	  case INTEGER_TYPE:
	    if (TREE_UNSIGNED (parm))
	      {
		if (TYPE_MAIN_VARIANT (parm) == long_unsigned_type_node)
		  *textp++ = 'l';
		else if (TYPE_MAIN_VARIANT (parm) == long_long_unsigned_type_node)
		  {
		    *textp++ = 'l';
		    *textp++ = 'l';
		  }
		*textp++ = 'u';
	      }
	    else if (TYPE_MAIN_VARIANT (parm) == long_integer_type_node)
	      *textp++ = 'l';
	    else if (TYPE_MAIN_VARIANT (parm) == long_long_unsigned_type_node)
	      {
		*textp++ = 'l';
		*textp++ = 'l';
	      }
	    strcpy (textp, mode_name[(int) TYPE_MODE (parm)]);
	    textp += strlen (textp);
	    break;

	  case REAL_TYPE:
	    if (TYPE_MAIN_VARIANT (parm) == long_double_type_node)
	      *textp++ = 'l';
	    strcpy (textp, mode_name[(int) TYPE_MODE (parm)]);
	    textp += strlen (textp);
	    break;

	  case COMPLEX_TYPE:
	    *textp++ = 'c';
	    strcpy (textp, mode_name[(int) TYPE_MODE (parm)]);
	    textp += strlen (textp);
	    break;

	  case BOOLEAN_TYPE:
	    *textp++ = 'b';
	    strcpy (textp, mode_name[(int) TYPE_MODE (parm)]);
	    textp += strlen (textp);
	    break;

	  case ERROR_MARK:	/* not right, but nothing is anyway */
	  case VOID_TYPE:
	    goto done;		/* end of parameter list */

	    /* have to do these */
	  case UNION_TYPE:
	    *textp++ = 'U';
	    goto common;
	  case ENUMERAL_TYPE:
	    *textp++ = 'E';
	    goto common;
	  case RECORD_TYPE:
	    *textp++ = 'S';
	    goto common;
	  common:
	    parm = TYPE_NAME (parm);
	    if (TREE_CODE (parm) == TYPE_DECL)
	      parm = DECL_NAME (parm);
	    if (TREE_CODE (parm) != IDENTIFIER_NODE)
	      abort ();
	    strcpy (textp, IDENTIFIER_POINTER (parm));
	    textp += IDENTIFIER_LENGTH (parm);
	    break;

	  case UNKNOWN_TYPE:
	    /* This will take some work.  */
	    *textp++ = '?';
	    break;

	    /* these are for PASCAL */
	  case CHAR_TYPE:
	  case FILE_TYPE:
	  case SET_TYPE:
	  case STRING_TYPE:
	    really_sorry ("PASCAL types");
	  case IDENTIFIER_NODE:
	    fatal ("Identifier to overload?");

	  default:
	    abort ();
	  }

	*textp++ = '_';
	if (just_one) break;
	parms = TREE_CHAIN (parms);
      }
    }
  done:
  *textp = '\0';		/* hassle from single type parameter */
  return textp;
}

/* Given a tree_code CODE, and some arguments (at least one),
   attempt to use an overloaded operator on the arguments.

   For unary operators, only the first argument need be checked.
   For binary operators, both arguments may need to be checked.

   Member functions can convert class references to class pointers,
   for one-level deep indirection.  More than that is not supported.
   Operators [](), ()(), and ->() must be member functions.

   We call function call building calls with nonzero complain if
   they are our only hope.  This is true when we see a vanilla operator
   applied to something of aggregate type.  If this fails, we are free to
   return `error_mark_node', because we will have reported the error.

   Operators NEW and delete overload in funny ways: operator new takes
   a single `size' parameter, and operator delete takes a pointer to the
   storage being deleted.  When overloading these operators, success is
   assumed.  If there is a failure, report an error message and return
   `error_mark_node'.  */

/* NOSTRICT */
tree
build_opfncall (code, xarg1, xarg2, arg3)
     enum tree_code code;
     tree xarg1, xarg2;
     tree arg3;
{
  extern int tree_code_length[];
  tree rval = 0;
  tree arg1, arg2;
  tree type1, type2, fnname;
  tree fields1 = 0, parms = 0;
  tree global_fn;
  int try_second;
  int binary_is_unary;
  int flags;

  if (xarg1 == error_mark_node)
    return error_mark_node;

  if (code == COND_EXPR)
    {
      if (TREE_CODE (xarg2) == ERROR_MARK
	  || TREE_CODE (arg3) == ERROR_MARK)
	return error_mark_node;
    }
  if (code == COMPONENT_REF)
    if (TREE_CODE (TREE_TYPE (xarg1)) == POINTER_TYPE)
      return rval;

  /* Some tree codes have length > 1, but we really only want to
     overload them if their first argument has a user defined type.  */
  switch (code)
    {
    case PREINCREMENT_EXPR:
      code = POSTINCREMENT_EXPR;
      binary_is_unary = 1;
      try_second = 0;
      break;

    case POSTDECREMENT_EXPR:
      code = PREDECREMENT_EXPR;
      binary_is_unary = 1;
      try_second = 0;
      break;

    case PREDECREMENT_EXPR:
    case POSTINCREMENT_EXPR:
    case COMPONENT_REF:
      binary_is_unary = 1;
      try_second = 0;
      break;

      /* ARRAY_REFs and CALL_EXPRs must overload successfully.
	 If they do not, return error_mark_node instead of NULL_TREE.  */
    case ARRAY_REF:
      if (xarg2 == error_mark_node)
	return error_mark_node;
    case CALL_EXPR:
      rval = error_mark_node;
    case METHOD_REF:
      binary_is_unary = 0;
      try_second = 0;
      break;

    case NEW_EXPR:
      rval = build_method_call (build (NOP_EXPR, xarg1, integer_zero_node),
				get_identifier ("op$new_expr"),
				build_tree_list (NULL_TREE, xarg2),
				NULL_TREE, LOOKUP_NORMAL);
      /* This happens when the user mis-declares `operator new'.  */
      if (rval == error_mark_node)
	return error_mark_node;
      TREE_TYPE (rval) = xarg1;
      return rval;
      break;

    case DELETE_EXPR:
      if (xarg1 == NULL_TREE)
	xarg1 = build (NOP_EXPR, TREE_TYPE (xarg2), integer_zero_node);
      rval = build_method_call (xarg2,
				get_identifier ("op$delete_expr"),
				build_tree_list (NULL_TREE, xarg2),
				NULL_TREE, LOOKUP_NORMAL);
      /* This happens when the user mis-declares `operator delete'.  */
      if (rval == error_mark_node)
	return error_mark_node;
      TREE_TYPE (rval) = void_type_node;
      return rval;
      break;

    default:
      binary_is_unary = 0;
      try_second = tree_code_length [(int) code] == 2;
      if (xarg2 == error_mark_node)
	return error_mark_node;
      break;
    }

  if (try_second && xarg2 == error_mark_node)
    return error_mark_node;

  /* First, see if we can work with the first argument */
  type1 = TREE_TYPE (xarg1);

  if (type1 == unknown_type_node
      || (try_second && TREE_TYPE (xarg2) == unknown_type_node))
    {
      /* This will not be implemented in the forseeable future.  */
      return rval;
    }

  /* What ever it was, we do not know how to deal with it.  */
  if (type1 == NULL_TREE)
    return rval;

  if (TREE_CODE (type1) == OFFSET_TYPE)
    type1 = TREE_TYPE (type1);

  if (TREE_CODE (type1) == REFERENCE_TYPE)
    {
      arg1 = convert_from_reference (xarg1);
      type1 = TREE_TYPE (arg1);
    }
  else
    {
      arg1 = xarg1;
    }

  if (!IS_AGGR_TYPE (type1))
    {
      /* Try to fail. First, fail if unary */
      if (! try_second)
	return rval;
      /* Second, see if second argument is non-aggregate. */
      type2 = TREE_TYPE (xarg2);
      if (TREE_CODE (type2) == OFFSET_TYPE)
	type2 = TREE_TYPE (type2);
      if (TREE_CODE (type2) == REFERENCE_TYPE)
	{
	  arg2 = convert_from_reference (xarg2);
	  type2 = TREE_TYPE (arg2);
	}
      else
	{
	  arg2 = xarg2;
	}

      if (!IS_AGGR_TYPE (type2))
	return rval;
      try_second = 0;
    }
  if (try_second)
    {
      /* First arg may succeed; see whether second should.  */
      type2 = TREE_TYPE (xarg2);
      if (TREE_CODE (type2) == OFFSET_TYPE)
	type2 = TREE_TYPE (type2);
      if (TREE_CODE (type2) == REFERENCE_TYPE)
	{
	  arg2 = convert_from_reference (xarg2);
	  type2 = TREE_TYPE (arg2);
	}
      else
	{
	  arg2 = xarg2;
	}

      if (! IS_AGGR_TYPE (type2))
	try_second = 0;
    }

  if (code == MODIFY_EXPR)
    fnname = build_operator_fnname (build_op_identifier (make_node (MODIFY_EXPR), make_node ((enum tree_code)arg3)), 0, 2);
  else if (binary_is_unary)
    fnname = build_operator_fnname (build_op_identifier (0, make_node (code)), 0, 1);
  else
    fnname = build_operator_fnname (build_op_identifier (0, make_node (code)), 0,
				    tree_code_length [(int) code]);

  global_fn = IDENTIFIER_GLOBAL_VALUE (fnname);

  /* This is the last point where we will accept failure.  This
     may be too eager if we wish an overloaded operator not to match,
     but would rather a normal operator be called on a type-converted
     argument.  */

  if (IS_AGGR_TYPE (type1))
    fields1 = lookup_fnfields (CLASSTYPE_AS_LIST (type1), fnname, 0);

  if (fields1 == NULL_TREE && global_fn == NULL_TREE)
    return rval;

  /* If RVAL winds up being `error_mark_node', we will return
     that... There is no way that normal semantics of these
     operators will succeed.  */

  /* This argument may be an uncommited MEMBER_REF.  This is
     the case for example when dealing with static class members
     which are referenced from their class name rather than
     from a class instance.  */
  if (TREE_CODE (xarg1) == MEMBER_REF
      && TREE_CODE (TREE_OPERAND (xarg1, 1)) == VAR_DECL)
    xarg1 = TREE_OPERAND (xarg1, 1);
  if (try_second && xarg2 && TREE_CODE (xarg2) == MEMBER_REF
      && TREE_CODE (TREE_OPERAND (xarg2, 1)) == VAR_DECL)
    xarg2 = TREE_OPERAND (xarg2, 1);

  if (global_fn)
    flags = LOOKUP_GLOBAL | LOOKUP_PROTECT;
  else
    flags = LOOKUP_NORMAL;

  if (code == CALL_EXPR)
    {
      /* This can only be a member function.  */
      return build_method_call (xarg1, fnname, xarg2,
				NULL_TREE, LOOKUP_NORMAL);
    }
  else if (tree_code_length[(int) code] == 1 || binary_is_unary)
    {
      parms = NULL_TREE;
      rval = build_method_call (xarg1, fnname, NULL_TREE, NULL_TREE, flags);
    }
  else if (code == COND_EXPR)
    {
      parms = tree_cons (0, xarg2, build_tree_list (0, arg3));
      rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
    }
  else if (code == METHOD_CALL_EXPR)
    {
      /* must be a member function.  */
      parms = tree_cons (NULL_TREE, xarg2, arg3);
      return build_method_call (xarg1, fnname, parms, NULL_TREE, LOOKUP_NORMAL);
    }
  else if (fields1)
    {
      parms = build_tree_list (NULL_TREE, xarg2);
      rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
    }
  else
    {
      parms = tree_cons (NULL_TREE, xarg1,
			 build_tree_list (NULL_TREE, xarg2));
      rval = do_actual_overload (fnname, parms, 0, 0);
    }

  /* Array references, calls, and certain other types must
     succeed.  */
  if (TREE_CODE (rval) == ERROR_MARK)
    switch (code)
      {
      case ARRAY_REF:
      case CALL_EXPR:
	return rval;
      default:
	return 0;
      }

  return rval;
}

/* This function takes an identifier, ID, and attempts to figure out what
   it means. There are a number of possible scenarios, presented in increasing
   order of hair:

   1) not in a class's scope
   2) in class's scope, member name of the class's method
   3) in class's scope, but not a member name of the class
   4) in class's scope, member name of a class's variable

   NAME is $1 from the bison rule. It is an IDENTIFIER_NODE.
   VALUE is $$ from the bison rule. It is the value returned by lookup_name ($1)
   yychar is the pending input character (suitably encoded :-).

   As a last ditch, try to look up the name as a label and return that
   address.

   Values which are declared as being of REFERENCE_TYPE are
   automatically dereferenced here (as a hack to make the
   compiler faster).  */

tree
hack_identifier (value, name, yychar)
     tree value, name;
{
  if (TREE_CODE (value) == ERROR_MARK)
    {
      if (current_class_name)
	{
	  tree fields = lookup_fnfields (CLASSTYPE_AS_LIST (current_class_type), name, 0);
	  if (fields)
	    {
	      fields = TREE_VALUE (TREE_VALUE (fields));
	      if (TREE_CHAIN (fields) == NULL_TREE)
		{
		  warning ("methods cannot be converted to function pointers");
		  return fields;
		}
	      else
		{
		  error ("ambiguous request for method pointer `%s'",
			 IDENTIFIER_POINTER (name));
		  return error_mark_node;
		}
	    }
	}
      if (flag_labels_ok && IDENTIFIER_LABEL_VALUE (name))
	{
	  return IDENTIFIER_LABEL_VALUE (name);
	}
      return error_mark_node;
    }

  if (TREE_NONLOCAL (value))
    {
      if (TREE_CODE (value) == FIELD_DECL)
	{
	  tree type = TREE_TYPE (value);

	  if (current_class_decl == NULL_TREE)
	    {
	      error_with_decl (value, "request for member `%s' in static member function");
	      return error_mark_node;
	    }
	  TREE_USED (current_class_decl) = 1;
	  if (yychar == '(')
	    if (! (IS_AGGR_TYPE (type)
		   && TYPE_OVERLOADS_CALL_EXPR (type))
		&& TREE_CODE (type) != FUNCTION_TYPE
		&& TREE_CODE (type) != METHOD_TYPE
		&& (TREE_CODE (type) != POINTER_TYPE
		    || TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE
		    || TREE_CODE (TREE_TYPE (type)) != METHOD_TYPE))
	    {
	      error ("component `%s' is not a method",
		     IDENTIFIER_POINTER (name));
	      return error_mark_node;
	    }
	  /* Mark so that if we are in a constructor, and then find that
	     this field was initialized by a base initializer,
	     we can emit an error message.  */
	  TREE_USED (value) = 1;
	  return build_component_ref (C_C_D, name, 0, 1);
	}

      if (TREE_CODE (value) == TREE_LIST && TREE_TYPE (value) == 0)
	{
	  error ("request for member `%s' is ambiguous in multiple inheritance lattice",
		 IDENTIFIER_POINTER (name));
	  return error_mark_node;
	}

      return value;
    }

  if (! TREE_USED (value))
    {
      if (TREE_EXTERNAL (value))
	assemble_external (value);
      TREE_USED (value) = 1;
    }
  if (TREE_CODE (value) != FUNCTION_DECL)
    {
      if (TREE_CODE (TREE_TYPE (value)) == REFERENCE_TYPE)
	{
	  assert (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == PARM_DECL);
	  if (DECL_REFERENCE_SLOT (value))
	    return DECL_REFERENCE_SLOT (value);
	}
    }
  return value;
}

tree
hack_operator (op)
     tree op;
{
  if (op == NULL_TREE)
    return get_identifier ("<missing operator>");

  if (TREE_CODE (op) != TYPE_EXPR)
    return grokopexpr (op, NULL_TREE, 0, 0);

  return op;
}

/* NONWRAPPER is nonzero if this call is not to be wrapped.
   TYPE is the type that the wrapper belongs to (in case
   it should be non-virtual.)
   DECL is the function will will be (not be) wrapped.  */
tree
hack_wrapper (nonwrapper, type, decl)
     int nonwrapper;
     tree type, decl;
{
  if (type == NULL_TREE || is_aggr_typedef_or_else (type))
    {
      if (type)
	type = TREE_TYPE (type);

      switch (nonwrapper)
	{
	case 0:
	  return build_nt (WRAPPER_EXPR, type, decl);
	case 1:
	  return build_nt (ANTI_WRAPPER_EXPR, type, decl);
	case 2:
	  return build_nt (WRAPPER_EXPR, type,
			   build_nt (COND_EXPR, decl, NULL_TREE, NULL_TREE));
	default:
	  assert (0 <= nonwrapper && nonwrapper <= 2);
	}
    }
  return error_mark_node;
}

/* Return an IDENTIFIER which can be used as a name for
   anonymous structs and unions.  */
tree
make_anon_name ()
{
  static int cnt = 0;
  char buf[32];

  sprintf (buf, ANON_AGGRNAME_FORMAT, cnt++);
  return get_identifier (buf);
}

/* Given an object OF, and a type conversion operator COMPONENT
   build a call to the conversion operator, if a call is requested,
   or return the address (as a pointer to member function) if one is not.

   OF can be a TYPE_DECL or any kind of datum that would normally
   be passed to `build_component_ref'.  It may also be NULL_TREE,
   in which case `current_class_type' and `current_class_decl'
   provide default values.

   BASETYPE_PATH, if non-null, is the path of basetypes
   to go through before we get the the instance of interest.

   PROTECT says whether we apply C++ scoping rules or not.  */
tree
build_component_type_expr (of, component, basetype_path, protect)
     tree of, component, basetype_path;
     int protect;
{
  tree cname = NULL_TREE;
  tree tmp, last;
  tree name;
  int flags = protect ? LOOKUP_NORMAL : LOOKUP_COMPLAIN;

  assert (IS_AGGR_TYPE (TREE_TYPE (of)));
  assert (TREE_CODE (component) == TYPE_EXPR);

  tmp = TREE_OPERAND (component, 0);
  last = NULL_TREE;

  while (tmp)
    {
      switch (TREE_CODE (tmp))
	{
	case CALL_EXPR:
	  if (last)
	    TREE_OPERAND (last, 0) = TREE_OPERAND (tmp, 0);
	  else
	    TREE_OPERAND (component, 0) = TREE_OPERAND (tmp, 0);
	  if (TREE_OPERAND (tmp, 0)
	      && TREE_VALUE (TREE_OPERAND (tmp, 0)) != void_type_node)
	    {
	      error ("operator <typename> requires empty parameter list");
	      TREE_OPERAND (tmp, 0) = NULL_TREE;
	    }
	  last = groktypename (build_tree_list (TREE_TYPE (component),
						TREE_OPERAND (component, 0)));
	  name = build_typename_overload (last);
	  TREE_TYPE (name) = last;

	  if (of && TREE_CODE (of) != TYPE_DECL)
	    return build_method_call (of, name, NULL_TREE, NULL_TREE, flags);
	  else if (of)
	    {
	      tree this_this;

	      if (current_class_decl == NULL_TREE)
		{
		  error ("object required for `operator <typename>' call");
		  return error_mark_node;
		}

	      this_this = convert_to_nonzero_pointer (build_pointer_type (TREE_TYPE (of)), current_class_decl);
	      return build_method_call (this_this, name, NULL_TREE,
					NULL_TREE, flags);
	    }
	  else if (current_class_decl)
	    return build_method_call (tmp, name, NULL_TREE, NULL_TREE, flags);

	  error ("object required for `operator <typename>' call");
	  return error_mark_node;

	case INDIRECT_REF:
	case ADDR_EXPR:
	case ARRAY_REF:
	  break;

	case SCOPE_REF:
	  assert (cname == 0);
	  cname = TREE_OPERAND (tmp, 0);
	  tmp = TREE_OPERAND (tmp, 1);
	  break;

	default:
	  abort ();
	}
      last = tmp;
      tmp = TREE_OPERAND (tmp, 0);
    }

  last = groktypename (build_tree_list (TREE_TYPE (component), TREE_OPERAND (component, 0)));
  name = build_typename_overload (last);
  TREE_TYPE (name) = last;
  if (of && TREE_CODE (of) == TYPE_DECL)
    {
      if (cname == NULL_TREE)
	{
	  cname = DECL_NAME (of);
	  of = NULL_TREE;
	}
      else assert (cname == DECL_NAME (of));
    }

  if (of)
    {
      tree this_this;

      if (current_class_decl == NULL_TREE)
	{
	  error ("object required for `operator <typename>' call");
	  return error_mark_node;
	}

      this_this = convert_to_nonzero_pointer (build_pointer_type (TREE_TYPE (of)), current_class_decl);
      return build_component_ref (this_this, name, 0, protect);
    }
  else if (cname)
    return build_member_ref (cname, name, protect);
  else if (current_class_name)
    return build_member_ref (current_class_name, name, protect);

  error ("object required for `operator <typename>' member reference");
  return error_mark_node;
}
