2505 lines
61 KiB
C
2505 lines
61 KiB
C
/* 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 MAX_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;
|
||
static int in_parmlist;
|
||
/* 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;
|
||
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 + MAX_INLINE_BUF_SIZE)
|
||
{
|
||
fprintf (stderr, "recompile c++ with larger MAX_INLINE_BUF_SIZE (%d)", MAX_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 + MAX_INLINE_BUF_SIZE)
|
||
{
|
||
fprintf (stderr, "recompile c++ with larger MAX_INLINE_BUF_SIZE (%d)", MAX_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_readonly_or_volatile (t)
|
||
tree t;
|
||
{
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
}
|
||
|
||
static void
|
||
dump_type_prefix (t, p)
|
||
tree t;
|
||
int *p;
|
||
{
|
||
int old_p = 0;
|
||
int print_struct = 1;
|
||
tree name;
|
||
|
||
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_CHAIN (t) != void_list_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;
|
||
}
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
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;
|
||
}
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
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;
|
||
}
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
return;
|
||
}
|
||
|
||
case REFERENCE_TYPE:
|
||
dump_type_prefix (TREE_TYPE (t), p);
|
||
OB_PUTC ('&');
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
return;
|
||
|
||
case ARRAY_TYPE:
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
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;
|
||
}
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
return;
|
||
|
||
case IDENTIFIER_NODE:
|
||
sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
|
||
break;
|
||
|
||
case RECORD_TYPE:
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
|
||
print_struct = 0;
|
||
name = TYPE_NAME (t);
|
||
if (TREE_CODE (name) == TYPE_DECL)
|
||
name = DECL_NAME (name);
|
||
if (print_struct)
|
||
sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (name));
|
||
else
|
||
sprintf (inline_bufp, "class %s ", IDENTIFIER_POINTER (name));
|
||
break;
|
||
|
||
case UNION_TYPE:
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
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:
|
||
if (TREE_READONLY (t) | TREE_VOLATILE (t))
|
||
dump_readonly_or_volatile (t);
|
||
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:
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
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:
|
||
if (TREE_READONLY (t))
|
||
OB_PUTS ("const ");
|
||
if (TREE_VOLATILE (t))
|
||
OB_PUTS ("volatile ");
|
||
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 (next_arg != void_list_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 (next_arg != void_list_node)
|
||
{
|
||
in_parmlist++;
|
||
dump_type (next_arg, &old_p);
|
||
in_parmlist--;
|
||
}
|
||
}
|
||
else OB_PUTS ("...");
|
||
OB_PUTC (')');
|
||
dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))));
|
||
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) && TYPE_ARG_TYPES (t) != void_list_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_type (t, p)
|
||
tree t;
|
||
int *p;
|
||
{
|
||
int old_p = 0;
|
||
int print_struct = 1;
|
||
|
||
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_CHAIN (t) != void_list_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);
|
||
if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
|
||
print_struct = 0;
|
||
t = TYPE_NAME (t);
|
||
if (TREE_CODE (t) == TYPE_DECL)
|
||
t = DECL_NAME (t);
|
||
if (print_struct)
|
||
sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (t));
|
||
else
|
||
sprintf (inline_bufp, "class %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 || t != void_list_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;
|
||
{
|
||
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 SAVE_EXPR:
|
||
if (TREE_HAS_CONSTRUCTOR (t))
|
||
{
|
||
dummy = 0;
|
||
OB_PUTS ("new ");
|
||
dump_type (TREE_TYPE (TREE_TYPE (t)), &dummy);
|
||
PARM_DECL_EXPR (t) = 1;
|
||
}
|
||
else
|
||
{
|
||
sorry ("operand of SAVE_EXPR not understood");
|
||
*inline_errp = '\0';
|
||
inline_bufp = inline_errp + 1;
|
||
}
|
||
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;
|
||
|
||
case CONSTRUCTOR:
|
||
OB_PUTC ('{');
|
||
dump_init_list (CONSTRUCTOR_ELTS (t));
|
||
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);
|
||
}
|
||
|
||
#ifdef DO_METHODS_THE_OLD_WAY
|
||
/* 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;
|
||
inline_buffer = (char *)alloca (MAX_INLINE_BUF_SIZE + 4);
|
||
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 (fntype) == 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 || typelist != void_list_node)
|
||
OB_PUTS ("...");
|
||
}
|
||
OB_PUTC (')');
|
||
|
||
if (! OPERATOR_TYPENAME_P (name))
|
||
dump_type_suffix (TREE_TYPE (fntype), &p);
|
||
if (TREE_CODE (fntype) == METHOD_TYPE)
|
||
dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))));
|
||
{
|
||
extern tree value_identifier;
|
||
|
||
if (DECL_RESULT (fndecl) != value_identifier)
|
||
{
|
||
tree result = DECL_RESULT (fndecl);
|
||
|
||
OB_PUTS ("return ");
|
||
OB_PUTS (IDENTIFIER_POINTER (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;
|
||
}
|
||
#endif
|
||
|
||
#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;
|
||
|
||
inline_buffer = buf;
|
||
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++ = ':';
|
||
if (TREE_CODE (fntype) == METHOD_TYPE && parmtypes)
|
||
parmtypes = TREE_CHAIN (parmtypes);
|
||
if (DECL_CONSTRUCTOR_FOR_VBASE_P (fndecl))
|
||
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));
|
||
/* Skip past "in_charge" identifier. */
|
||
if (TYPE_USES_VIRTUAL_BASECLASSES (TREE_TYPE (cname)))
|
||
parmtypes = TREE_CHAIN (parmtypes);
|
||
}
|
||
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 (parmtypes != void_list_node)
|
||
spaces = 2;
|
||
while (parmtypes && parmtypes != void_list_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);
|
||
|
||
if (TREE_CODE (fntype) == METHOD_TYPE)
|
||
dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))));
|
||
|
||
OB_FINISH ();
|
||
check_text_len (inline_bufp);
|
||
|
||
if (strlen (buf) >= OVERLOAD_MAX_LEN)
|
||
{
|
||
fprintf (stderr, "fndecl_as_string returns something too large");
|
||
abort ();
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
#ifdef FIELD_XREF
|
||
|
||
char *
|
||
type_as_string (buf, typ)
|
||
char *buf;
|
||
tree typ;
|
||
{
|
||
int p = 0;
|
||
int spaces = 0;
|
||
|
||
inline_buffer = buf;
|
||
OB_INIT ();
|
||
|
||
dump_type(typ,&p);
|
||
|
||
OB_FINISH ();
|
||
|
||
return buf;
|
||
}
|
||
|
||
#endif
|
||
|
||
/* 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;
|
||
tree args;
|
||
|
||
t2 = DECL_PENDING_INLINE_INFO (fndecl);
|
||
t2->next = pending_inlines;
|
||
t2->fndecl = fndecl;
|
||
args = DECL_ARGUMENTS (fndecl);
|
||
while (args)
|
||
{
|
||
DECL_CONTEXT (args) = fndecl;
|
||
args = TREE_CHAIN (args);
|
||
}
|
||
#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 (type, friend_list)
|
||
tree type, friend_list;
|
||
{
|
||
tree cname = DECL_NAME (TYPE_NAME (type));
|
||
tree method_vec = CLASSTYPE_METHOD_VEC (type);
|
||
if (method_vec != 0)
|
||
{
|
||
tree *methods = &TREE_VEC_ELT (method_vec, 0);
|
||
tree *end = TREE_VEC_END (method_vec);
|
||
while (methods != end)
|
||
{
|
||
/* Do inline member functions. */
|
||
tree method = *methods;
|
||
while (method)
|
||
{
|
||
prepare_inline (cname, method);
|
||
method = TREE_CHAIN (method);
|
||
}
|
||
methods++;
|
||
}
|
||
}
|
||
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 == -3)
|
||
{
|
||
if (TREE_READONLY (TREE_TYPE (TREE_VALUE (parmtypes))))
|
||
error ("call to const %s `%s' with non-const object", name_kind, err_name);
|
||
else
|
||
error ("call to non-const %s `%s' with const object", name_kind, err_name);
|
||
return;
|
||
}
|
||
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. */
|
||
assert (get_base_type (DECL_CONTEXT (cp->function), TREE_TYPE (TREE_TYPE (TREE_VALUE (parmtypes))), 1) == error_mark_node);
|
||
return;
|
||
}
|
||
}
|
||
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 + strlen (inline_buffer) + 1;
|
||
inline_buffer = inline_bufp;
|
||
|
||
/* Reset `i' so that type printing routines do the right thing. */
|
||
if (tta)
|
||
{
|
||
enum tree_code code = TREE_CODE (TREE_TYPE (TREE_VALUE (tta)));
|
||
if (code == ERROR_MARK)
|
||
OB_PUTS ("(failed type instatiation)");
|
||
else
|
||
{
|
||
i = (code == FUNCTION_TYPE || code == 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 starts. */
|
||
|
||
#define OVERLOAD_MAX_LEN 1024
|
||
|
||
/* Array of types seen so far in top-level call to `build_overload_name'.
|
||
Allocated and deallocated by caller. */
|
||
static tree *typevec;
|
||
|
||
/* Number of types interned by `build_overload_name' so far. */
|
||
static int maxtype;
|
||
|
||
/* Number of occurances of last type seen. */
|
||
static int nrepeats;
|
||
|
||
/* Nonzero if we should not try folding parameter types. */
|
||
static int nofold;
|
||
|
||
#define ALLOCATE_TYPEVEC(PARMTYPES) \
|
||
do { maxtype = 0, nrepeats = 0; \
|
||
typevec = (tree *)alloca (list_length (PARMTYPES) * sizeof (tree)); } while (0)
|
||
|
||
#define DEALLOCATE_TYPEVEC(PARMTYPES) \
|
||
do { tree t = (PARMTYPES); \
|
||
while (t) { TREE_USED (TREE_VALUE (t)) = 0; t = TREE_CHAIN (t); } \
|
||
} while (0)
|
||
|
||
/* Code to concatenate an asciified integer to a string,
|
||
and return the end of the string. */
|
||
static
|
||
#ifdef __GNUC__
|
||
__inline
|
||
#endif
|
||
char *
|
||
icat (s, i)
|
||
char *s;
|
||
int i;
|
||
{
|
||
if (i < 10)
|
||
{
|
||
*s++ = '0' + i;
|
||
return s;
|
||
}
|
||
s = icat (s, i / 10);
|
||
*s++ = '0' + (i % 10);
|
||
return s;
|
||
}
|
||
|
||
static
|
||
#ifdef __GNUC__
|
||
__inline
|
||
#endif
|
||
char *
|
||
flush_repeats (s, type)
|
||
char *s;
|
||
tree type;
|
||
{
|
||
int tindex = 0;
|
||
char *rval;
|
||
|
||
while (typevec[tindex] != type)
|
||
tindex++;
|
||
|
||
if (nrepeats > 1)
|
||
{
|
||
*s++ = 'N';
|
||
s = icat (s, nrepeats);
|
||
if (nrepeats > 9)
|
||
*s++ = '_';
|
||
}
|
||
else
|
||
*s++ = 'T';
|
||
nrepeats = 0;
|
||
rval = icat (s, tindex);
|
||
if (tindex > 9)
|
||
*rval++ = '_';
|
||
return rval;
|
||
}
|
||
|
||
/* 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.
|
||
|
||
Caller must deal with whether a final `e' goes on the end or not.
|
||
|
||
Any default conversions must take place before this function
|
||
is called. */
|
||
|
||
static char *
|
||
build_overload_name (parmtypes, text, text_end)
|
||
tree parmtypes;
|
||
char *text, *text_end;
|
||
{
|
||
char *textp = text;
|
||
int just_one;
|
||
tree parmtype;
|
||
|
||
if (just_one = (TREE_CODE (parmtypes) != TREE_LIST))
|
||
{
|
||
parmtype = parmtypes;
|
||
goto only_one;
|
||
}
|
||
|
||
while (parmtypes)
|
||
{
|
||
if (text_end - text < 4)
|
||
fatal ("Out of string space in build_overload_name!");
|
||
parmtype = TREE_VALUE (parmtypes);
|
||
|
||
only_one:
|
||
|
||
if (! nofold)
|
||
{
|
||
if (! just_one)
|
||
/* Every argument gets counted. */
|
||
typevec[maxtype++] = parmtype;
|
||
|
||
if (TREE_USED (parmtype))
|
||
{
|
||
if (! just_one && parmtype == typevec[maxtype-2])
|
||
nrepeats++;
|
||
else
|
||
{
|
||
if (nrepeats)
|
||
textp = flush_repeats (textp, parmtype);
|
||
if (! just_one && TREE_CHAIN (parmtypes)
|
||
&& parmtype == TREE_VALUE (TREE_CHAIN (parmtypes)))
|
||
nrepeats++;
|
||
else
|
||
{
|
||
int tindex = 0;
|
||
|
||
while (typevec[tindex] != parmtype)
|
||
tindex++;
|
||
*textp++ = 'T';
|
||
textp = icat (textp, tindex);
|
||
if (tindex > 9)
|
||
*textp++ = '_';
|
||
}
|
||
}
|
||
goto next;
|
||
}
|
||
if (nrepeats)
|
||
textp = flush_repeats (textp, typevec[maxtype-2]);
|
||
if (! just_one
|
||
/* Only cache types which take more than one character. */
|
||
&& (parmtype != TYPE_MAIN_VARIANT (parmtype)
|
||
|| (TREE_CODE (parmtype) != INTEGER_TYPE
|
||
&& TREE_CODE (parmtype) != REAL_TYPE)))
|
||
TREE_USED (parmtype) = 1;
|
||
}
|
||
|
||
if (TREE_READONLY (parmtype))
|
||
*textp++ = 'C';
|
||
if (TREE_CODE (parmtype) == INTEGER_TYPE && TREE_UNSIGNED (parmtype))
|
||
*textp++ = 'U';
|
||
if (TREE_VOLATILE (parmtype))
|
||
*textp++ = 'V';
|
||
|
||
switch (TREE_CODE (parmtype))
|
||
{
|
||
case OFFSET_TYPE:
|
||
*textp++ = 'O';
|
||
textp = build_overload_name (TYPE_OFFSET_BASETYPE (parmtype), textp, text_end);
|
||
*textp++ = '_';
|
||
textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
|
||
break;
|
||
|
||
case REFERENCE_TYPE:
|
||
*textp++ = 'R';
|
||
goto more;
|
||
|
||
case ARRAY_TYPE:
|
||
#ifdef PARM_CAN_BE_ARRAY_TYPE
|
||
{
|
||
tree length;
|
||
|
||
*textp++ = 'A';
|
||
length = array_type_nelts (parmtype);
|
||
if (TREE_CODE (length) == INTEGER_CST)
|
||
textp = icat (textp, TREE_INT_CST_LOW (length));
|
||
*textp++ = '_';
|
||
goto more;
|
||
}
|
||
#else
|
||
*textp++ = 'P';
|
||
goto more;
|
||
#endif
|
||
|
||
case POINTER_TYPE:
|
||
*textp++ = 'P';
|
||
more:
|
||
textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
|
||
break;
|
||
|
||
case FUNCTION_TYPE:
|
||
case METHOD_TYPE:
|
||
{
|
||
tree firstarg = TYPE_ARG_TYPES (parmtype);
|
||
/* Otherwise have to implement reentrant typevecs,
|
||
unmark and remark types, etc. */
|
||
int old_nofold = nofold;
|
||
nofold = 1;
|
||
|
||
if (nrepeats)
|
||
textp = flush_repeats (textp, typevec[maxtype-1]);
|
||
|
||
/* @@ It may be possible to pass a function type in
|
||
which is not preceded by a 'P'. */
|
||
if (TREE_CODE (parmtype) == FUNCTION_TYPE)
|
||
{
|
||
*textp++ = 'F';
|
||
if (firstarg == NULL_TREE)
|
||
*textp++ = 'e';
|
||
else if (firstarg == void_list_node)
|
||
*textp++ = 'v';
|
||
else
|
||
textp = build_overload_name (firstarg, textp, text_end);
|
||
}
|
||
else
|
||
{
|
||
int constp = TREE_READONLY (TREE_TYPE (TREE_VALUE (firstarg)));
|
||
int volatilep = TREE_VOLATILE (TREE_TYPE (TREE_VALUE (firstarg)));
|
||
*textp++ = 'M';
|
||
firstarg = TREE_CHAIN (firstarg);
|
||
|
||
textp = build_overload_name (TYPE_METHOD_BASETYPE (parmtype), textp, text_end);
|
||
if (constp)
|
||
*textp++ = 'C';
|
||
if (volatilep)
|
||
*textp++ = 'V';
|
||
|
||
/* For cfront 2.0 compatability. */
|
||
*textp++ = 'F';
|
||
|
||
if (firstarg == NULL_TREE)
|
||
*textp++ = 'e';
|
||
else if (firstarg == void_list_node)
|
||
*textp++ = 'v';
|
||
else
|
||
textp = build_overload_name (firstarg, textp, text_end);
|
||
}
|
||
|
||
/* Separate args from return type. */
|
||
*textp++ = '_';
|
||
textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
|
||
nofold = old_nofold;
|
||
break;
|
||
}
|
||
|
||
case INTEGER_TYPE:
|
||
parmtype = TYPE_MAIN_VARIANT (parmtype);
|
||
switch (TYPE_MODE (parmtype))
|
||
{
|
||
case TImode:
|
||
if (parmtype == long_integer_type_node
|
||
|| parmtype == long_unsigned_type_node)
|
||
*textp++ = 'l';
|
||
else
|
||
*textp++ = 'q';
|
||
break;
|
||
case DImode:
|
||
if (parmtype == long_integer_type_node
|
||
|| parmtype == long_unsigned_type_node)
|
||
*textp++ = 'l';
|
||
else if (parmtype == integer_type_node
|
||
|| parmtype == unsigned_type_node)
|
||
*textp++ = 'i';
|
||
else if (parmtype == short_integer_type_node
|
||
|| parmtype == short_unsigned_type_node)
|
||
*textp++ = 's';
|
||
else
|
||
*textp++ = 'x';
|
||
break;
|
||
case SImode:
|
||
if (parmtype == long_integer_type_node
|
||
|| parmtype == long_unsigned_type_node)
|
||
*textp++ = 'l';
|
||
else if (parmtype == short_integer_type_node
|
||
|| parmtype == short_unsigned_type_node)
|
||
*textp++ = 's';
|
||
else
|
||
*textp++ = 'i';
|
||
break;
|
||
case HImode:
|
||
if (parmtype == integer_type_node
|
||
|| parmtype == unsigned_type_node)
|
||
*textp++ = 'i';
|
||
else
|
||
*textp++ = 's';
|
||
break;
|
||
case QImode:
|
||
*textp++ = 'c';
|
||
break;
|
||
default:
|
||
abort ();
|
||
}
|
||
break;
|
||
|
||
case REAL_TYPE:
|
||
parmtype = TYPE_MAIN_VARIANT (parmtype);
|
||
if (parmtype == long_double_type_node)
|
||
*textp++ = 'r';
|
||
else if (parmtype == double_type_node)
|
||
*textp++ = 'd';
|
||
else if (parmtype == float_type_node)
|
||
*textp++ = 'f';
|
||
else abort ();
|
||
break;
|
||
|
||
case VOID_TYPE:
|
||
if (! just_one)
|
||
{
|
||
#if 0
|
||
extern tree void_list_node;
|
||
|
||
/* See if anybody is wasting memory. */
|
||
assert (parmtypes == void_list_node);
|
||
#endif
|
||
/* This is the end of a parameter list. */
|
||
*textp = '\0';
|
||
return textp;
|
||
}
|
||
*textp++ = 'v';
|
||
break;
|
||
|
||
case ERROR_MARK: /* not right, but nothing is anyway */
|
||
break;
|
||
|
||
/* have to do these */
|
||
case UNION_TYPE:
|
||
case RECORD_TYPE:
|
||
if (! just_one)
|
||
/* Make this type signature look incompatible
|
||
with AT&T. */
|
||
*textp++ = 'G';
|
||
goto common;
|
||
case ENUMERAL_TYPE:
|
||
common:
|
||
{
|
||
tree name = TYPE_NAME (parmtype);
|
||
if (TREE_CODE (name) == TYPE_DECL)
|
||
name = DECL_NAME (name);
|
||
assert (TREE_CODE (name) == IDENTIFIER_NODE);
|
||
textp = icat (textp, IDENTIFIER_LENGTH (name));
|
||
strcpy (textp, IDENTIFIER_POINTER (name));
|
||
textp += IDENTIFIER_LENGTH (name);
|
||
break;
|
||
}
|
||
|
||
case UNKNOWN_TYPE:
|
||
/* This will take some work. */
|
||
*textp++ = '?';
|
||
break;
|
||
|
||
default:
|
||
abort ();
|
||
}
|
||
|
||
next:
|
||
if (just_one) break;
|
||
parmtypes = TREE_CHAIN (parmtypes);
|
||
}
|
||
if (! just_one)
|
||
{
|
||
if (nrepeats)
|
||
textp = flush_repeats (textp, typevec[maxtype-1]);
|
||
|
||
/* To get here, parms must end with `...'. */
|
||
*textp++ = 'e';
|
||
}
|
||
|
||
*textp = '\0';
|
||
return textp;
|
||
}
|
||
|
||
/* 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).
|
||
|
||
FOR_METHOD is 1 if this overload is being performed
|
||
for a method, rather than a function type. It is 2 if
|
||
this overload is being performed for a constructor. */
|
||
tree
|
||
build_decl_overload (name, parms, for_method)
|
||
char *name;
|
||
tree parms;
|
||
int for_method;
|
||
{
|
||
int tmp;
|
||
char tname[OVERLOAD_MAX_LEN];
|
||
|
||
if (for_method == 2)
|
||
/* We can divine that this is a constructor,
|
||
and figure out its name without any extra encoding. */
|
||
tmp = 0;
|
||
else
|
||
{
|
||
strcpy (tname, name);
|
||
tmp = strlen (tname);
|
||
}
|
||
tname[tmp++] = '_';
|
||
tname[tmp++] = '_';
|
||
if (for_method)
|
||
{
|
||
#if 0
|
||
/* We can get away without doing this. */
|
||
tname[tmp++] = 'M';
|
||
#endif
|
||
parms = temp_tree_cons (NULL_TREE, TREE_TYPE (TREE_VALUE (parms)), TREE_CHAIN (parms));
|
||
}
|
||
else
|
||
tname[tmp++] = 'F';
|
||
|
||
if (parms == NULL_TREE)
|
||
tname[tmp++] = 'e', tname[tmp] = '\0';
|
||
else if (parms == void_list_node)
|
||
tname[tmp++] = 'v', tname[tmp] = '\0';
|
||
else
|
||
{
|
||
ALLOCATE_TYPEVEC (parms);
|
||
nofold = 0;
|
||
if (for_method)
|
||
{
|
||
tmp = build_overload_name (TREE_VALUE (parms), tname+tmp, &tname[OVERLOAD_MAX_LEN]) - tname;
|
||
|
||
#ifndef LONGERNAMES
|
||
typevec[maxtype++] = TREE_VALUE (parms);
|
||
TREE_USED (TREE_VALUE (parms)) = 1;
|
||
#endif
|
||
|
||
if (TREE_CHAIN (parms))
|
||
build_overload_name (TREE_CHAIN (parms), tname+tmp, &tname[OVERLOAD_MAX_LEN]);
|
||
else
|
||
{
|
||
tname[tmp++] = 'e';
|
||
tname[tmp] = '\0';
|
||
}
|
||
}
|
||
else
|
||
build_overload_name (parms, tname+tmp, &tname[OVERLOAD_MAX_LEN]);
|
||
DEALLOCATE_TYPEVEC (parms);
|
||
}
|
||
return get_identifier (tname);
|
||
}
|
||
|
||
/* Build an overload name for the type expression TYPE. */
|
||
tree
|
||
build_typename_overload (type)
|
||
tree type;
|
||
{
|
||
char tname[OVERLOAD_MAX_LEN];
|
||
int i = sizeof (OPERATOR_TYPENAME_FORMAT) - 1;
|
||
sprintf (tname, OPERATOR_TYPENAME_FORMAT);
|
||
#if 0
|
||
/* We can get away without doing this--it really gets
|
||
overloaded later. */
|
||
tname[i++] = '_';
|
||
tname[i++] = '_';
|
||
tname[i++] = 'M';
|
||
#endif
|
||
nofold = 1;
|
||
build_overload_name (type, tname + i, &tname[OVERLOAD_MAX_LEN]);
|
||
return get_identifier (tname);
|
||
}
|
||
|
||
/* 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 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, flags, xarg1, xarg2, arg3)
|
||
enum tree_code code;
|
||
tree xarg1, xarg2;
|
||
tree arg3;
|
||
{
|
||
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;
|
||
|
||
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;
|
||
|
||
/* First, see if we can work with the first argument */
|
||
type1 = TREE_TYPE (xarg1);
|
||
|
||
/* 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;
|
||
binary_is_unary = 0;
|
||
try_second = 0;
|
||
break;
|
||
|
||
case NEW_EXPR:
|
||
{
|
||
/* For operators `new' (`delete'), only check visibility
|
||
if we are in a constructor (destructor), and we are
|
||
allocating for that constructor's (destructor's) type. */
|
||
|
||
fnname = get_identifier (OPERATOR_NEW_FORMAT);
|
||
if (flags & LOOKUP_GLOBAL)
|
||
return build_overload_call (fnname, tree_cons (NULL_TREE, xarg2, arg3),
|
||
flags & LOOKUP_COMPLAIN, 0);
|
||
|
||
if (current_function_decl == NULL_TREE
|
||
|| !DECL_CONSTRUCTOR_P (current_function_decl)
|
||
|| current_class_type != TYPE_MAIN_VARIANT (type1))
|
||
flags = LOOKUP_COMPLAIN;
|
||
rval = build_method_call (build1 (NOP_EXPR, xarg1, error_mark_node),
|
||
fnname, tree_cons (NULL_TREE, xarg2, arg3),
|
||
NULL_TREE, flags);
|
||
if (rval == error_mark_node)
|
||
/* User might declare fancy operator new, but invoke it
|
||
like standard one. */
|
||
return rval;
|
||
|
||
TREE_TYPE (rval) = xarg1;
|
||
TREE_CALLS_NEW (rval) = 1;
|
||
return rval;
|
||
}
|
||
break;
|
||
|
||
case DELETE_EXPR:
|
||
{
|
||
/* See comment above. */
|
||
|
||
fnname = get_identifier (OPERATOR_DELETE_FORMAT);
|
||
if (flags & LOOKUP_GLOBAL)
|
||
return build_overload_call (fnname, build_tree_list (NULL_TREE, xarg1),
|
||
flags & LOOKUP_COMPLAIN, 0);
|
||
|
||
if (current_function_decl == NULL_TREE
|
||
|| !DESTRUCTOR_NAME_P (DECL_NAME (current_function_decl))
|
||
|| current_class_type != TYPE_MAIN_VARIANT (type1))
|
||
flags = LOOKUP_COMPLAIN;
|
||
rval = build_method_call (build1 (NOP_EXPR, TREE_TYPE (xarg1), error_mark_node),
|
||
fnname, build_tree_list (NULL_TREE, xarg1),
|
||
NULL_TREE, flags);
|
||
/* This happens when the user mis-declares `operator delete'.
|
||
Should now be impossible. */
|
||
assert (rval != 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;
|
||
|
||
/* 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 (type1 == unknown_type_node
|
||
|| (try_second && TREE_TYPE (xarg2) == unknown_type_node))
|
||
{
|
||
/* This will not be implemented in the forseeable future. */
|
||
return rval;
|
||
}
|
||
|
||
if (code == MODIFY_EXPR)
|
||
{
|
||
tree op_id = build_opid (MODIFY_EXPR, arg3);
|
||
fnname = build_operator_fnname (&op_id, 0, 2);
|
||
}
|
||
else
|
||
{
|
||
tree op_id = build_opid (0, code);
|
||
if (binary_is_unary)
|
||
fnname = build_operator_fnname (&op_id, 0, 1);
|
||
else
|
||
fnname = build_operator_fnname (&op_id, 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 OFFSET_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) == OFFSET_REF
|
||
&& TREE_CODE (TREE_OPERAND (xarg1, 1)) == VAR_DECL)
|
||
xarg1 = TREE_OPERAND (xarg1, 1);
|
||
if (try_second && xarg2 && TREE_CODE (xarg2) == OFFSET_REF
|
||
&& TREE_CODE (TREE_OPERAND (xarg2, 1)) == VAR_DECL)
|
||
xarg2 = TREE_OPERAND (xarg2, 1);
|
||
|
||
if (global_fn)
|
||
flags |= LOOKUP_GLOBAL;
|
||
|
||
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 = build_overload_call (fnname, parms, flags & LOOKUP_COMPLAIN, 0);
|
||
}
|
||
|
||
/* If we did not win, do not lose yet, since type conversion may work. */
|
||
if (TREE_CODE (rval) == ERROR_MARK)
|
||
{
|
||
if (flags & LOOKUP_COMPLAIN)
|
||
return rval;
|
||
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;
|
||
{
|
||
tree type;
|
||
|
||
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 (fields);
|
||
assert (TREE_CODE (fields) == FUNCTION_DECL);
|
||
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;
|
||
}
|
||
|
||
type = TREE_TYPE (value);
|
||
if (TREE_NONLOCAL (value))
|
||
{
|
||
if (TREE_CODE (value) == FIELD_DECL)
|
||
{
|
||
if (current_class_decl == NULL_TREE)
|
||
{
|
||
error ("request for member `%s' in static member function",
|
||
IDENTIFIER_POINTER (DECL_NAME (value)));
|
||
return error_mark_node;
|
||
}
|
||
TREE_USED (current_class_decl) = 1;
|
||
if (yychar == '(')
|
||
if (! ((TYPE_LANG_SPECIFIC (type)
|
||
&& TYPE_OVERLOADS_CALL_EXPR (type))
|
||
|| (TREE_CODE (type) == REFERENCE_TYPE
|
||
&& TYPE_LANG_SPECIFIC (TREE_TYPE (type))
|
||
&& TYPE_OVERLOADS_CALL_EXPR (TREE_TYPE (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 (DECL_CONTEXT (value) != current_class_type
|
||
&& (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == CONST_DECL))
|
||
{
|
||
tree path;
|
||
enum visibility_type visibility;
|
||
|
||
get_base_distance (DECL_CONTEXT (value), current_class_type, 0, &path);
|
||
visibility = compute_visibility (path, value);
|
||
if (visibility != visibility_public)
|
||
{
|
||
if (TREE_CODE (value) == VAR_DECL)
|
||
error ("static member `%s' is from private base class",
|
||
IDENTIFIER_POINTER (name));
|
||
else
|
||
error ("enum `%s' is from private base class",
|
||
IDENTIFIER_POINTER (name));
|
||
return error_mark_node;
|
||
}
|
||
}
|
||
else if (TREE_CODE (value) == TREE_LIST && type == 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 (type) == REFERENCE_TYPE)
|
||
{
|
||
assert (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == PARM_DECL
|
||
|| TREE_CODE (value) == RESULT_DECL);
|
||
if (DECL_REFERENCE_SLOT (value))
|
||
return DECL_REFERENCE_SLOT (value);
|
||
}
|
||
return value;
|
||
}
|
||
|
||
tree
|
||
hack_operator (op)
|
||
tree op;
|
||
{
|
||
if (op == NULL_TREE)
|
||
return error_mark_node;
|
||
|
||
if (TREE_CODE (op) != TYPE_EXPR)
|
||
return grokopexpr (&op, NULL_TREE, 0, 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 (type, 1))
|
||
{
|
||
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_OPERAND (tmp, 0) != void_list_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_pointer_to (TREE_TYPE (of), current_class_decl);
|
||
return build_method_call (this_this, name, NULL_TREE,
|
||
NULL_TREE, flags | LOOKUP_NONVIRTUAL);
|
||
}
|
||
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_pointer_to (TREE_TYPE (of), current_class_decl);
|
||
return build_component_ref (this_this, name, 0, protect);
|
||
}
|
||
else if (cname)
|
||
return build_offset_ref (cname, name);
|
||
else if (current_class_name)
|
||
return build_offset_ref (current_class_name, name);
|
||
|
||
error ("object required for `operator <typename>' member reference");
|
||
return error_mark_node;
|
||
}
|