mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
2453 lines
64 KiB
C
2453 lines
64 KiB
C
/* Call-backs for C++ error reporting.
|
||
This code is non-reentrant.
|
||
Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002,
|
||
2003 Free Software Foundation, Inc.
|
||
This file is part of GCC.
|
||
|
||
GCC 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 2, or (at your option)
|
||
any later version.
|
||
|
||
GCC 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 GCC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||
Boston, MA 02111-1307, USA. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "tm.h"
|
||
#include "tree.h"
|
||
#include "cp-tree.h"
|
||
#include "real.h"
|
||
#include "toplev.h"
|
||
#include "flags.h"
|
||
#include "diagnostic.h"
|
||
#include "langhooks-def.h"
|
||
#include "cxx-pretty-print.h"
|
||
|
||
enum pad { none, before, after };
|
||
|
||
#define pp_template_argument_list_start(PP) \
|
||
pp_non_consecutive_character (PP, '<')
|
||
#define pp_template_argument_list_end(PP) \
|
||
pp_non_consecutive_character (PP, '>')
|
||
#define pp_separate_with_comma(PP) pp_string (PP, ", ")
|
||
|
||
/* The global buffer where we dump everything. It is there only for
|
||
transitional purpose. It is expected, in the near future, to be
|
||
completely removed. */
|
||
static cxx_pretty_printer scratch_pretty_printer;
|
||
#define cxx_pp (&scratch_pretty_printer)
|
||
|
||
# define NEXT_CODE(T) (TREE_CODE (TREE_TYPE (T)))
|
||
|
||
#define reinit_global_formatting_buffer() \
|
||
output_clear_message_text (scratch_buffer)
|
||
|
||
static const char *args_to_string (tree, int);
|
||
static const char *assop_to_string (enum tree_code);
|
||
static const char *code_to_string (enum tree_code);
|
||
static const char *cv_to_string (tree, int);
|
||
static const char *decl_to_string (tree, int);
|
||
static const char *expr_to_string (tree);
|
||
static const char *fndecl_to_string (tree, int);
|
||
static const char *op_to_string (enum tree_code);
|
||
static const char *parm_to_string (int);
|
||
static const char *type_to_string (tree, int);
|
||
|
||
static void dump_type (tree, int);
|
||
static void dump_typename (tree, int);
|
||
static void dump_simple_decl (tree, tree, int);
|
||
static void dump_decl (tree, int);
|
||
static void dump_template_decl (tree, int);
|
||
static void dump_function_decl (tree, int);
|
||
static void dump_expr (tree, int);
|
||
static void dump_unary_op (const char *, tree, int);
|
||
static void dump_binary_op (const char *, tree, int);
|
||
static void dump_aggr_type (tree, int);
|
||
static enum pad dump_type_prefix (tree, int);
|
||
static void dump_type_suffix (tree, int);
|
||
static void dump_function_name (tree, int);
|
||
static void dump_expr_list (tree, int);
|
||
static void dump_global_iord (tree);
|
||
static enum pad dump_qualifiers (tree, enum pad);
|
||
static void dump_parameters (tree, int);
|
||
static void dump_exception_spec (tree, int);
|
||
static const char *class_key_or_enum (tree);
|
||
static void dump_template_argument (tree, int);
|
||
static void dump_template_argument_list (tree, int);
|
||
static void dump_template_parameter (tree, int);
|
||
static void dump_template_bindings (tree, tree);
|
||
static void dump_scope (tree, int);
|
||
static void dump_template_parms (tree, int, int);
|
||
|
||
static const char *function_category (tree);
|
||
static void maybe_print_instantiation_context (diagnostic_context *);
|
||
static void print_instantiation_full_context (diagnostic_context *);
|
||
static void print_instantiation_partial_context (diagnostic_context *,
|
||
tree, location_t);
|
||
static void cp_diagnostic_starter (diagnostic_context *, diagnostic_info *);
|
||
static void cp_diagnostic_finalizer (diagnostic_context *, diagnostic_info *);
|
||
static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
|
||
|
||
static bool cp_printer (pretty_printer *, text_info *);
|
||
static void pp_non_consecutive_character (cxx_pretty_printer *, int);
|
||
static tree locate_error (const char *, va_list);
|
||
static location_t location_of (tree);
|
||
|
||
void
|
||
init_error (void)
|
||
{
|
||
diagnostic_starter (global_dc) = cp_diagnostic_starter;
|
||
diagnostic_finalizer (global_dc) = cp_diagnostic_finalizer;
|
||
diagnostic_format_decoder (global_dc) = cp_printer;
|
||
|
||
pp_construct (pp_base (cxx_pp), NULL, 0);
|
||
pp_cxx_pretty_printer_init (cxx_pp);
|
||
}
|
||
|
||
/* Dump a scope, if deemed necessary. */
|
||
|
||
static void
|
||
dump_scope (tree scope, int flags)
|
||
{
|
||
int f = ~TFF_RETURN_TYPE & (flags & (TFF_SCOPE | TFF_CHASE_TYPEDEF));
|
||
|
||
if (scope == NULL_TREE)
|
||
return;
|
||
|
||
if (TREE_CODE (scope) == NAMESPACE_DECL)
|
||
{
|
||
if (scope != global_namespace)
|
||
{
|
||
dump_decl (scope, f);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
}
|
||
else if (AGGREGATE_TYPE_P (scope))
|
||
{
|
||
dump_type (scope, f);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
else if ((flags & TFF_SCOPE) && TREE_CODE (scope) == FUNCTION_DECL)
|
||
{
|
||
dump_function_decl (scope, f);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
}
|
||
|
||
/* Dump type qualifiers, providing padding as requested. Return an
|
||
indication of whether we dumped something. */
|
||
|
||
static enum pad
|
||
dump_qualifiers (tree t, enum pad p)
|
||
{
|
||
static const int masks[] =
|
||
{TYPE_QUAL_CONST, TYPE_QUAL_VOLATILE, TYPE_QUAL_RESTRICT};
|
||
static const char *const names[] =
|
||
{"const", "volatile", "__restrict"};
|
||
int ix;
|
||
int quals = TYPE_QUALS (t);
|
||
int do_after = p == after;
|
||
|
||
if (quals)
|
||
{
|
||
for (ix = 0; ix != 3; ix++)
|
||
if (masks[ix] & quals)
|
||
{
|
||
if (p == before)
|
||
pp_space (cxx_pp);
|
||
p = before;
|
||
pp_identifier (cxx_pp, names[ix]);
|
||
}
|
||
if (do_after)
|
||
pp_space (cxx_pp);
|
||
}
|
||
else
|
||
p = none;
|
||
return p;
|
||
}
|
||
|
||
/* Dump the template ARGument under control of FLAGS. */
|
||
|
||
static void
|
||
dump_template_argument (tree arg, int flags)
|
||
{
|
||
if (TYPE_P (arg) || TREE_CODE (arg) == TEMPLATE_DECL)
|
||
dump_type (arg, flags & ~TFF_CLASS_KEY_OR_ENUM);
|
||
else
|
||
dump_expr (arg, (flags | TFF_EXPR_IN_PARENS) & ~TFF_CLASS_KEY_OR_ENUM);
|
||
}
|
||
|
||
/* Dump a template-argument-list ARGS (always a TREE_VEC) under control
|
||
of FLAGS. */
|
||
|
||
static void
|
||
dump_template_argument_list (tree args, int flags)
|
||
{
|
||
int n = TREE_VEC_LENGTH (args);
|
||
int need_comma = 0;
|
||
int i;
|
||
|
||
for (i = 0; i< n; ++i)
|
||
{
|
||
if (need_comma)
|
||
pp_separate_with_comma (cxx_pp);
|
||
dump_template_argument (TREE_VEC_ELT (args, i), flags);
|
||
need_comma = 1;
|
||
}
|
||
}
|
||
|
||
/* Dump a template parameter PARM (a TREE_LIST) under control of FLAGS. */
|
||
|
||
static void
|
||
dump_template_parameter (tree parm, int flags)
|
||
{
|
||
tree p = TREE_VALUE (parm);
|
||
tree a = TREE_PURPOSE (parm);
|
||
|
||
if (TREE_CODE (p) == TYPE_DECL)
|
||
{
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
{
|
||
pp_identifier (cxx_pp, "class");
|
||
if (DECL_NAME (p))
|
||
{
|
||
pp_space (cxx_pp);
|
||
pp_tree_identifier (cxx_pp, DECL_NAME (p));
|
||
}
|
||
}
|
||
else if (DECL_NAME (p))
|
||
pp_tree_identifier (cxx_pp, DECL_NAME (p));
|
||
else
|
||
pp_cxx_canonical_template_parameter (cxx_pp, TREE_TYPE (p));
|
||
}
|
||
else
|
||
dump_decl (p, flags | TFF_DECL_SPECIFIERS);
|
||
|
||
if ((flags & TFF_FUNCTION_DEFAULT_ARGUMENTS) && a != NULL_TREE)
|
||
{
|
||
pp_string (cxx_pp, " = ");
|
||
if (TREE_CODE (p) == TYPE_DECL || TREE_CODE (p) == TEMPLATE_DECL)
|
||
dump_type (a, flags & ~TFF_CHASE_TYPEDEF);
|
||
else
|
||
dump_expr (a, flags | TFF_EXPR_IN_PARENS);
|
||
}
|
||
}
|
||
|
||
/* Dump, under control of FLAGS, a template-parameter-list binding.
|
||
PARMS is a TREE_LIST of TREE_VEC of TREE_LIST and ARGS is a
|
||
TREE_VEC. */
|
||
|
||
static void
|
||
dump_template_bindings (tree parms, tree args)
|
||
{
|
||
int need_comma = 0;
|
||
|
||
while (parms)
|
||
{
|
||
tree p = TREE_VALUE (parms);
|
||
int lvl = TMPL_PARMS_DEPTH (parms);
|
||
int arg_idx = 0;
|
||
int i;
|
||
|
||
for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
|
||
{
|
||
tree arg = NULL_TREE;
|
||
|
||
/* Don't crash if we had an invalid argument list. */
|
||
if (TMPL_ARGS_DEPTH (args) >= lvl)
|
||
{
|
||
tree lvl_args = TMPL_ARGS_LEVEL (args, lvl);
|
||
if (NUM_TMPL_ARGS (lvl_args) > arg_idx)
|
||
arg = TREE_VEC_ELT (lvl_args, arg_idx);
|
||
}
|
||
|
||
if (need_comma)
|
||
pp_separate_with_comma (cxx_pp);
|
||
dump_template_parameter (TREE_VEC_ELT (p, i), TFF_PLAIN_IDENTIFIER);
|
||
pp_string (cxx_pp, " = ");
|
||
if (arg)
|
||
dump_template_argument (arg, TFF_PLAIN_IDENTIFIER);
|
||
else
|
||
pp_identifier (cxx_pp, "<missing>");
|
||
|
||
++arg_idx;
|
||
need_comma = 1;
|
||
}
|
||
|
||
parms = TREE_CHAIN (parms);
|
||
}
|
||
}
|
||
|
||
/* Dump a human-readable equivalent of TYPE. FLAGS controls the
|
||
format. */
|
||
|
||
static void
|
||
dump_type (tree t, int flags)
|
||
{
|
||
if (t == NULL_TREE)
|
||
return;
|
||
|
||
if (TYPE_PTRMEMFUNC_P (t))
|
||
goto offset_type;
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case UNKNOWN_TYPE:
|
||
pp_identifier (cxx_pp, "<unknown type>");
|
||
break;
|
||
|
||
case TREE_LIST:
|
||
/* A list of function parms. */
|
||
dump_parameters (t, flags);
|
||
break;
|
||
|
||
case IDENTIFIER_NODE:
|
||
pp_tree_identifier (cxx_pp, t);
|
||
break;
|
||
|
||
case TREE_VEC:
|
||
dump_type (BINFO_TYPE (t), flags);
|
||
break;
|
||
|
||
case RECORD_TYPE:
|
||
case UNION_TYPE:
|
||
case ENUMERAL_TYPE:
|
||
dump_aggr_type (t, flags);
|
||
break;
|
||
|
||
case TYPE_DECL:
|
||
if (flags & TFF_CHASE_TYPEDEF)
|
||
{
|
||
dump_type (DECL_ORIGINAL_TYPE (t)
|
||
? DECL_ORIGINAL_TYPE (t) : TREE_TYPE (t), flags);
|
||
break;
|
||
}
|
||
/* Else fall through. */
|
||
|
||
case TEMPLATE_DECL:
|
||
case NAMESPACE_DECL:
|
||
dump_decl (t, flags & ~TFF_DECL_SPECIFIERS);
|
||
break;
|
||
|
||
case INTEGER_TYPE:
|
||
case REAL_TYPE:
|
||
case VOID_TYPE:
|
||
case BOOLEAN_TYPE:
|
||
case COMPLEX_TYPE:
|
||
case VECTOR_TYPE:
|
||
pp_base (cxx_pp)->padding = pp_none;
|
||
pp_type_specifier_seq (cxx_pp, t);
|
||
break;
|
||
|
||
case TEMPLATE_TEMPLATE_PARM:
|
||
/* For parameters inside template signature. */
|
||
if (TYPE_IDENTIFIER (t))
|
||
pp_tree_identifier (cxx_pp, TYPE_IDENTIFIER (t));
|
||
else
|
||
pp_cxx_canonical_template_parameter (cxx_pp, t);
|
||
break;
|
||
|
||
case BOUND_TEMPLATE_TEMPLATE_PARM:
|
||
{
|
||
tree args = TYPE_TI_ARGS (t);
|
||
dump_qualifiers (t, after);
|
||
pp_tree_identifier (cxx_pp, TYPE_IDENTIFIER (t));
|
||
pp_template_argument_list_start (cxx_pp);
|
||
dump_template_argument_list (args, flags);
|
||
pp_template_argument_list_end (cxx_pp);
|
||
}
|
||
break;
|
||
|
||
case TEMPLATE_TYPE_PARM:
|
||
dump_qualifiers (t, after);
|
||
if (TYPE_IDENTIFIER (t))
|
||
pp_tree_identifier (cxx_pp, TYPE_IDENTIFIER (t));
|
||
else
|
||
pp_cxx_canonical_template_parameter
|
||
(cxx_pp, TEMPLATE_TYPE_PARM_INDEX (t));
|
||
break;
|
||
|
||
/* This is not always necessary for pointers and such, but doing this
|
||
reduces code size. */
|
||
case ARRAY_TYPE:
|
||
case POINTER_TYPE:
|
||
case REFERENCE_TYPE:
|
||
case OFFSET_TYPE:
|
||
offset_type:
|
||
case FUNCTION_TYPE:
|
||
case METHOD_TYPE:
|
||
{
|
||
dump_type_prefix (t, flags);
|
||
dump_type_suffix (t, flags);
|
||
break;
|
||
}
|
||
case TYPENAME_TYPE:
|
||
dump_qualifiers (t, after);
|
||
pp_string (cxx_pp, "typename ");
|
||
dump_typename (t, flags);
|
||
break;
|
||
|
||
case UNBOUND_CLASS_TEMPLATE:
|
||
dump_type (TYPE_CONTEXT (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
pp_identifier (cxx_pp, "template ");
|
||
dump_type (DECL_NAME (TYPE_NAME (t)), flags);
|
||
break;
|
||
|
||
case TYPEOF_TYPE:
|
||
pp_string (cxx_pp, "__typeof (");
|
||
dump_expr (TYPE_FIELDS (t), flags & ~TFF_EXPR_IN_PARENS);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
default:
|
||
pp_unsupported_tree (cxx_pp, t);
|
||
/* Fall through to error. */
|
||
|
||
case ERROR_MARK:
|
||
pp_identifier (cxx_pp, "<type error>");
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Dump a TYPENAME_TYPE. We need to notice when the context is itself
|
||
a TYPENAME_TYPE. */
|
||
|
||
static void
|
||
dump_typename (tree t, int flags)
|
||
{
|
||
tree ctx = TYPE_CONTEXT (t);
|
||
|
||
if (TREE_CODE (ctx) == TYPENAME_TYPE)
|
||
dump_typename (ctx, flags);
|
||
else
|
||
dump_type (ctx, flags & ~TFF_CLASS_KEY_OR_ENUM);
|
||
pp_colon_colon (cxx_pp);
|
||
dump_decl (TYPENAME_TYPE_FULLNAME (t), flags);
|
||
}
|
||
|
||
/* Return the name of the supplied aggregate, or enumeral type. */
|
||
|
||
static const char *
|
||
class_key_or_enum (tree t)
|
||
{
|
||
if (TREE_CODE (t) == ENUMERAL_TYPE)
|
||
return "enum";
|
||
else if (TREE_CODE (t) == UNION_TYPE)
|
||
return "union";
|
||
else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
|
||
return "class";
|
||
else
|
||
return "struct";
|
||
}
|
||
|
||
/* Print out a class declaration T under the control of FLAGS,
|
||
in the form `class foo'. */
|
||
|
||
static void
|
||
dump_aggr_type (tree t, int flags)
|
||
{
|
||
tree name;
|
||
const char *variety = class_key_or_enum (t);
|
||
int typdef = 0;
|
||
int tmplate = 0;
|
||
|
||
dump_qualifiers (t, after);
|
||
|
||
if (flags & TFF_CLASS_KEY_OR_ENUM)
|
||
{
|
||
pp_identifier (cxx_pp, variety);
|
||
pp_space (cxx_pp);
|
||
}
|
||
|
||
if (flags & TFF_CHASE_TYPEDEF)
|
||
t = TYPE_MAIN_VARIANT (t);
|
||
|
||
name = TYPE_NAME (t);
|
||
|
||
if (name)
|
||
{
|
||
typdef = !DECL_ARTIFICIAL (name);
|
||
tmplate = !typdef && TREE_CODE (t) != ENUMERAL_TYPE
|
||
&& TYPE_LANG_SPECIFIC (t) && CLASSTYPE_TEMPLATE_INFO (t)
|
||
&& (CLASSTYPE_TEMPLATE_SPECIALIZATION (t)
|
||
|| TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
|
||
|| DECL_TEMPLATE_SPECIALIZATION (CLASSTYPE_TI_TEMPLATE (t))
|
||
|| PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
|
||
dump_scope (CP_DECL_CONTEXT (name), flags | TFF_SCOPE);
|
||
if (tmplate)
|
||
{
|
||
/* Because the template names are mangled, we have to locate
|
||
the most general template, and use that name. */
|
||
tree tpl = CLASSTYPE_TI_TEMPLATE (t);
|
||
|
||
while (DECL_TEMPLATE_INFO (tpl))
|
||
tpl = DECL_TI_TEMPLATE (tpl);
|
||
name = tpl;
|
||
}
|
||
name = DECL_NAME (name);
|
||
}
|
||
|
||
if (name == 0 || ANON_AGGRNAME_P (name))
|
||
{
|
||
if (flags & TFF_CLASS_KEY_OR_ENUM)
|
||
pp_identifier (cxx_pp, "<anonymous>");
|
||
else
|
||
pp_printf (pp_base (cxx_pp), "<anonymous %s>", variety);
|
||
}
|
||
else
|
||
pp_tree_identifier (cxx_pp, name);
|
||
if (tmplate)
|
||
dump_template_parms (TYPE_TEMPLATE_INFO (t),
|
||
!CLASSTYPE_USE_TEMPLATE (t),
|
||
flags & ~TFF_TEMPLATE_HEADER);
|
||
}
|
||
|
||
/* Dump into the obstack the initial part of the output for a given type.
|
||
This is necessary when dealing with things like functions returning
|
||
functions. Examples:
|
||
|
||
return type of `int (* fee ())()': pointer -> function -> int. Both
|
||
pointer (and reference and offset) and function (and member) types must
|
||
deal with prefix and suffix.
|
||
|
||
Arrays must also do this for DECL nodes, like int a[], and for things like
|
||
int *[]&.
|
||
|
||
Return indicates how you should pad an object name after this. I.e. you
|
||
want to pad non-*, non-& cores, but not pad * or & types. */
|
||
|
||
static enum pad
|
||
dump_type_prefix (tree t, int flags)
|
||
{
|
||
enum pad padding = before;
|
||
|
||
if (TYPE_PTRMEMFUNC_P (t))
|
||
{
|
||
t = TYPE_PTRMEMFUNC_FN_TYPE (t);
|
||
goto offset_type;
|
||
}
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case POINTER_TYPE:
|
||
case REFERENCE_TYPE:
|
||
{
|
||
tree sub = TREE_TYPE (t);
|
||
|
||
padding = dump_type_prefix (sub, flags);
|
||
if (TREE_CODE (sub) == ARRAY_TYPE)
|
||
{
|
||
pp_space (cxx_pp);
|
||
pp_left_paren (cxx_pp);
|
||
}
|
||
pp_character (cxx_pp, "&*"[TREE_CODE (t) == POINTER_TYPE]);
|
||
padding = dump_qualifiers (t, before);
|
||
}
|
||
break;
|
||
|
||
case OFFSET_TYPE:
|
||
offset_type:
|
||
padding = dump_type_prefix (TREE_TYPE (t), flags);
|
||
if (TREE_CODE (t) == OFFSET_TYPE) /* pmfs deal with this in d_t_p */
|
||
{
|
||
if (padding != none)
|
||
pp_space (cxx_pp);
|
||
dump_type (TYPE_OFFSET_BASETYPE (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
pp_star (cxx_pp);
|
||
padding = dump_qualifiers (t, none);
|
||
break;
|
||
|
||
/* Can only be reached through function pointer -- this would not be
|
||
correct if FUNCTION_DECLs used it. */
|
||
case FUNCTION_TYPE:
|
||
padding = dump_type_prefix (TREE_TYPE (t), flags);
|
||
if (padding != none)
|
||
pp_space (cxx_pp);
|
||
pp_left_paren (cxx_pp);
|
||
padding = none;
|
||
break;
|
||
|
||
case METHOD_TYPE:
|
||
padding = dump_type_prefix (TREE_TYPE (t), flags);
|
||
if (padding != none)
|
||
pp_space (cxx_pp);
|
||
pp_left_paren (cxx_pp);
|
||
padding = none;
|
||
dump_aggr_type (TYPE_METHOD_BASETYPE (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
break;
|
||
|
||
case ARRAY_TYPE:
|
||
padding = dump_type_prefix (TREE_TYPE (t), flags);
|
||
break;
|
||
|
||
case ENUMERAL_TYPE:
|
||
case IDENTIFIER_NODE:
|
||
case INTEGER_TYPE:
|
||
case BOOLEAN_TYPE:
|
||
case REAL_TYPE:
|
||
case RECORD_TYPE:
|
||
case TEMPLATE_TYPE_PARM:
|
||
case TEMPLATE_TEMPLATE_PARM:
|
||
case BOUND_TEMPLATE_TEMPLATE_PARM:
|
||
case TREE_LIST:
|
||
case TYPE_DECL:
|
||
case TREE_VEC:
|
||
case UNION_TYPE:
|
||
case UNKNOWN_TYPE:
|
||
case VOID_TYPE:
|
||
case TYPENAME_TYPE:
|
||
case COMPLEX_TYPE:
|
||
case VECTOR_TYPE:
|
||
case TYPEOF_TYPE:
|
||
dump_type (t, flags);
|
||
padding = before;
|
||
break;
|
||
|
||
default:
|
||
pp_unsupported_tree (cxx_pp, t);
|
||
/* fall through. */
|
||
case ERROR_MARK:
|
||
pp_identifier (cxx_pp, "<typeprefixerror>");
|
||
break;
|
||
}
|
||
return padding;
|
||
}
|
||
|
||
/* Dump the suffix of type T, under control of FLAGS. This is the part
|
||
which appears after the identifier (or function parms). */
|
||
|
||
static void
|
||
dump_type_suffix (tree t, int flags)
|
||
{
|
||
if (TYPE_PTRMEMFUNC_P (t))
|
||
t = TYPE_PTRMEMFUNC_FN_TYPE (t);
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case POINTER_TYPE:
|
||
case REFERENCE_TYPE:
|
||
case OFFSET_TYPE:
|
||
if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
|
||
pp_right_paren (cxx_pp);
|
||
dump_type_suffix (TREE_TYPE (t), flags);
|
||
break;
|
||
|
||
/* Can only be reached through function pointer. */
|
||
case FUNCTION_TYPE:
|
||
case METHOD_TYPE:
|
||
{
|
||
tree arg;
|
||
pp_right_paren (cxx_pp);
|
||
arg = TYPE_ARG_TYPES (t);
|
||
if (TREE_CODE (t) == METHOD_TYPE)
|
||
arg = TREE_CHAIN (arg);
|
||
|
||
/* Function pointers don't have default args. Not in standard C++,
|
||
anyway; they may in g++, but we'll just pretend otherwise. */
|
||
dump_parameters (arg, flags & ~TFF_FUNCTION_DEFAULT_ARGUMENTS);
|
||
|
||
if (TREE_CODE (t) == METHOD_TYPE)
|
||
dump_qualifiers
|
||
(TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))), before);
|
||
dump_exception_spec (TYPE_RAISES_EXCEPTIONS (t), flags);
|
||
dump_type_suffix (TREE_TYPE (t), flags);
|
||
break;
|
||
}
|
||
|
||
case ARRAY_TYPE:
|
||
pp_left_bracket (cxx_pp);
|
||
if (TYPE_DOMAIN (t))
|
||
{
|
||
if (host_integerp (TYPE_MAX_VALUE (TYPE_DOMAIN (t)), 0))
|
||
pp_wide_integer
|
||
(cxx_pp, tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (t)), 0) + 1);
|
||
else if (TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (t))) == MINUS_EXPR)
|
||
dump_expr (TREE_OPERAND (TYPE_MAX_VALUE (TYPE_DOMAIN (t)), 0),
|
||
flags & ~TFF_EXPR_IN_PARENS);
|
||
else
|
||
dump_expr (fold (cp_build_binary_op
|
||
(PLUS_EXPR, TYPE_MAX_VALUE (TYPE_DOMAIN (t)),
|
||
integer_one_node)),
|
||
flags & ~TFF_EXPR_IN_PARENS);
|
||
}
|
||
pp_right_bracket (cxx_pp);
|
||
dump_type_suffix (TREE_TYPE (t), flags);
|
||
break;
|
||
|
||
case ENUMERAL_TYPE:
|
||
case IDENTIFIER_NODE:
|
||
case INTEGER_TYPE:
|
||
case BOOLEAN_TYPE:
|
||
case REAL_TYPE:
|
||
case RECORD_TYPE:
|
||
case TEMPLATE_TYPE_PARM:
|
||
case TEMPLATE_TEMPLATE_PARM:
|
||
case BOUND_TEMPLATE_TEMPLATE_PARM:
|
||
case TREE_LIST:
|
||
case TYPE_DECL:
|
||
case TREE_VEC:
|
||
case UNION_TYPE:
|
||
case UNKNOWN_TYPE:
|
||
case VOID_TYPE:
|
||
case TYPENAME_TYPE:
|
||
case COMPLEX_TYPE:
|
||
case VECTOR_TYPE:
|
||
case TYPEOF_TYPE:
|
||
break;
|
||
|
||
default:
|
||
pp_unsupported_tree (cxx_pp, t);
|
||
case ERROR_MARK:
|
||
/* Don't mark it here, we should have already done in
|
||
dump_type_prefix. */
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
dump_global_iord (tree t)
|
||
{
|
||
const char *p = NULL;
|
||
|
||
if (DECL_GLOBAL_CTOR_P (t))
|
||
p = "initializers";
|
||
else if (DECL_GLOBAL_DTOR_P (t))
|
||
p = "destructors";
|
||
else
|
||
abort ();
|
||
|
||
pp_printf (pp_base (cxx_pp), "(static %s for %s)", p, input_filename);
|
||
}
|
||
|
||
static void
|
||
dump_simple_decl (tree t, tree type, int flags)
|
||
{
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
{
|
||
if (dump_type_prefix (type, flags) != none)
|
||
pp_space (cxx_pp);
|
||
}
|
||
if (!DECL_INITIAL (t) || TREE_CODE (DECL_INITIAL (t)) != TEMPLATE_PARM_INDEX)
|
||
dump_scope (CP_DECL_CONTEXT (t), flags);
|
||
if (DECL_NAME (t))
|
||
dump_decl (DECL_NAME (t), flags);
|
||
else
|
||
pp_identifier (cxx_pp, "<anonymous>");
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
dump_type_suffix (type, flags);
|
||
}
|
||
|
||
/* Dump a human readable string for the decl T under control of FLAGS. */
|
||
|
||
static void
|
||
dump_decl (tree t, int flags)
|
||
{
|
||
if (t == NULL_TREE)
|
||
return;
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case TYPE_DECL:
|
||
{
|
||
/* Don't say 'typedef class A' */
|
||
if (DECL_ARTIFICIAL (t))
|
||
{
|
||
if ((flags & TFF_DECL_SPECIFIERS)
|
||
&& TREE_CODE (TREE_TYPE (t)) == TEMPLATE_TYPE_PARM)
|
||
/* Say `class T' not just `T'. */
|
||
pp_string (cxx_pp, "class ");
|
||
|
||
dump_type (TREE_TYPE (t), flags);
|
||
break;
|
||
}
|
||
}
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
pp_string (cxx_pp, "typedef ");
|
||
dump_simple_decl (t, DECL_ORIGINAL_TYPE (t)
|
||
? DECL_ORIGINAL_TYPE (t) : TREE_TYPE (t),
|
||
flags);
|
||
break;
|
||
|
||
case VAR_DECL:
|
||
if (DECL_NAME (t) && VTABLE_NAME_P (DECL_NAME (t)))
|
||
{
|
||
pp_string (cxx_pp, "vtable for ");
|
||
my_friendly_assert (TYPE_P (DECL_CONTEXT (t)), 20010720);
|
||
dump_type (DECL_CONTEXT (t), flags);
|
||
break;
|
||
}
|
||
/* Else fall through. */
|
||
case FIELD_DECL:
|
||
case PARM_DECL:
|
||
case ALIAS_DECL:
|
||
dump_simple_decl (t, TREE_TYPE (t), flags);
|
||
break;
|
||
|
||
case RESULT_DECL:
|
||
pp_string (cxx_pp, "<return value> ");
|
||
dump_simple_decl (t, TREE_TYPE (t), flags);
|
||
break;
|
||
|
||
case NAMESPACE_DECL:
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
pp_cxx_declaration (cxx_pp, t);
|
||
else
|
||
{
|
||
dump_scope (CP_DECL_CONTEXT (t), flags);
|
||
if (DECL_NAME (t) == NULL_TREE)
|
||
pp_identifier (cxx_pp, "<unnamed>");
|
||
else
|
||
pp_tree_identifier (cxx_pp, DECL_NAME (t));
|
||
}
|
||
break;
|
||
|
||
case SCOPE_REF:
|
||
dump_decl (TREE_OPERAND (t, 0), flags & ~TFF_DECL_SPECIFIERS);
|
||
pp_colon_colon (cxx_pp);
|
||
dump_decl (TREE_OPERAND (t, 1), flags);
|
||
break;
|
||
|
||
case ARRAY_REF:
|
||
dump_decl (TREE_OPERAND (t, 0), flags);
|
||
pp_left_bracket (cxx_pp);
|
||
dump_decl (TREE_OPERAND (t, 1), flags);
|
||
pp_right_bracket (cxx_pp);
|
||
break;
|
||
|
||
/* So that we can do dump_decl on an aggr type. */
|
||
case RECORD_TYPE:
|
||
case UNION_TYPE:
|
||
case ENUMERAL_TYPE:
|
||
dump_type (t, flags);
|
||
break;
|
||
|
||
case BIT_NOT_EXPR:
|
||
/* This is a pseudo destructor call which has not been folded into
|
||
a PSEUDO_DTOR_EXPR yet. */
|
||
pp_complement (cxx_pp);
|
||
dump_type (TREE_OPERAND (t, 0), flags);
|
||
break;
|
||
|
||
case TYPE_EXPR:
|
||
abort ();
|
||
break;
|
||
|
||
/* These special cases are duplicated here so that other functions
|
||
can feed identifiers to error and get them demangled properly. */
|
||
case IDENTIFIER_NODE:
|
||
if (IDENTIFIER_TYPENAME_P (t))
|
||
{
|
||
pp_string (cxx_pp, "operator ");
|
||
/* Not exactly IDENTIFIER_TYPE_VALUE. */
|
||
dump_type (TREE_TYPE (t), flags);
|
||
break;
|
||
}
|
||
else
|
||
pp_tree_identifier (cxx_pp, t);
|
||
break;
|
||
|
||
case OVERLOAD:
|
||
if (OVL_CHAIN (t))
|
||
{
|
||
t = OVL_CURRENT (t);
|
||
if (DECL_CLASS_SCOPE_P (t))
|
||
{
|
||
dump_type (DECL_CONTEXT (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
else if (DECL_CONTEXT (t))
|
||
{
|
||
dump_decl (DECL_CONTEXT (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
dump_decl (DECL_NAME (t), flags);
|
||
break;
|
||
}
|
||
|
||
/* If there's only one function, just treat it like an ordinary
|
||
FUNCTION_DECL. */
|
||
t = OVL_CURRENT (t);
|
||
/* Fall through. */
|
||
|
||
case FUNCTION_DECL:
|
||
if (DECL_GLOBAL_CTOR_P (t) || DECL_GLOBAL_DTOR_P (t))
|
||
dump_global_iord (t);
|
||
else if (! DECL_LANG_SPECIFIC (t))
|
||
pp_identifier (cxx_pp, "<internal>");
|
||
else
|
||
dump_function_decl (t, flags);
|
||
break;
|
||
|
||
case TEMPLATE_DECL:
|
||
dump_template_decl (t, flags);
|
||
break;
|
||
|
||
case TEMPLATE_ID_EXPR:
|
||
{
|
||
tree name = TREE_OPERAND (t, 0);
|
||
|
||
if (is_overloaded_fn (name))
|
||
name = DECL_NAME (get_first_fn (name));
|
||
dump_decl (name, flags);
|
||
pp_template_argument_list_start (cxx_pp);
|
||
if (TREE_OPERAND (t, 1))
|
||
dump_template_argument_list (TREE_OPERAND (t, 1), flags);
|
||
pp_template_argument_list_end (cxx_pp);
|
||
}
|
||
break;
|
||
|
||
case LABEL_DECL:
|
||
pp_tree_identifier (cxx_pp, DECL_NAME (t));
|
||
break;
|
||
|
||
case CONST_DECL:
|
||
if ((TREE_TYPE (t) != NULL_TREE && NEXT_CODE (t) == ENUMERAL_TYPE)
|
||
|| (DECL_INITIAL (t) &&
|
||
TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX))
|
||
dump_simple_decl (t, TREE_TYPE (t), flags);
|
||
else if (DECL_NAME (t))
|
||
dump_decl (DECL_NAME (t), flags);
|
||
else if (DECL_INITIAL (t))
|
||
dump_expr (DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS);
|
||
else
|
||
pp_identifier (cxx_pp, "<enumerator>");
|
||
break;
|
||
|
||
case USING_DECL:
|
||
pp_string (cxx_pp, "using ");
|
||
dump_type (DECL_INITIAL (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
dump_decl (DECL_NAME (t), flags);
|
||
break;
|
||
|
||
case BASELINK:
|
||
dump_decl (BASELINK_FUNCTIONS (t), flags);
|
||
break;
|
||
|
||
case NON_DEPENDENT_EXPR:
|
||
dump_expr (t, flags);
|
||
break;
|
||
|
||
case TEMPLATE_TYPE_PARM:
|
||
if (flags & TFF_DECL_SPECIFIERS)
|
||
pp_cxx_declaration (cxx_pp, t);
|
||
else
|
||
pp_type_id (cxx_pp, t);
|
||
break;
|
||
|
||
default:
|
||
pp_unsupported_tree (cxx_pp, t);
|
||
/* Fallthrough to error. */
|
||
|
||
case ERROR_MARK:
|
||
pp_identifier (cxx_pp, "<declaration error>");
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Dump a template declaration T under control of FLAGS. This means the
|
||
'template <...> leaders plus the 'class X' or 'void fn(...)' part. */
|
||
|
||
static void
|
||
dump_template_decl (tree t, int flags)
|
||
{
|
||
tree orig_parms = DECL_TEMPLATE_PARMS (t);
|
||
tree parms;
|
||
int i;
|
||
|
||
if (flags & TFF_TEMPLATE_HEADER)
|
||
{
|
||
for (parms = orig_parms = nreverse (orig_parms);
|
||
parms;
|
||
parms = TREE_CHAIN (parms))
|
||
{
|
||
tree inner_parms = INNERMOST_TEMPLATE_PARMS (parms);
|
||
int len = TREE_VEC_LENGTH (inner_parms);
|
||
|
||
pp_string (cxx_pp, "template<");
|
||
|
||
/* If we've shown the template prefix, we'd better show the
|
||
parameters' and decl's type too. */
|
||
flags |= TFF_DECL_SPECIFIERS;
|
||
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
if (i)
|
||
pp_separate_with_comma (cxx_pp);
|
||
dump_template_parameter (TREE_VEC_ELT (inner_parms, i), flags);
|
||
}
|
||
pp_template_argument_list_end (cxx_pp);
|
||
pp_space (cxx_pp);
|
||
}
|
||
nreverse(orig_parms);
|
||
|
||
if (DECL_TEMPLATE_TEMPLATE_PARM_P (t))
|
||
/* Say `template<arg> class TT' not just `template<arg> TT'. */
|
||
pp_string (cxx_pp, "class ");
|
||
}
|
||
|
||
if (TREE_CODE (DECL_TEMPLATE_RESULT (t)) == TYPE_DECL)
|
||
dump_type (TREE_TYPE (t),
|
||
((flags & ~TFF_CLASS_KEY_OR_ENUM) | TFF_TEMPLATE_NAME
|
||
| (flags & TFF_DECL_SPECIFIERS ? TFF_CLASS_KEY_OR_ENUM : 0)));
|
||
else if (TREE_CODE (DECL_TEMPLATE_RESULT (t)) == VAR_DECL)
|
||
dump_decl (DECL_TEMPLATE_RESULT (t), flags | TFF_TEMPLATE_NAME);
|
||
else if (TREE_TYPE (t) == NULL_TREE)
|
||
abort ();
|
||
else
|
||
switch (NEXT_CODE (t))
|
||
{
|
||
case METHOD_TYPE:
|
||
case FUNCTION_TYPE:
|
||
dump_function_decl (t, flags | TFF_TEMPLATE_NAME);
|
||
break;
|
||
default:
|
||
/* This case can occur with some invalid code. */
|
||
dump_type (TREE_TYPE (t),
|
||
(flags & ~TFF_CLASS_KEY_OR_ENUM) | TFF_TEMPLATE_NAME
|
||
| (flags & TFF_DECL_SPECIFIERS ? TFF_CLASS_KEY_OR_ENUM : 0));
|
||
}
|
||
}
|
||
|
||
/* Pretty print a function decl. There are several ways we want to print a
|
||
function declaration. The TFF_ bits in FLAGS tells us how to behave.
|
||
As error can only apply the '#' flag once to give 0 and 1 for V, there
|
||
is %D which doesn't print the throw specs, and %F which does. */
|
||
|
||
static void
|
||
dump_function_decl (tree t, int flags)
|
||
{
|
||
tree fntype;
|
||
tree parmtypes;
|
||
tree cname = NULL_TREE;
|
||
tree template_args = NULL_TREE;
|
||
tree template_parms = NULL_TREE;
|
||
int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS;
|
||
|
||
if (TREE_CODE (t) == TEMPLATE_DECL)
|
||
t = DECL_TEMPLATE_RESULT (t);
|
||
|
||
/* Pretty print template instantiations only. */
|
||
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t))
|
||
{
|
||
tree tmpl;
|
||
|
||
template_args = DECL_TI_ARGS (t);
|
||
tmpl = most_general_template (t);
|
||
if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL)
|
||
{
|
||
template_parms = DECL_TEMPLATE_PARMS (tmpl);
|
||
t = tmpl;
|
||
}
|
||
}
|
||
|
||
fntype = TREE_TYPE (t);
|
||
parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
|
||
|
||
if (DECL_CLASS_SCOPE_P (t))
|
||
cname = DECL_CONTEXT (t);
|
||
/* This is for partially instantiated template methods. */
|
||
else if (TREE_CODE (fntype) == METHOD_TYPE)
|
||
cname = TREE_TYPE (TREE_VALUE (parmtypes));
|
||
|
||
if (!(flags & TFF_DECL_SPECIFIERS))
|
||
/* OK */;
|
||
else if (DECL_STATIC_FUNCTION_P (t))
|
||
pp_identifier (cxx_pp, "static ");
|
||
else if (DECL_VIRTUAL_P (t))
|
||
pp_identifier (cxx_pp, "virtual ");
|
||
|
||
/* Print the return type? */
|
||
if (show_return)
|
||
show_return = !DECL_CONV_FN_P (t) && !DECL_CONSTRUCTOR_P (t)
|
||
&& !DECL_DESTRUCTOR_P (t);
|
||
if (show_return)
|
||
{
|
||
dump_type_prefix (TREE_TYPE (fntype), flags);
|
||
pp_space (cxx_pp);
|
||
}
|
||
|
||
/* Print the function name. */
|
||
if (cname)
|
||
{
|
||
dump_type (cname, flags);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
else
|
||
dump_scope (CP_DECL_CONTEXT (t), flags);
|
||
|
||
dump_function_name (t, flags);
|
||
|
||
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
|
||
{
|
||
dump_parameters (parmtypes, flags);
|
||
|
||
if (TREE_CODE (fntype) == METHOD_TYPE)
|
||
dump_qualifiers (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))),
|
||
before);
|
||
|
||
if (flags & TFF_EXCEPTION_SPECIFICATION)
|
||
dump_exception_spec (TYPE_RAISES_EXCEPTIONS (fntype), flags);
|
||
|
||
if (show_return)
|
||
dump_type_suffix (TREE_TYPE (fntype), flags);
|
||
}
|
||
|
||
/* If T is a template instantiation, dump the parameter binding. */
|
||
if (template_parms != NULL_TREE && template_args != NULL_TREE)
|
||
{
|
||
pp_string (cxx_pp, " [with ");
|
||
dump_template_bindings (template_parms, template_args);
|
||
pp_right_bracket (cxx_pp);
|
||
}
|
||
}
|
||
|
||
/* Print a parameter list. If this is for a member function, the
|
||
member object ptr (and any other hidden args) should have
|
||
already been removed. */
|
||
|
||
static void
|
||
dump_parameters (tree parmtypes, int flags)
|
||
{
|
||
int first;
|
||
|
||
pp_left_paren (cxx_pp);
|
||
|
||
for (first = 1; parmtypes != void_list_node;
|
||
parmtypes = TREE_CHAIN (parmtypes))
|
||
{
|
||
if (!first)
|
||
pp_separate_with_comma (cxx_pp);
|
||
first = 0;
|
||
if (!parmtypes)
|
||
{
|
||
pp_identifier (cxx_pp, "...");
|
||
break;
|
||
}
|
||
dump_type (TREE_VALUE (parmtypes), flags);
|
||
|
||
if ((flags & TFF_FUNCTION_DEFAULT_ARGUMENTS) && TREE_PURPOSE (parmtypes))
|
||
{
|
||
pp_string (cxx_pp, " = ");
|
||
dump_expr (TREE_PURPOSE (parmtypes), flags | TFF_EXPR_IN_PARENS);
|
||
}
|
||
}
|
||
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
|
||
/* Print an exception specification. T is the exception specification. */
|
||
|
||
static void
|
||
dump_exception_spec (tree t, int flags)
|
||
{
|
||
if (t)
|
||
{
|
||
pp_string (cxx_pp, " throw (");
|
||
if (TREE_VALUE (t) != NULL_TREE)
|
||
while (1)
|
||
{
|
||
dump_type (TREE_VALUE (t), flags);
|
||
t = TREE_CHAIN (t);
|
||
if (!t)
|
||
break;
|
||
pp_separate_with_comma (cxx_pp);
|
||
}
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
}
|
||
|
||
/* Handle the function name for a FUNCTION_DECL node, grokking operators
|
||
and destructors properly. */
|
||
|
||
static void
|
||
dump_function_name (tree t, int flags)
|
||
{
|
||
tree name = DECL_NAME (t);
|
||
|
||
if (TREE_CODE (t) == TEMPLATE_DECL)
|
||
t = DECL_TEMPLATE_RESULT (t);
|
||
|
||
/* Don't let the user see __comp_ctor et al. */
|
||
if (DECL_CONSTRUCTOR_P (t)
|
||
|| DECL_DESTRUCTOR_P (t))
|
||
name = constructor_name (DECL_CONTEXT (t));
|
||
|
||
if (DECL_DESTRUCTOR_P (t))
|
||
{
|
||
pp_complement (cxx_pp);
|
||
dump_decl (name, TFF_PLAIN_IDENTIFIER);
|
||
}
|
||
else if (DECL_CONV_FN_P (t))
|
||
{
|
||
/* 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. */
|
||
pp_string (cxx_pp, "operator ");
|
||
dump_type (TREE_TYPE (TREE_TYPE (t)), flags);
|
||
}
|
||
else if (IDENTIFIER_OPNAME_P (name))
|
||
pp_tree_identifier (cxx_pp, name);
|
||
else
|
||
dump_decl (name, flags);
|
||
|
||
if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t)
|
||
&& !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
|
||
&& (DECL_TEMPLATE_SPECIALIZATION (t)
|
||
|| TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
|
||
|| DECL_TEMPLATE_SPECIALIZATION (DECL_TI_TEMPLATE (t))
|
||
|| PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
|
||
dump_template_parms (DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
|
||
}
|
||
|
||
/* Dump the template parameters from the template info INFO under control of
|
||
FLAGS. PRIMARY indicates whether this is a primary template decl, or
|
||
specialization (partial or complete). For partial specializations we show
|
||
the specialized parameter values. For a primary template we show no
|
||
decoration. */
|
||
|
||
static void
|
||
dump_template_parms (tree info, int primary, int flags)
|
||
{
|
||
tree args = info ? TI_ARGS (info) : NULL_TREE;
|
||
|
||
if (primary && flags & TFF_TEMPLATE_NAME)
|
||
return;
|
||
flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
|
||
pp_template_argument_list_start (cxx_pp);
|
||
|
||
/* Be careful only to print things when we have them, so as not
|
||
to crash producing error messages. */
|
||
if (args && !primary)
|
||
{
|
||
int len, ix;
|
||
|
||
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
|
||
args = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1);
|
||
|
||
len = TREE_VEC_LENGTH (args);
|
||
|
||
for (ix = 0; ix != len; ix++)
|
||
{
|
||
tree arg = TREE_VEC_ELT (args, ix);
|
||
|
||
if (ix)
|
||
pp_separate_with_comma (cxx_pp);
|
||
|
||
if (!arg)
|
||
pp_identifier (cxx_pp, "<template parameter error>");
|
||
else
|
||
dump_template_argument (arg, flags);
|
||
}
|
||
}
|
||
else if (primary)
|
||
{
|
||
tree tpl = TI_TEMPLATE (info);
|
||
tree parms = DECL_TEMPLATE_PARMS (tpl);
|
||
int len, ix;
|
||
|
||
parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
|
||
len = parms ? TREE_VEC_LENGTH (parms) : 0;
|
||
|
||
for (ix = 0; ix != len; ix++)
|
||
{
|
||
tree parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
|
||
|
||
if (ix)
|
||
pp_separate_with_comma (cxx_pp);
|
||
|
||
dump_decl (parm, flags & ~TFF_DECL_SPECIFIERS);
|
||
}
|
||
}
|
||
pp_template_argument_list_end (cxx_pp);
|
||
}
|
||
|
||
/* Print out a list of initializers (subr of dump_expr). */
|
||
|
||
static void
|
||
dump_expr_list (tree l, int flags)
|
||
{
|
||
while (l)
|
||
{
|
||
dump_expr (TREE_VALUE (l), flags | TFF_EXPR_IN_PARENS);
|
||
l = TREE_CHAIN (l);
|
||
if (l)
|
||
pp_separate_with_comma (cxx_pp);
|
||
}
|
||
}
|
||
|
||
/* Print out an expression E under control of FLAGS. */
|
||
|
||
static void
|
||
dump_expr (tree t, int flags)
|
||
{
|
||
if (t == 0)
|
||
return;
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case VAR_DECL:
|
||
case PARM_DECL:
|
||
case FIELD_DECL:
|
||
case CONST_DECL:
|
||
case FUNCTION_DECL:
|
||
case TEMPLATE_DECL:
|
||
case NAMESPACE_DECL:
|
||
case OVERLOAD:
|
||
case IDENTIFIER_NODE:
|
||
dump_decl (t, (flags & ~TFF_DECL_SPECIFIERS) | TFF_NO_FUNCTION_ARGUMENTS);
|
||
break;
|
||
|
||
case INTEGER_CST:
|
||
case STRING_CST:
|
||
case REAL_CST:
|
||
pp_c_constant (pp_c_base (cxx_pp), t);
|
||
break;
|
||
|
||
case THROW_EXPR:
|
||
pp_identifier (cxx_pp, "throw");
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
break;
|
||
|
||
case PTRMEM_CST:
|
||
pp_ampersand (cxx_pp);
|
||
dump_type (PTRMEM_CST_CLASS (t), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
pp_tree_identifier (cxx_pp, DECL_NAME (PTRMEM_CST_MEMBER (t)));
|
||
break;
|
||
|
||
case COMPOUND_EXPR:
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_separate_with_comma (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case COND_EXPR:
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_string (cxx_pp, " ? ");
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
pp_string (cxx_pp, " : ");
|
||
dump_expr (TREE_OPERAND (t, 2), flags | TFF_EXPR_IN_PARENS);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case SAVE_EXPR:
|
||
if (TREE_HAS_CONSTRUCTOR (t))
|
||
{
|
||
pp_string (cxx_pp, "new ");
|
||
dump_type (TREE_TYPE (TREE_TYPE (t)), flags);
|
||
}
|
||
else
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
break;
|
||
|
||
case AGGR_INIT_EXPR:
|
||
{
|
||
tree fn = NULL_TREE;
|
||
|
||
if (TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR)
|
||
fn = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
|
||
|
||
if (fn && TREE_CODE (fn) == FUNCTION_DECL)
|
||
{
|
||
if (DECL_CONSTRUCTOR_P (fn))
|
||
pp_tree_identifier (cxx_pp, TYPE_IDENTIFIER (TREE_TYPE (t)));
|
||
else
|
||
dump_decl (fn, 0);
|
||
}
|
||
else
|
||
dump_expr (TREE_OPERAND (t, 0), 0);
|
||
}
|
||
pp_left_paren (cxx_pp);
|
||
if (TREE_OPERAND (t, 1))
|
||
dump_expr_list (TREE_CHAIN (TREE_OPERAND (t, 1)), flags);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case CALL_EXPR:
|
||
{
|
||
tree fn = TREE_OPERAND (t, 0);
|
||
tree args = TREE_OPERAND (t, 1);
|
||
|
||
if (TREE_CODE (fn) == ADDR_EXPR)
|
||
fn = TREE_OPERAND (fn, 0);
|
||
|
||
if (TREE_TYPE (fn) != NULL_TREE && NEXT_CODE (fn) == METHOD_TYPE)
|
||
{
|
||
tree ob = TREE_VALUE (args);
|
||
if (TREE_CODE (ob) == ADDR_EXPR)
|
||
{
|
||
dump_expr (TREE_OPERAND (ob, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_dot (cxx_pp);
|
||
}
|
||
else if (TREE_CODE (ob) != PARM_DECL
|
||
|| strcmp (IDENTIFIER_POINTER (DECL_NAME (ob)), "this"))
|
||
{
|
||
dump_expr (ob, flags | TFF_EXPR_IN_PARENS);
|
||
pp_arrow (cxx_pp);
|
||
}
|
||
args = TREE_CHAIN (args);
|
||
}
|
||
dump_expr (fn, flags | TFF_EXPR_IN_PARENS);
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr_list (args, flags);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
break;
|
||
|
||
case NEW_EXPR:
|
||
{
|
||
tree type = TREE_OPERAND (t, 1);
|
||
tree init = TREE_OPERAND (t, 2);
|
||
if (NEW_EXPR_USE_GLOBAL (t))
|
||
pp_colon_colon (cxx_pp);
|
||
pp_string (cxx_pp, "new ");
|
||
if (TREE_OPERAND (t, 0))
|
||
{
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr_list (TREE_OPERAND (t, 0), flags);
|
||
pp_string (cxx_pp, ") ");
|
||
}
|
||
if (TREE_CODE (type) == ARRAY_REF)
|
||
type = build_cplus_array_type
|
||
(TREE_OPERAND (type, 0),
|
||
build_index_type (fold (build (MINUS_EXPR, integer_type_node,
|
||
TREE_OPERAND (type, 1),
|
||
integer_one_node))));
|
||
dump_type (type, flags);
|
||
if (init)
|
||
{
|
||
pp_left_paren (cxx_pp);
|
||
if (TREE_CODE (init) == TREE_LIST)
|
||
dump_expr_list (init, flags);
|
||
else if (init == void_zero_node)
|
||
/* This representation indicates an empty initializer,
|
||
e.g.: "new int()". */
|
||
;
|
||
else
|
||
dump_expr (init, flags);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case TARGET_EXPR:
|
||
/* Note that this only works for G++ target exprs. If somebody
|
||
builds a general TARGET_EXPR, there's no way to represent that
|
||
it initializes anything other that the parameter slot for the
|
||
default argument. Note we may have cleared out the first
|
||
operand in expand_expr, so don't go killing ourselves. */
|
||
if (TREE_OPERAND (t, 1))
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
break;
|
||
|
||
case INIT_EXPR:
|
||
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 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:
|
||
case EXACT_DIV_EXPR:
|
||
dump_binary_op (operator_name_info[(int) TREE_CODE (t)].name, t, flags);
|
||
break;
|
||
|
||
case CEIL_DIV_EXPR:
|
||
case FLOOR_DIV_EXPR:
|
||
case ROUND_DIV_EXPR:
|
||
dump_binary_op ("/", t, flags);
|
||
break;
|
||
|
||
case CEIL_MOD_EXPR:
|
||
case FLOOR_MOD_EXPR:
|
||
case ROUND_MOD_EXPR:
|
||
dump_binary_op ("%", t, flags);
|
||
break;
|
||
|
||
case COMPONENT_REF:
|
||
{
|
||
tree ob = TREE_OPERAND (t, 0);
|
||
if (TREE_CODE (ob) == INDIRECT_REF)
|
||
{
|
||
ob = TREE_OPERAND (ob, 0);
|
||
if (TREE_CODE (ob) != PARM_DECL
|
||
|| strcmp (IDENTIFIER_POINTER (DECL_NAME (ob)), "this"))
|
||
{
|
||
dump_expr (ob, flags | TFF_EXPR_IN_PARENS);
|
||
pp_arrow (cxx_pp);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dump_expr (ob, flags | TFF_EXPR_IN_PARENS);
|
||
pp_dot (cxx_pp);
|
||
}
|
||
dump_expr (TREE_OPERAND (t, 1), flags & ~TFF_EXPR_IN_PARENS);
|
||
}
|
||
break;
|
||
|
||
case ARRAY_REF:
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_left_bracket (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
pp_right_bracket (cxx_pp);
|
||
break;
|
||
|
||
case CONVERT_EXPR:
|
||
if (TREE_TYPE (t) && VOID_TYPE_P (TREE_TYPE (t)))
|
||
{
|
||
pp_left_paren (cxx_pp);
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_right_paren (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
}
|
||
else
|
||
dump_unary_op ("+", t, flags);
|
||
break;
|
||
|
||
case ADDR_EXPR:
|
||
if (TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
|
||
|| TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST
|
||
/* An ADDR_EXPR can have reference type. In that case, we
|
||
shouldn't print the `&' doing so indicates to the user
|
||
that the expression has pointer type. */
|
||
|| (TREE_TYPE (t)
|
||
&& TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE))
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
else
|
||
dump_unary_op ("&", t, flags);
|
||
break;
|
||
|
||
case INDIRECT_REF:
|
||
if (TREE_HAS_CONSTRUCTOR (t))
|
||
{
|
||
t = TREE_OPERAND (t, 0);
|
||
my_friendly_assert (TREE_CODE (t) == CALL_EXPR, 237);
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr_list (TREE_CHAIN (TREE_OPERAND (t, 1)), flags);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
else
|
||
{
|
||
if (TREE_OPERAND (t,0) != NULL_TREE
|
||
&& TREE_TYPE (TREE_OPERAND (t, 0))
|
||
&& NEXT_CODE (TREE_OPERAND (t, 0)) == REFERENCE_TYPE)
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
else
|
||
dump_unary_op ("*", t, flags);
|
||
}
|
||
break;
|
||
|
||
case NEGATE_EXPR:
|
||
case BIT_NOT_EXPR:
|
||
case TRUTH_NOT_EXPR:
|
||
case PREDECREMENT_EXPR:
|
||
case PREINCREMENT_EXPR:
|
||
dump_unary_op (operator_name_info [(int)TREE_CODE (t)].name, t, flags);
|
||
break;
|
||
|
||
case POSTDECREMENT_EXPR:
|
||
case POSTINCREMENT_EXPR:
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_identifier (cxx_pp, operator_name_info[(int)TREE_CODE (t)].name);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case NON_LVALUE_EXPR:
|
||
/* FIXME: This is a KLUDGE workaround for a parsing problem. There
|
||
should be another level of INDIRECT_REF so that I don't have to do
|
||
this. */
|
||
if (TREE_TYPE (t) != NULL_TREE && NEXT_CODE (t) == POINTER_TYPE)
|
||
{
|
||
tree next = TREE_TYPE (TREE_TYPE (t));
|
||
|
||
while (TREE_CODE (next) == POINTER_TYPE)
|
||
next = TREE_TYPE (next);
|
||
|
||
if (TREE_CODE (next) == FUNCTION_TYPE)
|
||
{
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_left_paren (cxx_pp);
|
||
pp_star (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags & ~TFF_EXPR_IN_PARENS);
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
}
|
||
/* Else fall through. */
|
||
}
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
break;
|
||
|
||
case NOP_EXPR:
|
||
{
|
||
tree op = TREE_OPERAND (t, 0);
|
||
|
||
if (!same_type_p (TREE_TYPE (op), TREE_TYPE (t)))
|
||
{
|
||
/* It is a cast, but we cannot tell whether it is a
|
||
reinterpret or static cast. Use the C style notation. */
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_left_paren (cxx_pp);
|
||
pp_left_paren (cxx_pp);
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_right_paren (cxx_pp);
|
||
dump_expr (op, flags | TFF_EXPR_IN_PARENS);
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
else
|
||
dump_expr (op, flags);
|
||
break;
|
||
}
|
||
|
||
case EXPR_WITH_FILE_LOCATION:
|
||
dump_expr (EXPR_WFL_NODE (t), flags);
|
||
break;
|
||
|
||
case CONSTRUCTOR:
|
||
if (TREE_TYPE (t) && TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))
|
||
{
|
||
tree idx = build_ptrmemfunc_access_expr (t, pfn_identifier);
|
||
|
||
if (integer_zerop (idx))
|
||
{
|
||
/* A NULL pointer-to-member constant. */
|
||
pp_left_paren (cxx_pp);
|
||
pp_left_paren (cxx_pp);
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_right_paren (cxx_pp);
|
||
pp_string (cxx_pp, ")0)");
|
||
break;
|
||
}
|
||
else if (host_integerp (idx, 0))
|
||
{
|
||
tree virtuals;
|
||
unsigned HOST_WIDE_INT n;
|
||
|
||
t = TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (t)));
|
||
t = TYPE_METHOD_BASETYPE (t);
|
||
virtuals = TYPE_BINFO_VIRTUALS (TYPE_MAIN_VARIANT (t));
|
||
|
||
n = tree_low_cst (idx, 0);
|
||
|
||
/* Map vtable index back one, to allow for the null pointer to
|
||
member. */
|
||
--n;
|
||
|
||
while (n > 0 && virtuals)
|
||
{
|
||
--n;
|
||
virtuals = TREE_CHAIN (virtuals);
|
||
}
|
||
if (virtuals)
|
||
{
|
||
dump_expr (BV_FN (virtuals),
|
||
flags | TFF_EXPR_IN_PARENS);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (TREE_TYPE (t) && !CONSTRUCTOR_ELTS (t))
|
||
{
|
||
dump_type (TREE_TYPE (t), 0);
|
||
pp_left_paren (cxx_pp);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
else
|
||
{
|
||
pp_left_brace (cxx_pp);
|
||
dump_expr_list (CONSTRUCTOR_ELTS (t), flags);
|
||
pp_right_brace (cxx_pp);
|
||
}
|
||
|
||
break;
|
||
|
||
case OFFSET_REF:
|
||
{
|
||
tree ob = TREE_OPERAND (t, 0);
|
||
if (is_dummy_object (ob))
|
||
{
|
||
t = TREE_OPERAND (t, 1);
|
||
if (TREE_CODE (t) == FUNCTION_DECL)
|
||
/* A::f */
|
||
dump_expr (t, flags | TFF_EXPR_IN_PARENS);
|
||
else if (BASELINK_P (t))
|
||
dump_expr (OVL_CURRENT (BASELINK_FUNCTIONS (t)),
|
||
flags | TFF_EXPR_IN_PARENS);
|
||
else
|
||
dump_decl (t, flags);
|
||
}
|
||
else
|
||
{
|
||
if (TREE_CODE (ob) == INDIRECT_REF)
|
||
{
|
||
dump_expr (TREE_OPERAND (ob, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_string (cxx_pp, "->*");
|
||
}
|
||
else
|
||
{
|
||
dump_expr (ob, flags | TFF_EXPR_IN_PARENS);
|
||
pp_string (cxx_pp, ".*");
|
||
}
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case TEMPLATE_PARM_INDEX:
|
||
dump_decl (TEMPLATE_PARM_DECL (t), flags & ~TFF_DECL_SPECIFIERS);
|
||
break;
|
||
|
||
case SCOPE_REF:
|
||
dump_type (TREE_OPERAND (t, 0), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
break;
|
||
|
||
case CAST_EXPR:
|
||
if (TREE_OPERAND (t, 0) == NULL_TREE
|
||
|| TREE_CHAIN (TREE_OPERAND (t, 0)))
|
||
{
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr_list (TREE_OPERAND (t, 0), flags);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
else
|
||
{
|
||
pp_left_paren (cxx_pp);
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_string (cxx_pp, ")(");
|
||
dump_expr_list (TREE_OPERAND (t, 0), flags);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
break;
|
||
|
||
case STATIC_CAST_EXPR:
|
||
pp_string (cxx_pp, "static_cast<");
|
||
goto cast;
|
||
case REINTERPRET_CAST_EXPR:
|
||
pp_string (cxx_pp, "reinterpret_cast<");
|
||
goto cast;
|
||
case CONST_CAST_EXPR:
|
||
pp_string (cxx_pp, "const_cast<");
|
||
goto cast;
|
||
case DYNAMIC_CAST_EXPR:
|
||
pp_string (cxx_pp, "dynamic_cast<");
|
||
cast:
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_string (cxx_pp, ">(");
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case ARROW_EXPR:
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
pp_arrow (cxx_pp);
|
||
break;
|
||
|
||
case SIZEOF_EXPR:
|
||
case ALIGNOF_EXPR:
|
||
if (TREE_CODE (t) == SIZEOF_EXPR)
|
||
pp_string (cxx_pp, "sizeof (");
|
||
else
|
||
{
|
||
my_friendly_assert (TREE_CODE (t) == ALIGNOF_EXPR, 0);
|
||
pp_string (cxx_pp, "__alignof__ (");
|
||
}
|
||
if (TYPE_P (TREE_OPERAND (t, 0)))
|
||
dump_type (TREE_OPERAND (t, 0), flags);
|
||
else
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case REALPART_EXPR:
|
||
case IMAGPART_EXPR:
|
||
pp_identifier (cxx_pp, operator_name_info[TREE_CODE (t)].name);
|
||
pp_space (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
break;
|
||
|
||
case DEFAULT_ARG:
|
||
pp_identifier (cxx_pp, "<unparsed>");
|
||
break;
|
||
|
||
case TRY_CATCH_EXPR:
|
||
case WITH_CLEANUP_EXPR:
|
||
case CLEANUP_POINT_EXPR:
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
break;
|
||
|
||
case PSEUDO_DTOR_EXPR:
|
||
dump_expr (TREE_OPERAND (t, 2), flags);
|
||
pp_dot (cxx_pp);
|
||
dump_type (TREE_OPERAND (t, 0), flags);
|
||
pp_colon_colon (cxx_pp);
|
||
pp_complement (cxx_pp);
|
||
dump_type (TREE_OPERAND (t, 1), flags);
|
||
break;
|
||
|
||
case TEMPLATE_ID_EXPR:
|
||
dump_decl (t, flags);
|
||
break;
|
||
|
||
case STMT_EXPR:
|
||
/* We don't yet have a way of dumping statements in a
|
||
human-readable format. */
|
||
pp_string (cxx_pp, "({...})");
|
||
break;
|
||
|
||
case BIND_EXPR:
|
||
pp_left_brace (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 1), flags & ~TFF_EXPR_IN_PARENS);
|
||
pp_right_brace (cxx_pp);
|
||
break;
|
||
|
||
case LOOP_EXPR:
|
||
pp_string (cxx_pp, "while (1) { ");
|
||
dump_expr (TREE_OPERAND (t, 0), flags & ~TFF_EXPR_IN_PARENS);
|
||
pp_right_brace (cxx_pp);
|
||
break;
|
||
|
||
case EXIT_EXPR:
|
||
pp_string (cxx_pp, "if (");
|
||
dump_expr (TREE_OPERAND (t, 0), flags & ~TFF_EXPR_IN_PARENS);
|
||
pp_string (cxx_pp, ") break; ");
|
||
break;
|
||
|
||
case BASELINK:
|
||
dump_expr (get_first_fn (t), flags & ~TFF_EXPR_IN_PARENS);
|
||
break;
|
||
|
||
case EMPTY_CLASS_EXPR:
|
||
dump_type (TREE_TYPE (t), flags);
|
||
pp_left_paren (cxx_pp);
|
||
pp_right_paren (cxx_pp);
|
||
break;
|
||
|
||
case NON_DEPENDENT_EXPR:
|
||
dump_expr (TREE_OPERAND (t, 0), flags);
|
||
break;
|
||
|
||
/* 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:
|
||
pp_unsupported_tree (cxx_pp, t);
|
||
/* fall through to ERROR_MARK... */
|
||
case ERROR_MARK:
|
||
pp_identifier (cxx_pp, "<expression error>");
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
dump_binary_op (const char *opstring, tree t, int flags)
|
||
{
|
||
pp_left_paren (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
|
||
pp_space (cxx_pp);
|
||
if (opstring)
|
||
pp_identifier (cxx_pp, opstring);
|
||
else
|
||
pp_identifier (cxx_pp, "<unknown operator>");
|
||
pp_space (cxx_pp);
|
||
dump_expr (TREE_OPERAND (t, 1), flags | TFF_EXPR_IN_PARENS);
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
|
||
static void
|
||
dump_unary_op (const char *opstring, tree t, int flags)
|
||
{
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_left_paren (cxx_pp);
|
||
pp_identifier (cxx_pp, opstring);
|
||
dump_expr (TREE_OPERAND (t, 0), flags & ~TFF_EXPR_IN_PARENS);
|
||
if (flags & TFF_EXPR_IN_PARENS)
|
||
pp_right_paren (cxx_pp);
|
||
}
|
||
|
||
/* Exported interface to stringifying types, exprs and decls under TFF_*
|
||
control. */
|
||
|
||
const char *
|
||
type_as_string (tree typ, int flags)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_type (typ, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
const char *
|
||
expr_as_string (tree decl, int flags)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_expr (decl, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
const char *
|
||
decl_as_string (tree decl, int flags)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_decl (decl, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
const char *
|
||
context_as_string (tree context, int flags)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_scope (context, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
/* Generate the three forms of printable names for cxx_printable_name. */
|
||
|
||
const char *
|
||
lang_decl_name (tree decl, int v)
|
||
{
|
||
if (v >= 2)
|
||
return decl_as_string (decl, TFF_DECL_SPECIFIERS);
|
||
|
||
pp_clear_output_area (cxx_pp);
|
||
if (v == 1 && DECL_CLASS_SCOPE_P (decl))
|
||
{
|
||
dump_type (CP_DECL_CONTEXT (decl), TFF_PLAIN_IDENTIFIER);
|
||
pp_colon_colon (cxx_pp);
|
||
}
|
||
|
||
if (TREE_CODE (decl) == FUNCTION_DECL)
|
||
dump_function_name (decl, TFF_PLAIN_IDENTIFIER);
|
||
else
|
||
dump_decl (DECL_NAME (decl), TFF_PLAIN_IDENTIFIER);
|
||
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static location_t
|
||
location_of (tree t)
|
||
{
|
||
if (TREE_CODE (t) == PARM_DECL && DECL_CONTEXT (t))
|
||
t = DECL_CONTEXT (t);
|
||
else if (TYPE_P (t))
|
||
t = TYPE_MAIN_DECL (t);
|
||
else if (TREE_CODE (t) == OVERLOAD)
|
||
t = OVL_FUNCTION (t);
|
||
|
||
return DECL_SOURCE_LOCATION (t);
|
||
}
|
||
|
||
/* Now the interfaces from error et al to dump_type et al. Each takes an
|
||
on/off VERBOSE flag and supply the appropriate TFF_ flags to a dump_
|
||
function. */
|
||
|
||
static const char *
|
||
decl_to_string (tree decl, int verbose)
|
||
{
|
||
int flags = 0;
|
||
|
||
if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == RECORD_TYPE
|
||
|| TREE_CODE (decl) == UNION_TYPE || TREE_CODE (decl) == ENUMERAL_TYPE)
|
||
flags = TFF_CLASS_KEY_OR_ENUM;
|
||
if (verbose)
|
||
flags |= TFF_DECL_SPECIFIERS;
|
||
else if (TREE_CODE (decl) == FUNCTION_DECL)
|
||
flags |= TFF_DECL_SPECIFIERS | TFF_RETURN_TYPE;
|
||
flags |= TFF_TEMPLATE_HEADER;
|
||
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_decl (decl, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static const char *
|
||
expr_to_string (tree decl)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_expr (decl, 0);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static const char *
|
||
fndecl_to_string (tree fndecl, int verbose)
|
||
{
|
||
int flags;
|
||
|
||
flags = TFF_EXCEPTION_SPECIFICATION | TFF_DECL_SPECIFIERS;
|
||
if (verbose)
|
||
flags |= TFF_FUNCTION_DEFAULT_ARGUMENTS;
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_decl (fndecl, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
|
||
static const char *
|
||
code_to_string (enum tree_code c)
|
||
{
|
||
return tree_code_name [c];
|
||
}
|
||
|
||
const char *
|
||
language_to_string (enum languages c)
|
||
{
|
||
switch (c)
|
||
{
|
||
case lang_c:
|
||
return "C";
|
||
|
||
case lang_cplusplus:
|
||
return "C++";
|
||
|
||
case lang_java:
|
||
return "Java";
|
||
|
||
default:
|
||
abort ();
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* Return the proper printed version of a parameter to a C++ function. */
|
||
|
||
static const char *
|
||
parm_to_string (int p)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
if (p < 0)
|
||
pp_string (cxx_pp, "'this'");
|
||
else
|
||
pp_decimal_int (cxx_pp, p + 1);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static const char *
|
||
op_to_string (enum tree_code p)
|
||
{
|
||
tree id = operator_name_info[(int) p].identifier;
|
||
return id ? IDENTIFIER_POINTER (id) : "<unknown>";
|
||
}
|
||
|
||
static const char *
|
||
type_to_string (tree typ, int verbose)
|
||
{
|
||
int flags = 0;
|
||
if (verbose)
|
||
flags |= TFF_CLASS_KEY_OR_ENUM;
|
||
flags |= TFF_TEMPLATE_HEADER;
|
||
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_type (typ, flags);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static const char *
|
||
assop_to_string (enum tree_code p)
|
||
{
|
||
tree id = assignment_operator_name_info[(int) p].identifier;
|
||
return id ? IDENTIFIER_POINTER (id) : "{unknown}";
|
||
}
|
||
|
||
static const char *
|
||
args_to_string (tree p, int verbose)
|
||
{
|
||
int flags = 0;
|
||
if (verbose)
|
||
flags |= TFF_CLASS_KEY_OR_ENUM;
|
||
|
||
if (p == NULL_TREE)
|
||
return "";
|
||
|
||
if (TYPE_P (TREE_VALUE (p)))
|
||
return type_as_string (p, flags);
|
||
|
||
pp_clear_output_area (cxx_pp);
|
||
for (; p; p = TREE_CHAIN (p))
|
||
{
|
||
if (TREE_VALUE (p) == null_node)
|
||
pp_identifier (cxx_pp, "NULL");
|
||
else
|
||
dump_type (error_type (TREE_VALUE (p)), flags);
|
||
if (TREE_CHAIN (p))
|
||
pp_separate_with_comma (cxx_pp);
|
||
}
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
static const char *
|
||
cv_to_string (tree p, int v)
|
||
{
|
||
pp_clear_output_area (cxx_pp);
|
||
dump_qualifiers (p, v ? before : none);
|
||
return pp_formatted_text (cxx_pp);
|
||
}
|
||
|
||
/* Langhook for print_error_function. */
|
||
void
|
||
cxx_print_error_function (diagnostic_context *context, const char *file)
|
||
{
|
||
lhd_print_error_function (context, file);
|
||
pp_base_set_prefix (context->printer, file);
|
||
maybe_print_instantiation_context (context);
|
||
}
|
||
|
||
static void
|
||
cp_diagnostic_starter (diagnostic_context *context,
|
||
diagnostic_info *diagnostic)
|
||
{
|
||
diagnostic_report_current_module (context);
|
||
cp_print_error_function (context, diagnostic);
|
||
maybe_print_instantiation_context (context);
|
||
pp_base_set_prefix (context->printer, diagnostic_build_prefix (diagnostic));
|
||
}
|
||
|
||
static void
|
||
cp_diagnostic_finalizer (diagnostic_context *context,
|
||
diagnostic_info *diagnostic ATTRIBUTE_UNUSED)
|
||
{
|
||
pp_base_destroy_prefix (context->printer);
|
||
}
|
||
|
||
/* Print current function onto BUFFER, in the process of reporting
|
||
a diagnostic message. Called from cp_diagnostic_starter. */
|
||
static void
|
||
cp_print_error_function (diagnostic_context *context,
|
||
diagnostic_info *diagnostic)
|
||
{
|
||
if (diagnostic_last_function_changed (context))
|
||
{
|
||
const char *old_prefix = context->printer->prefix;
|
||
char *new_prefix = diagnostic->location.file
|
||
? file_name_as_prefix (diagnostic->location.file)
|
||
: NULL;
|
||
|
||
pp_base_set_prefix (context->printer, new_prefix);
|
||
|
||
if (current_function_decl == NULL)
|
||
pp_base_string (context->printer, "At global scope:");
|
||
else
|
||
pp_printf (context->printer, "In %s `%s':",
|
||
function_category (current_function_decl),
|
||
cxx_printable_name (current_function_decl, 2));
|
||
pp_base_newline (context->printer);
|
||
|
||
diagnostic_set_last_function (context);
|
||
pp_base_destroy_prefix (context->printer);
|
||
context->printer->prefix = old_prefix;
|
||
}
|
||
}
|
||
|
||
/* Returns a description of FUNCTION using standard terminology. */
|
||
static const char *
|
||
function_category (tree fn)
|
||
{
|
||
if (DECL_FUNCTION_MEMBER_P (fn))
|
||
{
|
||
if (DECL_STATIC_FUNCTION_P (fn))
|
||
return "static member function";
|
||
else if (DECL_COPY_CONSTRUCTOR_P (fn))
|
||
return "copy constructor";
|
||
else if (DECL_CONSTRUCTOR_P (fn))
|
||
return "constructor";
|
||
else if (DECL_DESTRUCTOR_P (fn))
|
||
return "destructor";
|
||
else
|
||
return "member function";
|
||
}
|
||
else
|
||
return "function";
|
||
}
|
||
|
||
/* Report the full context of a current template instantiation,
|
||
onto BUFFER. */
|
||
static void
|
||
print_instantiation_full_context (diagnostic_context *context)
|
||
{
|
||
tree p = current_instantiation ();
|
||
location_t location = input_location;
|
||
|
||
if (p)
|
||
{
|
||
if (current_function_decl != TINST_DECL (p)
|
||
&& current_function_decl != NULL_TREE)
|
||
/* We can get here during the processing of some synthesized
|
||
method. Then, TINST_DECL (p) will be the function that's causing
|
||
the synthesis. */
|
||
;
|
||
else
|
||
{
|
||
if (current_function_decl == TINST_DECL (p))
|
||
/* Avoid redundancy with the the "In function" line. */;
|
||
else
|
||
pp_verbatim (context->printer,
|
||
"%s: In instantiation of `%s':\n", location.file,
|
||
decl_as_string (TINST_DECL (p),
|
||
TFF_DECL_SPECIFIERS | TFF_RETURN_TYPE));
|
||
|
||
location.line = TINST_LINE (p);
|
||
location.file = TINST_FILE (p);
|
||
p = TREE_CHAIN (p);
|
||
}
|
||
}
|
||
|
||
print_instantiation_partial_context (context, p, location);
|
||
}
|
||
|
||
/* Same as above but less verbose. */
|
||
static void
|
||
print_instantiation_partial_context (diagnostic_context *context,
|
||
tree t, location_t loc)
|
||
{
|
||
for (; t; t = TREE_CHAIN (t))
|
||
{
|
||
pp_verbatim (context->printer, "%s:%d: instantiated from `%s'\n",
|
||
loc.file, loc.line,
|
||
decl_as_string (TINST_DECL (t),
|
||
TFF_DECL_SPECIFIERS | TFF_RETURN_TYPE));
|
||
loc.line = TINST_LINE (t);
|
||
loc.file = TINST_FILE (t);
|
||
}
|
||
pp_verbatim (context->printer, "%s:%d: instantiated from here\n",
|
||
loc.file, loc.line);
|
||
}
|
||
|
||
/* Called from cp_thing to print the template context for an error. */
|
||
static void
|
||
maybe_print_instantiation_context (diagnostic_context *context)
|
||
{
|
||
if (!problematic_instantiation_changed () || current_instantiation () == 0)
|
||
return;
|
||
|
||
record_last_problematic_instantiation ();
|
||
print_instantiation_full_context (context);
|
||
}
|
||
|
||
/* Report the bare minimum context of a template instantiation. */
|
||
void
|
||
print_instantiation_context (void)
|
||
{
|
||
print_instantiation_partial_context
|
||
(global_dc, current_instantiation (), input_location);
|
||
diagnostic_flush_buffer (global_dc);
|
||
}
|
||
|
||
/* Called from output_format -- during diagnostic message processing --
|
||
to handle C++ specific format specifier with the following meanings:
|
||
%A function argument-list.
|
||
%C tree code.
|
||
%D declaration.
|
||
%E expression.
|
||
%F function declaration.
|
||
%L language as used in extern "lang".
|
||
%O binary operator.
|
||
%P function parameter whose position is indicated by an integer.
|
||
%Q assignment operator.
|
||
%T type.
|
||
%V cv-qualifier. */
|
||
static bool
|
||
cp_printer (pretty_printer *pp, text_info *text)
|
||
{
|
||
int verbose = 0;
|
||
const char *result;
|
||
#define next_tree va_arg (*text->args_ptr, tree)
|
||
#define next_tcode va_arg (*text->args_ptr, enum tree_code)
|
||
#define next_lang va_arg (*text->args_ptr, enum languages)
|
||
#define next_int va_arg (*text->args_ptr, int)
|
||
|
||
if (*text->format_spec == '+')
|
||
++text->format_spec;
|
||
if (*text->format_spec == '#')
|
||
{
|
||
verbose = 1;
|
||
++text->format_spec;
|
||
}
|
||
|
||
switch (*text->format_spec)
|
||
{
|
||
case 'A': result = args_to_string (next_tree, verbose); break;
|
||
case 'C': result = code_to_string (next_tcode); break;
|
||
case 'D': result = decl_to_string (next_tree, verbose); break;
|
||
case 'E': result = expr_to_string (next_tree); break;
|
||
case 'F': result = fndecl_to_string (next_tree, verbose); break;
|
||
case 'L': result = language_to_string (next_lang); break;
|
||
case 'O': result = op_to_string (next_tcode); break;
|
||
case 'P': result = parm_to_string (next_int); break;
|
||
case 'Q': result = assop_to_string (next_tcode); break;
|
||
case 'T': result = type_to_string (next_tree, verbose); break;
|
||
case 'V': result = cv_to_string (next_tree, verbose); break;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
|
||
pp_base_string (pp, result);
|
||
return true;
|
||
#undef next_tree
|
||
#undef next_tcode
|
||
#undef next_lang
|
||
#undef next_int
|
||
}
|
||
|
||
static void
|
||
pp_non_consecutive_character (cxx_pretty_printer *pp, int c)
|
||
{
|
||
const char *p = pp_last_position_in_text (pp);
|
||
|
||
if (p != NULL && *p == c)
|
||
pp_space (pp);
|
||
pp_character (pp, c);
|
||
}
|
||
|
||
/* These are temporary wrapper functions which handle the historic
|
||
behavior of cp_*_at. */
|
||
|
||
static tree
|
||
locate_error (const char *msgid, va_list ap)
|
||
{
|
||
tree here = 0, t;
|
||
int plus = 0;
|
||
const char *f;
|
||
|
||
for (f = msgid; *f; f++)
|
||
{
|
||
plus = 0;
|
||
if (*f == '%')
|
||
{
|
||
f++;
|
||
if (*f == '+')
|
||
f++, plus = 1;
|
||
if (*f == '#')
|
||
f++;
|
||
|
||
switch (*f)
|
||
{
|
||
/* Just ignore these possibilities. */
|
||
case '%': break;
|
||
case 'P':
|
||
case 'd': (void) va_arg (ap, int); break;
|
||
case 's': (void) va_arg (ap, char *); break;
|
||
case 'L': (void) va_arg (ap, enum languages); break;
|
||
case 'C':
|
||
case 'O':
|
||
case 'Q': (void) va_arg (ap, enum tree_code); break;
|
||
|
||
/* These take a tree, which may be where the error is
|
||
located. */
|
||
case 'A':
|
||
case 'D':
|
||
case 'E':
|
||
case 'F':
|
||
case 'T':
|
||
case 'V':
|
||
t = va_arg (ap, tree);
|
||
if (!here || plus)
|
||
here = t;
|
||
break;
|
||
|
||
default:
|
||
errorcount = 0; /* damn ICE suppression */
|
||
internal_error ("unexpected letter `%c' in locate_error\n", *f);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (here == 0)
|
||
here = va_arg (ap, tree);
|
||
|
||
return here;
|
||
}
|
||
|
||
|
||
void
|
||
cp_error_at (const char *msgid, ...)
|
||
{
|
||
tree here;
|
||
diagnostic_info diagnostic;
|
||
va_list ap;
|
||
|
||
va_start (ap, msgid);
|
||
here = locate_error (msgid, ap);
|
||
va_end (ap);
|
||
|
||
va_start (ap, msgid);
|
||
diagnostic_set_info (&diagnostic, msgid, &ap,
|
||
location_of (here), DK_ERROR);
|
||
report_diagnostic (&diagnostic);
|
||
va_end (ap);
|
||
}
|
||
|
||
void
|
||
cp_warning_at (const char *msgid, ...)
|
||
{
|
||
tree here;
|
||
diagnostic_info diagnostic;
|
||
va_list ap;
|
||
|
||
va_start (ap, msgid);
|
||
here = locate_error (msgid, ap);
|
||
va_end (ap);
|
||
|
||
va_start (ap, msgid);
|
||
diagnostic_set_info (&diagnostic, msgid, &ap,
|
||
location_of (here), DK_WARNING);
|
||
report_diagnostic (&diagnostic);
|
||
va_end (ap);
|
||
}
|
||
|
||
void
|
||
cp_pedwarn_at (const char *msgid, ...)
|
||
{
|
||
tree here;
|
||
diagnostic_info diagnostic;
|
||
va_list ap;
|
||
|
||
va_start (ap, msgid);
|
||
here = locate_error (msgid, ap);
|
||
va_end (ap);
|
||
|
||
va_start (ap, msgid);
|
||
diagnostic_set_info (&diagnostic, msgid, &ap,
|
||
location_of (here), pedantic_error_kind());
|
||
report_diagnostic (&diagnostic);
|
||
va_end (ap);
|
||
}
|