/* Implementation of subroutines for the GNU C++ pretty-printer.
   Copyright (C) 2003-2017 Free Software Foundation, Inc.
   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>

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 3, 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 COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
#include "cxx-pretty-print.h"
#include "tree-pretty-print.h"

static void pp_cxx_unqualified_id (cxx_pretty_printer *, tree);
static void pp_cxx_nested_name_specifier (cxx_pretty_printer *, tree);
static void pp_cxx_qualified_id (cxx_pretty_printer *, tree);
static void pp_cxx_template_argument_list (cxx_pretty_printer *, tree);
static void pp_cxx_type_specifier_seq (cxx_pretty_printer *, tree);
static void pp_cxx_ptr_operator (cxx_pretty_printer *, tree);
static void pp_cxx_parameter_declaration_clause (cxx_pretty_printer *, tree);
static void pp_cxx_template_parameter (cxx_pretty_printer *, tree);
static void pp_cxx_cast_expression (cxx_pretty_printer *, tree);
static void pp_cxx_typeid_expression (cxx_pretty_printer *, tree);
static void pp_cxx_unary_left_fold_expression (cxx_pretty_printer *, tree);
static void pp_cxx_unary_right_fold_expression (cxx_pretty_printer *, tree);
static void pp_cxx_binary_fold_expression (cxx_pretty_printer *, tree);


static inline void
pp_cxx_nonconsecutive_character (cxx_pretty_printer *pp, int c)
{
  const char *p = pp_last_position_in_text (pp);

  if (p != NULL && *p == c)
    pp_cxx_whitespace (pp);
  pp_character (pp, c);
  pp->padding = pp_none;
}

#define pp_cxx_expression_list(PP, T)    \
   pp_c_expression_list (PP, T)
#define pp_cxx_space_for_pointer_operator(PP, T)  \
   pp_c_space_for_pointer_operator (PP, T)
#define pp_cxx_init_declarator(PP, T)    \
   pp_c_init_declarator (PP, T)
#define pp_cxx_call_argument_list(PP, T) \
   pp_c_call_argument_list (PP, T)

void
pp_cxx_colon_colon (cxx_pretty_printer *pp)
{
  pp_colon_colon (pp);
  pp->padding = pp_none;
}

void
pp_cxx_begin_template_argument_list (cxx_pretty_printer *pp)
{
  pp_cxx_nonconsecutive_character (pp, '<');
}

void
pp_cxx_end_template_argument_list (cxx_pretty_printer *pp)
{
  pp_cxx_nonconsecutive_character (pp, '>');
}

void
pp_cxx_separate_with (cxx_pretty_printer *pp, int c)
{
  pp_separate_with (pp, c);
  pp->padding = pp_none;
}

/* Expressions.  */

static inline bool
is_destructor_name (tree name)
{
  return name == complete_dtor_identifier
    || name == base_dtor_identifier
    || name == deleting_dtor_identifier;
}

/* conversion-function-id:
      operator conversion-type-id

   conversion-type-id:
      type-specifier-seq conversion-declarator(opt)

   conversion-declarator:
      ptr-operator conversion-declarator(opt)  */

static inline void
pp_cxx_conversion_function_id (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "operator");
  pp_cxx_type_specifier_seq (pp, TREE_TYPE (t));
}

static inline void
pp_cxx_template_id (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 0));
  pp_cxx_begin_template_argument_list (pp);
  pp_cxx_template_argument_list (pp, TREE_OPERAND (t, 1));
  pp_cxx_end_template_argument_list (pp);
}

/* Prints the unqualified part of the id-expression T.

   unqualified-id:
     identifier
     operator-function-id
     conversion-function-id
     ~ class-name
     template-id  */

static void
pp_cxx_unqualified_id (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case RESULT_DECL:
      pp->translate_string ("<return-value>");
      break;

    case OVERLOAD:
      t = OVL_CURRENT (t);
      /* FALLTHRU */
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case TYPE_DECL:
    case FUNCTION_DECL:
    case NAMESPACE_DECL:
    case FIELD_DECL:
    case LABEL_DECL:
    case USING_DECL:
    case TEMPLATE_DECL:
      t = DECL_NAME (t);
      /* FALLTHRU */

    case IDENTIFIER_NODE:
      if (t == NULL)
	pp->translate_string ("<unnamed>");
      else if (IDENTIFIER_TYPENAME_P (t))
	pp_cxx_conversion_function_id (pp, t);
      else
	{
	  if (is_destructor_name (t))
	    {
	      pp_complement (pp);
	      /* FIXME: Why is this necessary? */
	      if (TREE_TYPE (t))
		t = constructor_name (TREE_TYPE (t));
	    }
	  pp_cxx_tree_identifier (pp, t);
	}
      break;

    case TEMPLATE_ID_EXPR:
      pp_cxx_template_id (pp, t);
      break;

    case BASELINK:
      pp_cxx_unqualified_id (pp, BASELINK_FUNCTIONS (t));
      break;

    case RECORD_TYPE:
    case UNION_TYPE:
    case ENUMERAL_TYPE:
    case TYPENAME_TYPE:
    case UNBOUND_CLASS_TEMPLATE:
      pp_cxx_unqualified_id (pp, TYPE_NAME (t));
      if (CLASS_TYPE_P (t) && CLASSTYPE_USE_TEMPLATE (t))
	{
	  pp_cxx_begin_template_argument_list (pp);
	  pp_cxx_template_argument_list (pp, INNERMOST_TEMPLATE_ARGS
                                                 (CLASSTYPE_TI_ARGS (t)));
	  pp_cxx_end_template_argument_list (pp);
	}
      break;

    case BIT_NOT_EXPR:
      pp_cxx_complement (pp);
      pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 0));
      break;

    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
      if (TYPE_IDENTIFIER (t))
	pp_cxx_unqualified_id (pp, TYPE_IDENTIFIER (t));
      else
	pp_cxx_canonical_template_parameter (pp, t);
      break;

    case TEMPLATE_PARM_INDEX:
      pp_cxx_unqualified_id (pp, TEMPLATE_PARM_DECL (t));
      break;

    case BOUND_TEMPLATE_TEMPLATE_PARM:
      pp_cxx_cv_qualifier_seq (pp, t);
      pp_cxx_unqualified_id (pp, TYPE_IDENTIFIER (t));
      pp_cxx_begin_template_argument_list (pp);
      pp_cxx_template_argument_list (pp, TYPE_TI_ARGS (t));
      pp_cxx_end_template_argument_list (pp);
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

/* Pretty-print out the token sequence ":: template" in template codes
   where it is needed to "inline declare" the (following) member as
   a template.  This situation arises when SCOPE of T is dependent
   on template parameters.  */

static inline void
pp_cxx_template_keyword_if_needed (cxx_pretty_printer *pp, tree scope, tree t)
{
  if (TREE_CODE (t) == TEMPLATE_ID_EXPR
      && TYPE_P (scope) && dependent_type_p (scope))
    pp_cxx_ws_string (pp, "template");
}

/* nested-name-specifier:
      class-or-namespace-name :: nested-name-specifier(opt)
      class-or-namespace-name :: template nested-name-specifier   */

static void
pp_cxx_nested_name_specifier (cxx_pretty_printer *pp, tree t)
{
  if (!SCOPE_FILE_SCOPE_P (t) && t != pp->enclosing_scope)
    {
      tree scope = get_containing_scope (t);
      pp_cxx_nested_name_specifier (pp, scope);
      pp_cxx_template_keyword_if_needed (pp, scope, t);
      pp_cxx_unqualified_id (pp, t);
      pp_cxx_colon_colon (pp);
    }
}

/* qualified-id:
      nested-name-specifier template(opt) unqualified-id  */

static void
pp_cxx_qualified_id (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
      /* A pointer-to-member is always qualified.  */
    case PTRMEM_CST:
      pp_cxx_nested_name_specifier (pp, PTRMEM_CST_CLASS (t));
      pp_cxx_unqualified_id (pp, PTRMEM_CST_MEMBER (t));
      break;

      /* In Standard C++, functions cannot possibly be used as
	 nested-name-specifiers.  However, there are situations where
	 is "makes sense" to output the surrounding function name for the
	 purpose of emphasizing on the scope kind.  Just printing the
	 function name might not be sufficient as it may be overloaded; so,
	 we decorate the function with its signature too.
	 FIXME:  This is probably the wrong pretty-printing for conversion
	 functions and some function templates.  */
    case OVERLOAD:
      t = OVL_CURRENT (t);
      /* FALLTHRU */
    case FUNCTION_DECL:
      if (DECL_FUNCTION_MEMBER_P (t))
	pp_cxx_nested_name_specifier (pp, DECL_CONTEXT (t));
      pp_cxx_unqualified_id
	(pp, DECL_CONSTRUCTOR_P (t) ? DECL_CONTEXT (t) : t);
      pp_cxx_parameter_declaration_clause (pp, TREE_TYPE (t));
      break;

    case OFFSET_REF:
    case SCOPE_REF:
      pp_cxx_nested_name_specifier (pp, TREE_OPERAND (t, 0));
      pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 1));
      break;

    default:
      {
	tree scope = get_containing_scope (t);
	if (scope != pp->enclosing_scope)
	  {
	    pp_cxx_nested_name_specifier (pp, scope);
	    pp_cxx_template_keyword_if_needed (pp, scope, t);
	  }
	pp_cxx_unqualified_id (pp, t);
      }
      break;
    }
}


void
cxx_pretty_printer::constant (tree t)
{
  switch (TREE_CODE (t))
    {
    case STRING_CST:
      {
	const bool in_parens = PAREN_STRING_LITERAL_P (t);
	if (in_parens)
	  pp_cxx_left_paren (this);
	c_pretty_printer::constant (t);
	if (in_parens)
	  pp_cxx_right_paren (this);
      }
      break;

    case INTEGER_CST:
      if (NULLPTR_TYPE_P (TREE_TYPE (t)))
	{
	  pp_string (this, "nullptr");
	  break;
	}
      /* fall through.  */

    default:
      c_pretty_printer::constant (t);
      break;
    }
}

/* id-expression:
      unqualified-id
      qualified-id   */

void
cxx_pretty_printer::id_expression (tree t)
{
  if (TREE_CODE (t) == OVERLOAD)
    t = OVL_CURRENT (t);
  if (DECL_P (t) && DECL_CONTEXT (t))
    pp_cxx_qualified_id (this, t);
  else
    pp_cxx_unqualified_id (this, t);
}

/* user-defined literal:
      literal ud-suffix  */

void
pp_cxx_userdef_literal (cxx_pretty_printer *pp, tree t)
{
  pp->constant (USERDEF_LITERAL_VALUE (t));
  pp->id_expression (USERDEF_LITERAL_SUFFIX_ID (t));
}


/* primary-expression:
     literal
     this
     :: identifier
     :: operator-function-id
     :: qualifier-id
     ( expression )
     id-expression   

   GNU Extensions:
     __builtin_va_arg ( assignment-expression , type-id )
     __builtin_offsetof ( type-id, offsetof-expression )
     __builtin_addressof ( expression )

     __has_nothrow_assign ( type-id )   
     __has_nothrow_constructor ( type-id )
     __has_nothrow_copy ( type-id )
     __has_trivial_assign ( type-id )   
     __has_trivial_constructor ( type-id )
     __has_trivial_copy ( type-id )
     __has_unique_object_representations ( type-id )
     __has_trivial_destructor ( type-id )
     __has_virtual_destructor ( type-id )     
     __is_abstract ( type-id )
     __is_base_of ( type-id , type-id )
     __is_class ( type-id )
     __is_empty ( type-id )
     __is_enum ( type-id )
     __is_literal_type ( type-id )
     __is_pod ( type-id )
     __is_polymorphic ( type-id )
     __is_std_layout ( type-id )
     __is_trivial ( type-id )
     __is_union ( type-id )  */

void
cxx_pretty_printer::primary_expression (tree t)
{
  switch (TREE_CODE (t))
    {
    case VOID_CST:
    case INTEGER_CST:
    case REAL_CST:
    case COMPLEX_CST:
    case STRING_CST:
      constant (t);
      break;

    case USERDEF_LITERAL:
      pp_cxx_userdef_literal (this, t);
      break;

    case BASELINK:
      t = BASELINK_FUNCTIONS (t);
      /* FALLTHRU */
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case FUNCTION_DECL:
    case OVERLOAD:
    case CONST_DECL:
    case TEMPLATE_DECL:
      id_expression (t);
      break;

    case RESULT_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
    case TEMPLATE_PARM_INDEX:
      pp_cxx_unqualified_id (this, t);
      break;

    case STMT_EXPR:
      pp_cxx_left_paren (this);
      statement (STMT_EXPR_STMT (t));
      pp_cxx_right_paren (this);
      break;

    case TRAIT_EXPR:
      pp_cxx_trait_expression (this, t);
      break;

    case VA_ARG_EXPR:
      pp_cxx_va_arg_expression (this, t);
      break;

    case OFFSETOF_EXPR:
      pp_cxx_offsetof_expression (this, t);
      break;

    case ADDRESSOF_EXPR:
      pp_cxx_addressof_expression (this, t);
      break;

    case REQUIRES_EXPR:
      pp_cxx_requires_expr (this, t);
      break;

    default:
      c_pretty_printer::primary_expression (t);
      break;
    }
}

/* postfix-expression:
     primary-expression
     postfix-expression [ expression ]
     postfix-expression ( expression-list(opt) )
     simple-type-specifier ( expression-list(opt) )
     typename ::(opt) nested-name-specifier identifier ( expression-list(opt) )
     typename ::(opt) nested-name-specifier template(opt)
				       template-id ( expression-list(opt) )
     postfix-expression . template(opt) ::(opt) id-expression
     postfix-expression -> template(opt) ::(opt) id-expression
     postfix-expression . pseudo-destructor-name
     postfix-expression -> pseudo-destructor-name
     postfix-expression ++
     postfix-expression --
     dynamic_cast < type-id > ( expression )
     static_cast < type-id > ( expression )
     reinterpret_cast < type-id > ( expression )
     const_cast < type-id > ( expression )
     typeid ( expression )
     typeid ( type-id )  */

void
cxx_pretty_printer::postfix_expression (tree t)
{
  enum tree_code code = TREE_CODE (t);

  switch (code)
    {
    case AGGR_INIT_EXPR:
    case CALL_EXPR:
      {
	tree fun = cp_get_callee (t);
	tree saved_scope = enclosing_scope;
	bool skipfirst = false;
	tree arg;

	if (TREE_CODE (fun) == ADDR_EXPR)
	  fun = TREE_OPERAND (fun, 0);

	/* In templates, where there is no way to tell whether a given
	   call uses an actual member function.  So the parser builds
	   FUN as a COMPONENT_REF or a plain IDENTIFIER_NODE until
	   instantiation time.  */
	if (TREE_CODE (fun) != FUNCTION_DECL)
	  ;
	else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun))
	  {
	    tree object = (code == AGGR_INIT_EXPR
			   ? (AGGR_INIT_VIA_CTOR_P (t)
			      ? AGGR_INIT_EXPR_SLOT (t)
			      : AGGR_INIT_EXPR_ARG (t, 0))
			   : CALL_EXPR_ARG (t, 0));

	    while (TREE_CODE (object) == NOP_EXPR)
	      object = TREE_OPERAND (object, 0);

	    if (TREE_CODE (object) == ADDR_EXPR)
	      object = TREE_OPERAND (object, 0);

	    if (!TYPE_PTR_P (TREE_TYPE (object)))
	      {
		postfix_expression (object);
		pp_cxx_dot (this);
	      }
	    else
	      {
		postfix_expression (object);
		pp_cxx_arrow (this);
	      }
	    skipfirst = true;
	    enclosing_scope = strip_pointer_operator (TREE_TYPE (object));
	  }

	postfix_expression (fun);
	enclosing_scope = saved_scope;
	pp_cxx_left_paren (this);
	if (code == AGGR_INIT_EXPR)
	  {
	    aggr_init_expr_arg_iterator iter;
	    FOR_EACH_AGGR_INIT_EXPR_ARG (arg, iter, t)
	      {
		if (skipfirst)
		  skipfirst = false;
		else
		  {
		    expression (arg);
		    if (more_aggr_init_expr_args_p (&iter))
		      pp_cxx_separate_with (this, ',');
		  }
	      }
	  }
	else
	  {
	    call_expr_arg_iterator iter;
	    FOR_EACH_CALL_EXPR_ARG (arg, iter, t)
	      {
		if (skipfirst)
		  skipfirst = false;
		else
		  {
		    expression (arg);
		    if (more_call_expr_args_p (&iter))
		      pp_cxx_separate_with (this, ',');
		  }
	      }
	  }
	pp_cxx_right_paren (this);
      }
      if (code == AGGR_INIT_EXPR && AGGR_INIT_VIA_CTOR_P (t))
	{
	  pp_cxx_separate_with (this, ',');
	  postfix_expression (AGGR_INIT_EXPR_SLOT (t));
	}
      break;

    case BASELINK:
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case FUNCTION_DECL:
    case OVERLOAD:
    case CONST_DECL:
    case TEMPLATE_DECL:
    case RESULT_DECL:
      primary_expression (t);
      break;

    case DYNAMIC_CAST_EXPR:
    case STATIC_CAST_EXPR:
    case REINTERPRET_CAST_EXPR:
    case CONST_CAST_EXPR:
      if (code == DYNAMIC_CAST_EXPR)
	pp_cxx_ws_string (this, "dynamic_cast");
      else if (code == STATIC_CAST_EXPR)
	pp_cxx_ws_string (this, "static_cast");
      else if (code == REINTERPRET_CAST_EXPR)
	pp_cxx_ws_string (this, "reinterpret_cast");
      else
	pp_cxx_ws_string (this, "const_cast");
      pp_cxx_begin_template_argument_list (this);
      type_id (TREE_TYPE (t));
      pp_cxx_end_template_argument_list (this);
      pp_left_paren (this);
      expression (TREE_OPERAND (t, 0));
      pp_right_paren (this);
      break;

    case EMPTY_CLASS_EXPR:
      type_id (TREE_TYPE (t));
      pp_left_paren (this);
      pp_right_paren (this);
      break;

    case TYPEID_EXPR:
      pp_cxx_typeid_expression (this, t);
      break;

    case PSEUDO_DTOR_EXPR:
      postfix_expression (TREE_OPERAND (t, 0));
      pp_cxx_dot (this);
      if (TREE_OPERAND (t, 1))
	{
	  pp_cxx_qualified_id (this, TREE_OPERAND (t, 1));
	  pp_cxx_colon_colon (this);
	}
      pp_complement (this);
      pp_cxx_unqualified_id (this, TREE_OPERAND (t, 2));
      break;

    case ARROW_EXPR:
      postfix_expression (TREE_OPERAND (t, 0));
      pp_cxx_arrow (this);
      break;

    default:
      c_pretty_printer::postfix_expression (t);
      break;
    }
}

/* new-expression:
      ::(opt) new new-placement(opt) new-type-id new-initializer(opt)
      ::(opt) new new-placement(opt) ( type-id ) new-initializer(opt)

   new-placement:
      ( expression-list )

   new-type-id:
      type-specifier-seq new-declarator(opt)

   new-declarator:
      ptr-operator new-declarator(opt)
      direct-new-declarator

   direct-new-declarator
      [ expression ]
      direct-new-declarator [ constant-expression ]

   new-initializer:
      ( expression-list(opt) )  */

static void
pp_cxx_new_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  tree type = TREE_OPERAND (t, 1);
  tree init = TREE_OPERAND (t, 2);
  switch (code)
    {
    case NEW_EXPR:
    case VEC_NEW_EXPR:
      if (NEW_EXPR_USE_GLOBAL (t))
	pp_cxx_colon_colon (pp);
      pp_cxx_ws_string (pp, "new");
      if (TREE_OPERAND (t, 0))
	{
	  pp_cxx_call_argument_list (pp, TREE_OPERAND (t, 0));
	  pp_space (pp);
	}
      if (TREE_CODE (type) == ARRAY_REF)
	type = build_cplus_array_type
	  (TREE_OPERAND (type, 0),
	   build_index_type (fold_build2_loc (input_location,
					  MINUS_EXPR, integer_type_node,
					  TREE_OPERAND (type, 1),
					  integer_one_node)));
      pp->type_id (type);
      if (init)
	{
	  pp_left_paren (pp);
	  if (TREE_CODE (init) == TREE_LIST)
	    pp_c_expression_list (pp, init);
	  else if (init == void_node)
	    ;			/* OK, empty initializer list.  */
	  else
	    pp->expression (init);
	  pp_right_paren (pp);
	}
      break;

    default:
      pp_unsupported_tree (pp, t);
    }
}

/* delete-expression:
      ::(opt) delete cast-expression
      ::(opt) delete [ ] cast-expression   */

static void
pp_cxx_delete_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      if (DELETE_EXPR_USE_GLOBAL (t))
	pp_cxx_colon_colon (pp);
      pp_cxx_ws_string (pp, "delete");
      pp_space (pp);
      if (code == VEC_DELETE_EXPR
	  || DELETE_EXPR_USE_VEC (t))
	{
	  pp_left_bracket (pp);
	  pp_right_bracket (pp);
	  pp_space (pp);
	}
      pp_c_cast_expression (pp, TREE_OPERAND (t, 0));
      break;

    default:
      pp_unsupported_tree (pp, t);
    }
}

/* unary-expression:
      postfix-expression
      ++ cast-expression
      -- cast-expression
      unary-operator cast-expression
      sizeof unary-expression
      sizeof ( type-id )
      sizeof ... ( identifier )
      new-expression
      delete-expression

   unary-operator: one of
      *   &   +   -  !

   GNU extensions:
      __alignof__ unary-expression
      __alignof__ ( type-id )  */

void
cxx_pretty_printer::unary_expression (tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case NEW_EXPR:
    case VEC_NEW_EXPR:
      pp_cxx_new_expression (this, t);
      break;

    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      pp_cxx_delete_expression (this, t);
      break;

    case SIZEOF_EXPR:
      if (PACK_EXPANSION_P (TREE_OPERAND (t, 0)))
	{
	  pp_cxx_ws_string (this, "sizeof");
	  pp_cxx_ws_string (this, "...");
	  pp_cxx_whitespace (this);
	  pp_cxx_left_paren (this);
	  if (TYPE_P (TREE_OPERAND (t, 0)))
	    type_id (TREE_OPERAND (t, 0));
	  else
	    unary_expression (TREE_OPERAND (t, 0));
	  pp_cxx_right_paren (this);
	  break;
	}
      /* Fall through  */

    case ALIGNOF_EXPR:
      pp_cxx_ws_string (this, code == SIZEOF_EXPR ? "sizeof" : "__alignof__");
      pp_cxx_whitespace (this);
      if (TREE_CODE (t) == SIZEOF_EXPR && SIZEOF_EXPR_TYPE_P (t))
	{
	  pp_cxx_left_paren (this);
	  type_id (TREE_TYPE (TREE_OPERAND (t, 0)));
	  pp_cxx_right_paren (this);
	}
      else if (TYPE_P (TREE_OPERAND (t, 0)))
	{
	  pp_cxx_left_paren (this);
	  type_id (TREE_OPERAND (t, 0));
	  pp_cxx_right_paren (this);
	}
      else
	unary_expression (TREE_OPERAND (t, 0));
      break;

    case AT_ENCODE_EXPR:
      pp_cxx_ws_string (this, "@encode");
      pp_cxx_whitespace (this);
      pp_cxx_left_paren (this);
      type_id (TREE_OPERAND (t, 0));
      pp_cxx_right_paren (this);
      break;      

    case NOEXCEPT_EXPR:
      pp_cxx_ws_string (this, "noexcept");
      pp_cxx_whitespace (this);
      pp_cxx_left_paren (this);
      expression (TREE_OPERAND (t, 0));
      pp_cxx_right_paren (this);
      break;

    case UNARY_PLUS_EXPR:
      pp_plus (this);
      pp_cxx_cast_expression (this, TREE_OPERAND (t, 0));
      break;

    default:
      c_pretty_printer::unary_expression (t);
      break;
    }
}

/* cast-expression:
      unary-expression
      ( type-id ) cast-expression  */

static void
pp_cxx_cast_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case CAST_EXPR:
    case IMPLICIT_CONV_EXPR:
      pp->type_id (TREE_TYPE (t));
      pp_cxx_call_argument_list (pp, TREE_OPERAND (t, 0));
      break;

    default:
      pp_c_cast_expression (pp, t);
      break;
    }
}

/* pm-expression:
      cast-expression
      pm-expression .* cast-expression
      pm-expression ->* cast-expression  */

static void
pp_cxx_pm_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
      /* Handle unfortunate OFFSET_REF overloading here.  */
    case OFFSET_REF:
      if (TYPE_P (TREE_OPERAND (t, 0)))
	{
	  pp_cxx_qualified_id (pp, t);
	  break;
	}
      /* Fall through.  */
    case MEMBER_REF:
    case DOTSTAR_EXPR:
      pp_cxx_pm_expression (pp, TREE_OPERAND (t, 0));
      if (TREE_CODE (t) == MEMBER_REF)
	pp_cxx_arrow (pp);
      else
	pp_cxx_dot (pp);
      pp_star(pp);
      pp_cxx_cast_expression (pp, TREE_OPERAND (t, 1));
      break;


    default:
      pp_cxx_cast_expression (pp, t);
      break;
    }
}

/* multiplicative-expression:
      pm-expression
      multiplicative-expression * pm-expression
      multiplicative-expression / pm-expression
      multiplicative-expression % pm-expression  */

void
cxx_pretty_printer::multiplicative_expression (tree e)
{
  enum tree_code code = TREE_CODE (e);
  switch (code)
    {
    case MULT_EXPR:
    case TRUNC_DIV_EXPR:
    case TRUNC_MOD_EXPR:
      multiplicative_expression (TREE_OPERAND (e, 0));
      pp_space (this);
      if (code == MULT_EXPR)
	pp_star (this);
      else if (code == TRUNC_DIV_EXPR)
	pp_slash (this);
      else
	pp_modulo (this);
      pp_space (this);
      pp_cxx_pm_expression (this, TREE_OPERAND (e, 1));
      break;

    default:
      pp_cxx_pm_expression (this, e);
      break;
    }
}

/* conditional-expression:
      logical-or-expression
      logical-or-expression ?  expression  : assignment-expression  */

void
cxx_pretty_printer::conditional_expression (tree e)
{
  if (TREE_CODE (e) == COND_EXPR)
    {
      pp_c_logical_or_expression (this, TREE_OPERAND (e, 0));
      pp_space (this);
      pp_question (this);
      pp_space (this);
      expression (TREE_OPERAND (e, 1));
      pp_space (this);
      assignment_expression (TREE_OPERAND (e, 2));
    }
  else
    pp_c_logical_or_expression (this, e);
}

/* Pretty-print a compound assignment operator token as indicated by T.  */

static void
pp_cxx_assignment_operator (cxx_pretty_printer *pp, tree t)
{
  const char *op;

  switch (TREE_CODE (t))
    {
    case NOP_EXPR:
      op = "=";
      break;

    case PLUS_EXPR:
      op = "+=";
      break;

    case MINUS_EXPR:
      op = "-=";
      break;

    case TRUNC_DIV_EXPR:
      op = "/=";
      break;

    case TRUNC_MOD_EXPR:
      op = "%=";
      break;

    default:
      op = get_tree_code_name (TREE_CODE (t));
      break;
    }

  pp_cxx_ws_string (pp, op);
}


/* assignment-expression:
      conditional-expression
      logical-or-expression assignment-operator assignment-expression
      throw-expression

   throw-expression:
       throw assignment-expression(opt)

   assignment-operator: one of
      =    *=    /=    %=    +=    -=    >>=    <<=    &=    ^=    |=  */

void
cxx_pretty_printer::assignment_expression (tree e)
{
  switch (TREE_CODE (e))
    {
    case MODIFY_EXPR:
    case INIT_EXPR:
      pp_c_logical_or_expression (this, TREE_OPERAND (e, 0));
      pp_space (this);
      pp_equal (this);
      pp_space (this);
      assignment_expression (TREE_OPERAND (e, 1));
      break;

    case THROW_EXPR:
      pp_cxx_ws_string (this, "throw");
      if (TREE_OPERAND (e, 0))
	assignment_expression (TREE_OPERAND (e, 0));
      break;

    case MODOP_EXPR:
      pp_c_logical_or_expression (this, TREE_OPERAND (e, 0));
      pp_cxx_assignment_operator (this, TREE_OPERAND (e, 1));
      assignment_expression (TREE_OPERAND (e, 2));
      break;

    default:
      conditional_expression (e);
      break;
    }
}

void
cxx_pretty_printer::expression (tree t)
{
  switch (TREE_CODE (t))
    {
    case STRING_CST:
    case VOID_CST:
    case INTEGER_CST:
    case REAL_CST:
    case COMPLEX_CST:
      constant (t);
      break;

    case USERDEF_LITERAL:
      pp_cxx_userdef_literal (this, t);
      break;

    case RESULT_DECL:
      pp_cxx_unqualified_id (this, t);
      break;

#if 0
    case OFFSET_REF:
#endif
    case SCOPE_REF:
    case PTRMEM_CST:
      pp_cxx_qualified_id (this, t);
      break;

    case OVERLOAD:
      t = OVL_CURRENT (t);
      /* FALLTHRU */
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case CONST_DECL:
    case FUNCTION_DECL:
    case BASELINK:
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
    case TEMPLATE_TEMPLATE_PARM:
    case STMT_EXPR:
    case REQUIRES_EXPR:
      primary_expression (t);
      break;

    case CALL_EXPR:
    case DYNAMIC_CAST_EXPR:
    case STATIC_CAST_EXPR:
    case REINTERPRET_CAST_EXPR:
    case CONST_CAST_EXPR:
#if 0
    case MEMBER_REF:
#endif
    case EMPTY_CLASS_EXPR:
    case TYPEID_EXPR:
    case PSEUDO_DTOR_EXPR:
    case AGGR_INIT_EXPR:
    case ARROW_EXPR:
      postfix_expression (t);
      break;

    case NEW_EXPR:
    case VEC_NEW_EXPR:
      pp_cxx_new_expression (this, t);
      break;

    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      pp_cxx_delete_expression (this, t);
      break;

    case SIZEOF_EXPR:
    case ALIGNOF_EXPR:
    case NOEXCEPT_EXPR:
    case UNARY_PLUS_EXPR:
      unary_expression (t);
      break;

    case CAST_EXPR:
    case IMPLICIT_CONV_EXPR:
      pp_cxx_cast_expression (this, t);
      break;

    case OFFSET_REF:
    case MEMBER_REF:
    case DOTSTAR_EXPR:
      pp_cxx_pm_expression (this, t);
      break;

    case MULT_EXPR:
    case TRUNC_DIV_EXPR:
    case TRUNC_MOD_EXPR:
      multiplicative_expression (t);
      break;

    case COND_EXPR:
      conditional_expression (t);
      break;

    case MODIFY_EXPR:
    case INIT_EXPR:
    case THROW_EXPR:
    case MODOP_EXPR:
      assignment_expression (t);
      break;

    case NON_DEPENDENT_EXPR:
    case MUST_NOT_THROW_EXPR:
      expression (TREE_OPERAND (t, 0));
      break;

    case EXPR_PACK_EXPANSION:
      expression (PACK_EXPANSION_PATTERN (t));
      pp_cxx_ws_string (this, "...");
      break;

    case UNARY_LEFT_FOLD_EXPR:
      pp_cxx_unary_left_fold_expression (this, t);
      break;

    case UNARY_RIGHT_FOLD_EXPR:
      pp_cxx_unary_right_fold_expression (this, t);
    break;

    case BINARY_LEFT_FOLD_EXPR:
    case BINARY_RIGHT_FOLD_EXPR:
      pp_cxx_binary_fold_expression (this, t);
      break;

    case TEMPLATE_ID_EXPR:
      pp_cxx_template_id (this, t);
      break;

    case NONTYPE_ARGUMENT_PACK:
      {
	tree args = ARGUMENT_PACK_ARGS (t);
	int i, len = TREE_VEC_LENGTH (args);
	for (i = 0; i < len; ++i)
	  {
	    if (i > 0)
	      pp_cxx_separate_with (this, ',');
	    expression (TREE_VEC_ELT (args, i));
	  }
      }
      break;
      
    case LAMBDA_EXPR:
      pp_cxx_ws_string (this, "<lambda>");
      break;

    case TRAIT_EXPR:
      pp_cxx_trait_expression (this, t);
      break;

    case PRED_CONSTR:
    case CHECK_CONSTR:
    case EXPR_CONSTR:
    case TYPE_CONSTR:
    case ICONV_CONSTR:
    case DEDUCT_CONSTR:
    case EXCEPT_CONSTR:
    case PARM_CONSTR:
    case CONJ_CONSTR:
    case DISJ_CONSTR:
      pp_cxx_constraint (this, t);
      break;

    case PAREN_EXPR:
      pp_cxx_left_paren (this);
      expression (TREE_OPERAND (t, 0));
      pp_cxx_right_paren (this);
      break;

    default:
      c_pretty_printer::expression (t);
      break;
    }
}


/* Declarations.  */

/* function-specifier:
      inline
      virtual
      explicit   */

void
cxx_pretty_printer::function_specifier (tree t)
{
  switch (TREE_CODE (t))
    {
    case FUNCTION_DECL:
      if (DECL_VIRTUAL_P (t))
	pp_cxx_ws_string (this, "virtual");
      else if (DECL_CONSTRUCTOR_P (t) && DECL_NONCONVERTING_P (t))
	pp_cxx_ws_string (this, "explicit");
      else
        c_pretty_printer::function_specifier (t);

    default:
      break;
    }
}

/* decl-specifier-seq:
      decl-specifier-seq(opt) decl-specifier

   decl-specifier:
      storage-class-specifier
      type-specifier
      function-specifier
      friend
      typedef  */

void
cxx_pretty_printer::declaration_specifiers (tree t)
{
  switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case FIELD_DECL:
      storage_class_specifier (t);
      declaration_specifiers (TREE_TYPE (t));
      break;

    case TYPE_DECL:
      pp_cxx_ws_string (this, "typedef");
      declaration_specifiers (TREE_TYPE (t));
      break;

    case FUNCTION_DECL:
      /* Constructors don't have return types.  And conversion functions
	 do not have a type-specifier in their return types.  */
      if (DECL_CONSTRUCTOR_P (t) || DECL_CONV_FN_P (t))
	function_specifier (t);
      else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t))
	declaration_specifiers (TREE_TYPE (TREE_TYPE (t)));
      else
        c_pretty_printer::declaration_specifiers (t);
      break;
    default:
        c_pretty_printer::declaration_specifiers (t);
      break;
    }
}

/* simple-type-specifier:
      ::(opt) nested-name-specifier(opt) type-name
      ::(opt) nested-name-specifier(opt) template(opt) template-id
      char
      wchar_t
      bool
      short
      int
      long
      signed
      unsigned
      float
      double
      void  */

void
cxx_pretty_printer::simple_type_specifier (tree t)
{
  switch (TREE_CODE (t))
    {
    case RECORD_TYPE:
    case UNION_TYPE:
    case ENUMERAL_TYPE:
      pp_cxx_qualified_id (this, t);
      break;

    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
    case TEMPLATE_PARM_INDEX:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
      pp_cxx_unqualified_id (this, t);
      break;

    case TYPENAME_TYPE:
      pp_cxx_ws_string (this, "typename");
      pp_cxx_nested_name_specifier (this, TYPE_CONTEXT (t));
      pp_cxx_unqualified_id (this, TYPE_NAME (t));
      break;

    default:
      c_pretty_printer::simple_type_specifier (t);
      break;
    }
}

/* type-specifier-seq:
      type-specifier type-specifier-seq(opt)

   type-specifier:
      simple-type-specifier
      class-specifier
      enum-specifier
      elaborated-type-specifier
      cv-qualifier   */

static void
pp_cxx_type_specifier_seq (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
    case TYPE_DECL:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
      pp_cxx_cv_qualifier_seq (pp, t);
      pp->simple_type_specifier (t);
      break;

    case METHOD_TYPE:
      pp_cxx_type_specifier_seq (pp, TREE_TYPE (t));
      pp_cxx_space_for_pointer_operator (pp, TREE_TYPE (t));
      pp_cxx_nested_name_specifier (pp, TYPE_METHOD_BASETYPE (t));
      break;

    case DECLTYPE_TYPE:
      pp_cxx_ws_string (pp, "decltype");
      pp_cxx_left_paren (pp);
      pp->expression (DECLTYPE_TYPE_EXPR (t));
      pp_cxx_right_paren (pp);
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
	{
	  tree pfm = TYPE_PTRMEMFUNC_FN_TYPE (t);
	  pp->declaration_specifiers (TREE_TYPE (TREE_TYPE (pfm)));
	  pp_cxx_whitespace (pp);
	  pp_cxx_ptr_operator (pp, t);
	  break;
	}
      /* fall through */

    default:
      if (!(TREE_CODE (t) == FUNCTION_DECL && DECL_CONSTRUCTOR_P (t)))
	pp_c_specifier_qualifier_list (pp, t);
    }
}

/* ptr-operator:
      * cv-qualifier-seq(opt)
      &
      ::(opt) nested-name-specifier * cv-qualifier-seq(opt)  */

static void
pp_cxx_ptr_operator (cxx_pretty_printer *pp, tree t)
{
  if (!TYPE_P (t) && TREE_CODE (t) != TYPE_DECL)
    t = TREE_TYPE (t);
  switch (TREE_CODE (t))
    {
    case REFERENCE_TYPE:
    case POINTER_TYPE:
      if (TYPE_PTR_OR_PTRMEM_P (TREE_TYPE (t)))
	pp_cxx_ptr_operator (pp, TREE_TYPE (t));
      pp_c_attributes_display (pp, TYPE_ATTRIBUTES (TREE_TYPE (t)));
      if (TYPE_PTR_P (t))
	{
	  pp_star (pp);
	  pp_cxx_cv_qualifier_seq (pp, t);
	}
      else
	pp_ampersand (pp);
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
	{
	  pp_cxx_left_paren (pp);
	  pp_cxx_nested_name_specifier (pp, TYPE_PTRMEMFUNC_OBJECT_TYPE (t));
	  pp_star (pp);
	  break;
	}
      /* FALLTHRU */
    case OFFSET_TYPE:
      if (TYPE_PTRMEM_P (t))
	{
	  if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
	    pp_cxx_left_paren (pp);
	  pp_cxx_nested_name_specifier (pp, TYPE_PTRMEM_CLASS_TYPE (t));
	  pp_star (pp);
	  pp_cxx_cv_qualifier_seq (pp, t);
	  break;
	}
      /* fall through.  */

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

static inline tree
pp_cxx_implicit_parameter_type (tree mf)
{
  return class_of_this_parm (TREE_TYPE (mf));
}

/*
   parameter-declaration:
      decl-specifier-seq declarator
      decl-specifier-seq declarator = assignment-expression
      decl-specifier-seq abstract-declarator(opt)
      decl-specifier-seq abstract-declarator(opt) assignment-expression  */

static inline void
pp_cxx_parameter_declaration (cxx_pretty_printer *pp, tree t)
{
  pp->declaration_specifiers (t);
  if (TYPE_P (t))
    pp->abstract_declarator (t);
  else
    pp->declarator (t);
}

/* parameter-declaration-clause:
      parameter-declaration-list(opt) ...(opt)
      parameter-declaration-list , ...

   parameter-declaration-list:
      parameter-declaration
      parameter-declaration-list , parameter-declaration  */

static void
pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t)
{
  tree args;
  tree types;
  bool abstract;

  // For a requires clause or the explicit printing of a parameter list
  // we expect T to be a chain of PARM_DECLs. Otherwise, the list of
  // args and types are taken from the function decl T.
  if (TREE_CODE (t) == PARM_DECL)
    {
      args = t;
      types = t;
      abstract = false;
    }
  else
    {
      bool type_p = TYPE_P (t);
      args = type_p ? NULL : FUNCTION_FIRST_USER_PARM (t);
      types = type_p ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
      abstract = args == NULL || pp->flags & pp_c_flag_abstract;
    }
  bool first = true;

  /* Skip artificial parameter for nonstatic member functions.  */
  if (TREE_CODE (t) == METHOD_TYPE)
    types = TREE_CHAIN (types);

  pp_cxx_left_paren (pp);
  for (; args; args = TREE_CHAIN (args), types = TREE_CHAIN (types))
    {
      if (!first)
	pp_cxx_separate_with (pp, ',');
      first = false;
      pp_cxx_parameter_declaration (pp, abstract ? TREE_VALUE (types) : args);
      if (!abstract && pp->flags & pp_cxx_flag_default_argument)
	{
	  pp_cxx_whitespace (pp);
	  pp_equal (pp);
	  pp_cxx_whitespace (pp);
	  pp->assignment_expression (TREE_PURPOSE (types));
	}
    }
  pp_cxx_right_paren (pp);
}

/* exception-specification:
      throw ( type-id-list(opt) )

   type-id-list
      type-id
      type-id-list , type-id   */

static void
pp_cxx_exception_specification (cxx_pretty_printer *pp, tree t)
{
  tree ex_spec = TYPE_RAISES_EXCEPTIONS (t);
  bool need_comma = false;

  if (ex_spec == NULL)
    return;
  if (TREE_PURPOSE (ex_spec))
    {
      pp_cxx_ws_string (pp, "noexcept");
      pp_cxx_whitespace (pp);
      pp_cxx_left_paren (pp);
      if (DEFERRED_NOEXCEPT_SPEC_P (ex_spec))
	pp_cxx_ws_string (pp, "<uninstantiated>");
      else
	pp->expression (TREE_PURPOSE (ex_spec));
      pp_cxx_right_paren (pp);
      return;
    }
  pp_cxx_ws_string (pp, "throw");
  pp_cxx_left_paren (pp);
  for (; ex_spec && TREE_VALUE (ex_spec); ex_spec = TREE_CHAIN (ex_spec))
    {
      tree type = TREE_VALUE (ex_spec);
      tree argpack = NULL_TREE;
      int i, len = 1;

      if (ARGUMENT_PACK_P (type))
	{
	  argpack = ARGUMENT_PACK_ARGS (type);
	  len = TREE_VEC_LENGTH (argpack);
	}

      for (i = 0; i < len; ++i)
	{
	  if (argpack)
	    type = TREE_VEC_ELT (argpack, i);

	  if (need_comma)
	    pp_cxx_separate_with (pp, ',');
	  else
	    need_comma = true;

	  pp->type_id (type);
	}
    }
  pp_cxx_right_paren (pp);
}

/* direct-declarator:
      declarator-id
      direct-declarator ( parameter-declaration-clause ) cv-qualifier-seq(opt)
					    exception-specification(opt)
      direct-declaration [ constant-expression(opt) ]
      ( declarator )  */

void
cxx_pretty_printer::direct_declarator (tree t)
{
  switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case FIELD_DECL:
      if (DECL_NAME (t))
	{
	  pp_cxx_space_for_pointer_operator (this, TREE_TYPE (t));

	  if ((TREE_CODE (t) == PARM_DECL && DECL_PACK_P (t))
	      || template_parameter_pack_p (t))
	    /* A function parameter pack or non-type template
	       parameter pack.  */
	    pp_cxx_ws_string (this, "...");
		      
	  id_expression (DECL_NAME (t));
	}
      abstract_declarator (TREE_TYPE (t));
      break;

    case FUNCTION_DECL:
      pp_cxx_space_for_pointer_operator (this, TREE_TYPE (TREE_TYPE (t)));
      expression (t);
      pp_cxx_parameter_declaration_clause (this, t);

      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t))
	{
	  padding = pp_before;
	  pp_cxx_cv_qualifier_seq (this, pp_cxx_implicit_parameter_type (t));
	}

      pp_cxx_exception_specification (this, TREE_TYPE (t));
      break;

    case TYPENAME_TYPE:
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
    case TEMPLATE_TEMPLATE_PARM:
      break;

    default:
      c_pretty_printer::direct_declarator (t);
      break;
    }
}

/* declarator:
   direct-declarator
   ptr-operator declarator  */

void
cxx_pretty_printer::declarator (tree t)
{
  direct_declarator (t);

  // Print a requires clause.
  if (flag_concepts)
    if (tree ci = get_constraints (t))
      if (tree reqs = CI_DECLARATOR_REQS (ci))
        pp_cxx_requires_clause (this, reqs);
}

/* ctor-initializer:
      : mem-initializer-list

   mem-initializer-list:
      mem-initializer
      mem-initializer , mem-initializer-list

   mem-initializer:
      mem-initializer-id ( expression-list(opt) )

   mem-initializer-id:
      ::(opt) nested-name-specifier(opt) class-name
      identifier   */

static void
pp_cxx_ctor_initializer (cxx_pretty_printer *pp, tree t)
{
  t = TREE_OPERAND (t, 0);
  pp_cxx_whitespace (pp);
  pp_colon (pp);
  pp_cxx_whitespace (pp);
  for (; t; t = TREE_CHAIN (t))
    {
      tree purpose = TREE_PURPOSE (t);
      bool is_pack = PACK_EXPANSION_P (purpose);

      if (is_pack)
	pp->primary_expression (PACK_EXPANSION_PATTERN (purpose));
      else
	pp->primary_expression (purpose);
      pp_cxx_call_argument_list (pp, TREE_VALUE (t));
      if (is_pack)
	pp_cxx_ws_string (pp, "...");
      if (TREE_CHAIN (t))
	pp_cxx_separate_with (pp, ',');
    }
}

/* function-definition:
      decl-specifier-seq(opt) declarator ctor-initializer(opt) function-body
      decl-specifier-seq(opt) declarator function-try-block  */

static void
pp_cxx_function_definition (cxx_pretty_printer *pp, tree t)
{
  tree saved_scope = pp->enclosing_scope;
  pp->declaration_specifiers (t);
  pp->declarator (t);
  pp_needs_newline (pp) = true;
  pp->enclosing_scope = DECL_CONTEXT (t);
  if (DECL_SAVED_TREE (t))
    pp->statement (DECL_SAVED_TREE (t));
  else
    pp_cxx_semicolon (pp);
  pp_newline_and_flush (pp);
  pp->enclosing_scope = saved_scope;
}

/* abstract-declarator:
      ptr-operator abstract-declarator(opt)
      direct-abstract-declarator  */

void
cxx_pretty_printer::abstract_declarator (tree t)
{
  if (TYPE_PTRMEM_P (t))
    pp_cxx_right_paren (this);
  else if (POINTER_TYPE_P (t))
    {
      if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
	  || TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
	pp_cxx_right_paren (this);
      t = TREE_TYPE (t);
    }
  direct_abstract_declarator (t);
}

/* direct-abstract-declarator:
      direct-abstract-declarator(opt) ( parameter-declaration-clause )
			   cv-qualifier-seq(opt) exception-specification(opt)
      direct-abstract-declarator(opt) [ constant-expression(opt) ]
      ( abstract-declarator )  */

void
cxx_pretty_printer::direct_abstract_declarator (tree t)
{
  switch (TREE_CODE (t))
    {
    case REFERENCE_TYPE:
      abstract_declarator (t);
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
	direct_abstract_declarator (TYPE_PTRMEMFUNC_FN_TYPE (t));
      break;

    case METHOD_TYPE:
    case FUNCTION_TYPE:
      pp_cxx_parameter_declaration_clause (this, t);
      direct_abstract_declarator (TREE_TYPE (t));
      if (TREE_CODE (t) == METHOD_TYPE)
	{
	  padding = pp_before;
	  pp_cxx_cv_qualifier_seq (this, class_of_this_parm (t));
	}
      pp_cxx_exception_specification (this, t);
      break;

    case TYPENAME_TYPE:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
    case UNBOUND_CLASS_TEMPLATE:
      break;

    default:
      c_pretty_printer::direct_abstract_declarator (t);
      break;
    }
}

/* type-id:
     type-specifier-seq abstract-declarator(opt) */

void
cxx_pretty_printer::type_id (tree t)
{
  pp_flags saved_flags = flags;
  flags |= pp_c_flag_abstract;

  switch (TREE_CODE (t))
    {
    case TYPE_DECL:
    case UNION_TYPE:
    case RECORD_TYPE:
    case ENUMERAL_TYPE:
    case TYPENAME_TYPE:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
    case UNBOUND_CLASS_TEMPLATE:
    case TEMPLATE_TEMPLATE_PARM:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
    case TEMPLATE_DECL:
    case TYPEOF_TYPE:
    case UNDERLYING_TYPE:
    case DECLTYPE_TYPE:
    case TEMPLATE_ID_EXPR:
      pp_cxx_type_specifier_seq (this, t);
      break;

    case TYPE_PACK_EXPANSION:
      type_id (PACK_EXPANSION_PATTERN (t));
      pp_cxx_ws_string (this, "...");
      break;

    default:
      c_pretty_printer::type_id (t);
      break;
    }

  flags = saved_flags;
}

/* template-argument-list:
      template-argument ...(opt)
      template-argument-list, template-argument ...(opt)

   template-argument:
      assignment-expression
      type-id
      template-name  */

static void
pp_cxx_template_argument_list (cxx_pretty_printer *pp, tree t)
{
  int i;
  bool need_comma = false;

  if (t == NULL)
    return;
  for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
    {
      tree arg = TREE_VEC_ELT (t, i);
      tree argpack = NULL_TREE;
      int idx, len = 1;

      if (ARGUMENT_PACK_P (arg))
	{
	  argpack = ARGUMENT_PACK_ARGS (arg);
	  len = TREE_VEC_LENGTH (argpack);
	}

      for (idx = 0; idx < len; idx++)
	{
	  if (argpack)
	    arg = TREE_VEC_ELT (argpack, idx);
	  
	  if (need_comma)
	    pp_cxx_separate_with (pp, ',');
	  else
	    need_comma = true;

	  if (TYPE_P (arg) || (TREE_CODE (arg) == TEMPLATE_DECL
			       && TYPE_P (DECL_TEMPLATE_RESULT (arg))))
	    pp->type_id (arg);
	  else
	    pp->expression (arg);
	}
    }
}


static void
pp_cxx_exception_declaration (cxx_pretty_printer *pp, tree t)
{
  t = DECL_EXPR_DECL (t);
  pp_cxx_type_specifier_seq (pp, t);
  if (TYPE_P (t))
    pp->abstract_declarator (t);
  else
    pp->declarator (t);
}

/* Statements.  */

void
cxx_pretty_printer::statement (tree t)
{
  switch (TREE_CODE (t))
    {
    case CTOR_INITIALIZER:
      pp_cxx_ctor_initializer (this, t);
      break;

    case USING_STMT:
      pp_cxx_ws_string (this, "using");
      pp_cxx_ws_string (this, "namespace");
      if (DECL_CONTEXT (t))
	pp_cxx_nested_name_specifier (this, DECL_CONTEXT (t));
      pp_cxx_qualified_id (this, USING_STMT_NAMESPACE (t));
      break;

    case USING_DECL:
      pp_cxx_ws_string (this, "using");
      pp_cxx_nested_name_specifier (this, USING_DECL_SCOPE (t));
      pp_cxx_unqualified_id (this, DECL_NAME (t));
      break;

    case EH_SPEC_BLOCK:
      break;

      /* try-block:
	    try compound-statement handler-seq  */
    case TRY_BLOCK:
      pp_maybe_newline_and_indent (this, 0);
      pp_cxx_ws_string (this, "try");
      pp_newline_and_indent (this, 3);
      statement (TRY_STMTS (t));
      pp_newline_and_indent (this, -3);
      if (CLEANUP_P (t))
	;
      else
	statement (TRY_HANDLERS (t));
      break;

      /*
	 handler-seq:
	    handler handler-seq(opt)

	 handler:
	 catch ( exception-declaration ) compound-statement

	 exception-declaration:
	    type-specifier-seq declarator
	    type-specifier-seq abstract-declarator
	    ...   */
    case HANDLER:
      pp_cxx_ws_string (this, "catch");
      pp_cxx_left_paren (this);
      pp_cxx_exception_declaration (this, HANDLER_PARMS (t));
      pp_cxx_right_paren (this);
      pp_indentation (this) += 3;
      pp_needs_newline (this) = true;
      statement (HANDLER_BODY (t));
      pp_indentation (this) -= 3;
      pp_needs_newline (this) = true;
      break;

      /* selection-statement:
	    if ( expression ) statement
	    if ( expression ) statement else statement  */
    case IF_STMT:
      pp_cxx_ws_string (this, "if");
      pp_cxx_whitespace (this);
      pp_cxx_left_paren (this);
      expression (IF_COND (t));
      pp_cxx_right_paren (this);
      pp_newline_and_indent (this, 2);
      statement (THEN_CLAUSE (t));
      pp_newline_and_indent (this, -2);
      if (ELSE_CLAUSE (t))
	{
	  tree else_clause = ELSE_CLAUSE (t);
	  pp_cxx_ws_string (this, "else");
	  if (TREE_CODE (else_clause) == IF_STMT)
	    pp_cxx_whitespace (this);
	  else
	    pp_newline_and_indent (this, 2);
	  statement (else_clause);
	  if (TREE_CODE (else_clause) != IF_STMT)
	    pp_newline_and_indent (this, -2);
	}
      break;

    case SWITCH_STMT:
      pp_cxx_ws_string (this, "switch");
      pp_space (this);
      pp_cxx_left_paren (this);
      expression (SWITCH_STMT_COND (t));
      pp_cxx_right_paren (this);
      pp_indentation (this) += 3;
      pp_needs_newline (this) = true;
      statement (SWITCH_STMT_BODY (t));
      pp_newline_and_indent (this, -3);
      break;

      /* iteration-statement:
	    while ( expression ) statement
	    do statement while ( expression ) ;
	    for ( expression(opt) ; expression(opt) ; expression(opt) ) statement
	    for ( declaration expression(opt) ; expression(opt) ) statement  */
    case WHILE_STMT:
      pp_cxx_ws_string (this, "while");
      pp_space (this);
      pp_cxx_left_paren (this);
      expression (WHILE_COND (t));
      pp_cxx_right_paren (this);
      pp_newline_and_indent (this, 3);
      statement (WHILE_BODY (t));
      pp_indentation (this) -= 3;
      pp_needs_newline (this) = true;
      break;

    case DO_STMT:
      pp_cxx_ws_string (this, "do");
      pp_newline_and_indent (this, 3);
      statement (DO_BODY (t));
      pp_newline_and_indent (this, -3);
      pp_cxx_ws_string (this, "while");
      pp_space (this);
      pp_cxx_left_paren (this);
      expression (DO_COND (t));
      pp_cxx_right_paren (this);
      pp_cxx_semicolon (this);
      pp_needs_newline (this) = true;
      break;

    case FOR_STMT:
      pp_cxx_ws_string (this, "for");
      pp_space (this);
      pp_cxx_left_paren (this);
      if (FOR_INIT_STMT (t))
	statement (FOR_INIT_STMT (t));
      else
	pp_cxx_semicolon (this);
      pp_needs_newline (this) = false;
      pp_cxx_whitespace (this);
      if (FOR_COND (t))
	expression (FOR_COND (t));
      pp_cxx_semicolon (this);
      pp_needs_newline (this) = false;
      pp_cxx_whitespace (this);
      if (FOR_EXPR (t))
	expression (FOR_EXPR (t));
      pp_cxx_right_paren (this);
      pp_newline_and_indent (this, 3);
      statement (FOR_BODY (t));
      pp_indentation (this) -= 3;
      pp_needs_newline (this) = true;
      break;

    case RANGE_FOR_STMT:
      pp_cxx_ws_string (this, "for");
      pp_space (this);
      pp_cxx_left_paren (this);
      statement (RANGE_FOR_DECL (t));
      pp_space (this);
      pp_needs_newline (this) = false;
      pp_colon (this);
      pp_space (this);
      statement (RANGE_FOR_EXPR (t));
      pp_cxx_right_paren (this);
      pp_newline_and_indent (this, 3);
      statement (FOR_BODY (t));
      pp_indentation (this) -= 3;
      pp_needs_newline (this) = true;
      break;

      /* jump-statement:
	    goto identifier;
	    continue ;
	    return expression(opt) ;  */
    case BREAK_STMT:
    case CONTINUE_STMT:
      pp_string (this, TREE_CODE (t) == BREAK_STMT ? "break" : "continue");
      pp_cxx_semicolon (this);
      pp_needs_newline (this) = true;
      break;

      /* expression-statement:
	    expression(opt) ;  */
    case EXPR_STMT:
      expression (EXPR_STMT_EXPR (t));
      pp_cxx_semicolon (this);
      pp_needs_newline (this) = true;
      break;

    case CLEANUP_STMT:
      pp_cxx_ws_string (this, "try");
      pp_newline_and_indent (this, 2);
      statement (CLEANUP_BODY (t));
      pp_newline_and_indent (this, -2);
      pp_cxx_ws_string (this, CLEANUP_EH_ONLY (t) ? "catch" : "finally");
      pp_newline_and_indent (this, 2);
      statement (CLEANUP_EXPR (t));
      pp_newline_and_indent (this, -2);
      break;

    case STATIC_ASSERT:
      declaration (t);
      break;

    default:
      c_pretty_printer::statement (t);
      break;
    }
}

/* original-namespace-definition:
      namespace identifier { namespace-body }

  As an edge case, we also handle unnamed namespace definition here.  */

static void
pp_cxx_original_namespace_definition (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "namespace");
  if (DECL_CONTEXT (t))
    pp_cxx_nested_name_specifier (pp, DECL_CONTEXT (t));
  if (DECL_NAME (t))
    pp_cxx_unqualified_id (pp, t);
  pp_cxx_whitespace (pp);
  pp_cxx_left_brace (pp);
  /* We do not print the namespace-body.  */
  pp_cxx_whitespace (pp);
  pp_cxx_right_brace (pp);
}

/* namespace-alias:
      identifier

   namespace-alias-definition:
      namespace identifier = qualified-namespace-specifier ;

   qualified-namespace-specifier:
      ::(opt) nested-name-specifier(opt) namespace-name   */

static void
pp_cxx_namespace_alias_definition (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "namespace");
  if (DECL_CONTEXT (t))
    pp_cxx_nested_name_specifier (pp, DECL_CONTEXT (t));
  pp_cxx_unqualified_id (pp, t);
  pp_cxx_whitespace (pp);
  pp_equal (pp);
  pp_cxx_whitespace (pp);
  if (DECL_CONTEXT (DECL_NAMESPACE_ALIAS (t)))
    pp_cxx_nested_name_specifier (pp,
				  DECL_CONTEXT (DECL_NAMESPACE_ALIAS (t)));
  pp_cxx_qualified_id (pp, DECL_NAMESPACE_ALIAS (t));
  pp_cxx_semicolon (pp);
}

/* simple-declaration:
      decl-specifier-seq(opt) init-declarator-list(opt)  */

static void
pp_cxx_simple_declaration (cxx_pretty_printer *pp, tree t)
{
  pp->declaration_specifiers (t);
  pp_cxx_init_declarator (pp, t);
  pp_cxx_semicolon (pp);
  pp_needs_newline (pp) = true;
}

/*
  template-parameter-list:
     template-parameter
     template-parameter-list , template-parameter  */

static inline void
pp_cxx_template_parameter_list (cxx_pretty_printer *pp, tree t)
{
  const int n = TREE_VEC_LENGTH (t);
  int i;
  for (i = 0; i < n; ++i)
    {
      if (i)
	pp_cxx_separate_with (pp, ',');
      pp_cxx_template_parameter (pp, TREE_VEC_ELT (t, i));
    }
}

/* template-parameter:
      type-parameter
      parameter-declaration

   type-parameter:
     class ...(opt) identifier(opt)
     class identifier(opt) = type-id
     typename identifier(opt)
     typename ...(opt) identifier(opt) = type-id
     template < template-parameter-list > class ...(opt) identifier(opt)
     template < template-parameter-list > class identifier(opt) = template-name  */

static void
pp_cxx_template_parameter (cxx_pretty_printer *pp, tree t)
{
  tree parameter =  TREE_VALUE (t);
  switch (TREE_CODE (parameter))
    {
    case TYPE_DECL:
      pp_cxx_ws_string (pp, "class");
      if (TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (t)))
	pp_cxx_ws_string (pp, "...");
      if (DECL_NAME (parameter))
	pp_cxx_tree_identifier (pp, DECL_NAME (parameter));
      /* FIXME: Check if we should print also default argument.  */
      break;

    case PARM_DECL:
      pp_cxx_parameter_declaration (pp, parameter);
      break;

    case TEMPLATE_DECL:
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

/* Pretty-print a template parameter in the canonical form
   "template-parameter-<level>-<position in parameter list>".  */

void
pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm)
{
  const enum tree_code code = TREE_CODE (parm);

  /* Brings type template parameters to the canonical forms.  */
  if (code == TEMPLATE_TYPE_PARM || code == TEMPLATE_TEMPLATE_PARM
      || code == BOUND_TEMPLATE_TEMPLATE_PARM)
    parm = TEMPLATE_TYPE_PARM_INDEX (parm);

  pp_cxx_begin_template_argument_list (pp);
  pp->translate_string ("template-parameter-");
  pp_wide_integer (pp, TEMPLATE_PARM_LEVEL (parm));
  pp_minus (pp);
  pp_wide_integer (pp, TEMPLATE_PARM_IDX (parm) + 1);
  pp_cxx_end_template_argument_list (pp);
}

/* Print a constrained-type-specifier.  */

void
pp_cxx_constrained_type_spec (cxx_pretty_printer *pp, tree c)
{
  tree t, a;
  if (c == error_mark_node)
    {
      pp_cxx_ws_string(pp, "<unsatisfied-constrained-placeholder>");
      return;
    }
  placeholder_extract_concept_and_args (c, t, a);
  pp->id_expression (t);
  if (TREE_VEC_LENGTH (a) > 1)
    {
      pp_cxx_begin_template_argument_list (pp);
      tree args = make_tree_vec (TREE_VEC_LENGTH (a) - 1);
      for (int i = TREE_VEC_LENGTH (a) - 1; i > 0; --i)
	TREE_VEC_ELT (args, i-1) = TREE_VEC_ELT (a, i);
      pp_cxx_template_argument_list (pp, args);
      ggc_free (args);
      pp_cxx_end_template_argument_list (pp);
    }
}

/*
  template-declaration:
     export(opt) template < template-parameter-list > declaration

  Concept extensions:

  template-declaration:
     export(opt) template < template-parameter-list >
       requires-clause(opt) declaration */

static void
pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
{
  tree tmpl = most_general_template (t);
  tree level;

  pp_maybe_newline_and_indent (pp, 0);
  for (level = DECL_TEMPLATE_PARMS (tmpl); level; level = TREE_CHAIN (level))
    {
      pp_cxx_ws_string (pp, "template");
      pp_cxx_begin_template_argument_list (pp);
      pp_cxx_template_parameter_list (pp, TREE_VALUE (level));
      pp_cxx_end_template_argument_list (pp);
      pp_newline_and_indent (pp, 3);
    }

  if (flag_concepts)
    if (tree ci = get_constraints (t))
      if (tree reqs = CI_TEMPLATE_REQS (ci))
         {
            pp_cxx_requires_clause (pp, reqs);
            pp_newline_and_indent (pp, 6);
         }

  if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t))
    pp_cxx_function_definition (pp, t);
  else
    pp_cxx_simple_declaration (pp, t);
}

static void
pp_cxx_explicit_specialization (cxx_pretty_printer *pp, tree t)
{
  pp_unsupported_tree (pp, t);
}

static void
pp_cxx_explicit_instantiation (cxx_pretty_printer *pp, tree t)
{
  pp_unsupported_tree (pp, t);
}

/*
    declaration:
       block-declaration
       function-definition
       template-declaration
       explicit-instantiation
       explicit-specialization
       linkage-specification
       namespace-definition

    block-declaration:
       simple-declaration
       asm-definition
       namespace-alias-definition
       using-declaration
       using-directive
       static_assert-declaration */
void
cxx_pretty_printer::declaration (tree t)
{
  if (TREE_CODE (t) == STATIC_ASSERT)
    {
      pp_cxx_ws_string (this, "static_assert");
      pp_cxx_left_paren (this);
      expression (STATIC_ASSERT_CONDITION (t));
      pp_cxx_separate_with (this, ',');
      expression (STATIC_ASSERT_MESSAGE (t));
      pp_cxx_right_paren (this);
    }
  else if (!DECL_LANG_SPECIFIC (t))
    pp_cxx_simple_declaration (this, t);
  else if (DECL_USE_TEMPLATE (t))
    switch (DECL_USE_TEMPLATE (t))
      {
      case 1:
	pp_cxx_template_declaration (this, t);
	break;

      case 2:
	pp_cxx_explicit_specialization (this, t);
	break;

      case 3:
	pp_cxx_explicit_instantiation (this, t);
	break;

      default:
	break;
      }
  else switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case TYPE_DECL:
      pp_cxx_simple_declaration (this, t);
      break;

    case FUNCTION_DECL:
      if (DECL_SAVED_TREE (t))
	pp_cxx_function_definition (this, t);
      else
	pp_cxx_simple_declaration (this, t);
      break;

    case NAMESPACE_DECL:
      if (DECL_NAMESPACE_ALIAS (t))
	pp_cxx_namespace_alias_definition (this, t);
      else
	pp_cxx_original_namespace_definition (this, t);
      break;

    default:
      pp_unsupported_tree (this, t);
      break;
    }
}

static void
pp_cxx_typeid_expression (cxx_pretty_printer *pp, tree t)
{
  t = TREE_OPERAND (t, 0);
  pp_cxx_ws_string (pp, "typeid");
  pp_cxx_left_paren (pp);
  if (TYPE_P (t))
    pp->type_id (t);
  else
    pp->expression (t);
  pp_cxx_right_paren (pp);
}

void
pp_cxx_va_arg_expression (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "va_arg");
  pp_cxx_left_paren (pp);
  pp->assignment_expression (TREE_OPERAND (t, 0));
  pp_cxx_separate_with (pp, ',');
  pp->type_id (TREE_TYPE (t));
  pp_cxx_right_paren (pp);
}

static bool
pp_cxx_offsetof_expression_1 (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case ARROW_EXPR:
      if (TREE_CODE (TREE_OPERAND (t, 0)) == STATIC_CAST_EXPR
	  && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0))))
	{
	  pp->type_id (TREE_TYPE (TREE_TYPE (TREE_OPERAND (t, 0))));
	  pp_cxx_separate_with (pp, ',');
	  return true;
	}
      return false;
    case COMPONENT_REF:
      if (!pp_cxx_offsetof_expression_1 (pp, TREE_OPERAND (t, 0)))
	return false;
      if (TREE_CODE (TREE_OPERAND (t, 0)) != ARROW_EXPR)
	pp_cxx_dot (pp);
      pp->expression (TREE_OPERAND (t, 1));
      return true;
    case ARRAY_REF:
      if (!pp_cxx_offsetof_expression_1 (pp, TREE_OPERAND (t, 0)))
	return false;
      pp_left_bracket (pp);
      pp->expression (TREE_OPERAND (t, 1));
      pp_right_bracket (pp);
      return true;
    default:
      return false;
    }
}

void
pp_cxx_offsetof_expression (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "offsetof");
  pp_cxx_left_paren (pp);
  if (!pp_cxx_offsetof_expression_1 (pp, TREE_OPERAND (t, 0)))
    pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_right_paren (pp);
}

void
pp_cxx_addressof_expression (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "__builtin_addressof");
  pp_cxx_left_paren (pp);
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_right_paren (pp);
}

static char const*
get_fold_operator (tree t)
{
  int op = int_cst_value (FOLD_EXPR_OP (t));
  if (FOLD_EXPR_MODIFY_P (t))
    {
      switch (op)
        {
        case NOP_EXPR: return "=";
        case PLUS_EXPR: return "+=";
        case MINUS_EXPR: return "-=";
        case MULT_EXPR: return "*=";
        case TRUNC_DIV_EXPR: return "/=";
        case TRUNC_MOD_EXPR: return "%=";
        case BIT_XOR_EXPR: return "^=";
        case BIT_AND_EXPR: return "&=";
        case BIT_IOR_EXPR: return "|=";
        case LSHIFT_EXPR: return "<<=";
        case RSHIFT_EXPR: return ">>=";
        default: gcc_unreachable ();
        }
    }
  else
    {
      switch (op)
        {
        case PLUS_EXPR: return "+";
        case MINUS_EXPR: return "-";
        case MULT_EXPR: return "*";
        case TRUNC_DIV_EXPR: return "/";
        case TRUNC_MOD_EXPR: return "%";
        case BIT_XOR_EXPR: return "^";
        case BIT_AND_EXPR: return "&";
        case BIT_IOR_EXPR: return "|";
        case LSHIFT_EXPR: return "<<";
        case RSHIFT_EXPR: return ">>";
        case EQ_EXPR: return "==";
        case NE_EXPR: return "!=";
        case LT_EXPR: return "<";
        case GT_EXPR: return ">";
        case LE_EXPR: return "<=";
        case GE_EXPR: return ">=";
        case TRUTH_ANDIF_EXPR: return "&&";
        case TRUTH_ORIF_EXPR: return "||";
        case MEMBER_REF: return "->*";
        case DOTSTAR_EXPR: return ".*";
        case OFFSET_REF: return ".*";
        default: return ","; /* FIXME: Not the right default.  */
        }
    }
}

void
pp_cxx_unary_left_fold_expression (cxx_pretty_printer *pp, tree t)
{
  char const* op = get_fold_operator (t);
  tree expr = PACK_EXPANSION_PATTERN (FOLD_EXPR_PACK (t));
  pp_cxx_left_paren (pp);
  pp_cxx_ws_string (pp, "...");
  pp_cxx_ws_string (pp, op);
  pp->expression (expr);
  pp_cxx_right_paren (pp);
}

void
pp_cxx_unary_right_fold_expression (cxx_pretty_printer *pp, tree t)
{
  char const* op = get_fold_operator (t);
  tree expr = PACK_EXPANSION_PATTERN (FOLD_EXPR_PACK (t));
  pp_cxx_left_paren (pp);
  pp->expression (expr);
  pp_space (pp);
  pp_cxx_ws_string (pp, op);
  pp_cxx_ws_string (pp, "...");
  pp_cxx_right_paren (pp);
}

void
pp_cxx_binary_fold_expression (cxx_pretty_printer *pp, tree t)
{
  char const* op = get_fold_operator (t);
  tree t1 = TREE_OPERAND (t, 1);
  tree t2 = TREE_OPERAND (t, 2);
  if (t1 == FOLD_EXPR_PACK (t))
    t1 = PACK_EXPANSION_PATTERN (t1);
  else
    t2 = PACK_EXPANSION_PATTERN (t2);
  pp_cxx_left_paren (pp);
  pp->expression (t1);
  pp_cxx_ws_string (pp, op);
  pp_cxx_ws_string (pp, "...");
  pp_cxx_ws_string (pp, op);
  pp->expression (t2);
  pp_cxx_right_paren (pp);
}

void
pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
{
  cp_trait_kind kind = TRAIT_EXPR_KIND (t);

  switch (kind)
    {
    case CPTK_HAS_NOTHROW_ASSIGN:
      pp_cxx_ws_string (pp, "__has_nothrow_assign");
      break;
    case CPTK_HAS_TRIVIAL_ASSIGN:
      pp_cxx_ws_string (pp, "__has_trivial_assign");
      break;
    case CPTK_HAS_NOTHROW_CONSTRUCTOR:
      pp_cxx_ws_string (pp, "__has_nothrow_constructor");
      break;
    case CPTK_HAS_TRIVIAL_CONSTRUCTOR:
      pp_cxx_ws_string (pp, "__has_trivial_constructor");
      break;
    case CPTK_HAS_NOTHROW_COPY:
      pp_cxx_ws_string (pp, "__has_nothrow_copy");
      break;
    case CPTK_HAS_TRIVIAL_COPY:
      pp_cxx_ws_string (pp, "__has_trivial_copy");
      break;
    case CPTK_HAS_TRIVIAL_DESTRUCTOR:
      pp_cxx_ws_string (pp, "__has_trivial_destructor");
      break;
    case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
      pp_cxx_ws_string (pp, "__has_unique_object_representations");
      break;
    case CPTK_HAS_VIRTUAL_DESTRUCTOR:
      pp_cxx_ws_string (pp, "__has_virtual_destructor");
      break;
    case CPTK_IS_ABSTRACT:
      pp_cxx_ws_string (pp, "__is_abstract");
      break;
    case CPTK_IS_AGGREGATE:
      pp_cxx_ws_string (pp, "__is_aggregate");
      break;
    case CPTK_IS_BASE_OF:
      pp_cxx_ws_string (pp, "__is_base_of");
      break;
    case CPTK_IS_CLASS:
      pp_cxx_ws_string (pp, "__is_class");
      break;
    case CPTK_IS_EMPTY:
      pp_cxx_ws_string (pp, "__is_empty");
      break;
    case CPTK_IS_ENUM:
      pp_cxx_ws_string (pp, "__is_enum");
      break;
    case CPTK_IS_FINAL:
      pp_cxx_ws_string (pp, "__is_final");
      break;
    case CPTK_IS_POD:
      pp_cxx_ws_string (pp, "__is_pod");
      break;
    case CPTK_IS_POLYMORPHIC:
      pp_cxx_ws_string (pp, "__is_polymorphic");
      break;
    case CPTK_IS_SAME_AS:
      pp_cxx_ws_string (pp, "__is_same_as");
      break;
    case CPTK_IS_STD_LAYOUT:
      pp_cxx_ws_string (pp, "__is_std_layout");
      break;
    case CPTK_IS_TRIVIAL:
      pp_cxx_ws_string (pp, "__is_trivial");
      break;
    case CPTK_IS_TRIVIALLY_ASSIGNABLE:
      pp_cxx_ws_string (pp, "__is_trivially_assignable");
      break;
    case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
      pp_cxx_ws_string (pp, "__is_trivially_constructible");
      break;
    case CPTK_IS_TRIVIALLY_COPYABLE:
      pp_cxx_ws_string (pp, "__is_trivially_copyable");
      break;
    case CPTK_IS_UNION:
      pp_cxx_ws_string (pp, "__is_union");
      break;
    case CPTK_IS_LITERAL_TYPE:
      pp_cxx_ws_string (pp, "__is_literal_type");
      break;

    default:
      gcc_unreachable ();
    }

  pp_cxx_left_paren (pp);
  pp->type_id (TRAIT_EXPR_TYPE1 (t));

  if (kind == CPTK_IS_BASE_OF || kind == CPTK_IS_SAME_AS)
    {
      pp_cxx_separate_with (pp, ',');
      pp->type_id (TRAIT_EXPR_TYPE2 (t));
    }

  pp_cxx_right_paren (pp);
}

// requires-clause:
//    'requires' logical-or-expression
void
pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t)
{
  if (!t)
    return;
  pp->padding = pp_before;
  pp_cxx_ws_string (pp, "requires");
  pp_space (pp);
  pp->expression (t);
}

/* requirement:
     simple-requirement
     compound-requirement
     type-requirement
     nested-requirement */
static void
pp_cxx_requirement (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case SIMPLE_REQ:
      pp_cxx_simple_requirement (pp, t);
      break;

    case TYPE_REQ:
      pp_cxx_type_requirement (pp, t);
      break;

    case COMPOUND_REQ:
      pp_cxx_compound_requirement (pp, t);
      break;

    case NESTED_REQ:
      pp_cxx_nested_requirement (pp, t);
      break;

    default:
      gcc_unreachable ();
    }
}

// requirement-list:
//    requirement
//    requirement-list ';' requirement[opt]
//
static void
pp_cxx_requirement_list (cxx_pretty_printer *pp, tree t)
{
  for (; t; t = TREE_CHAIN (t))
    pp_cxx_requirement (pp, TREE_VALUE (t));
}

// requirement-body:
//    '{' requirement-list '}'
static void
pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_left_brace (pp);
  pp_cxx_requirement_list (pp, t);
  pp_cxx_right_brace (pp);
}

// requires-expression:
//    'requires' requirement-parameter-list requirement-body
void
pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t)
{
  pp_string (pp, "requires");
  if (tree parms = TREE_OPERAND (t, 0))
    {
      pp_cxx_parameter_declaration_clause (pp, parms);
      pp_cxx_whitespace (pp);
    }
  pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1));
}

/* simple-requirement:
     expression ';' */
void
pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t)
{
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_semicolon (pp);
}

/* type-requirement:
     typename type-name ';' */
void
pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t)
{
  pp->type_id (TREE_OPERAND (t, 0));
  pp_cxx_semicolon (pp);
}

/* compound-requirement:
     '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] */
void
pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_left_brace (pp);
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_right_brace (pp);

  if (COMPOUND_REQ_NOEXCEPT_P (t))
    pp_cxx_ws_string (pp, "noexcept");

  if (tree type = TREE_OPERAND (t, 1))
    {
      pp_cxx_ws_string (pp, "->");
      pp->type_id (type);
    }
  pp_cxx_semicolon (pp);
}

/* nested requirement:
     'requires' constraint-expression */
void
pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "requires");
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_semicolon (pp);
}

void
pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t)
{
  pp->expression (TREE_OPERAND (t, 0));
}

void
pp_cxx_check_constraint (cxx_pretty_printer *pp, tree t)
{
  tree decl = CHECK_CONSTR_CONCEPT (t);
  tree tmpl = DECL_TI_TEMPLATE (decl);
  tree args = CHECK_CONSTR_ARGS (t);
  tree id = build_nt (TEMPLATE_ID_EXPR, tmpl, args);

  if (VAR_P (decl))
    pp->expression (id);
  else if (TREE_CODE (decl) == FUNCTION_DECL)
    {
      tree call = build_vl_exp (CALL_EXPR, 2);
      TREE_OPERAND (call, 0) = integer_two_node;
      TREE_OPERAND (call, 1) = id;
      pp->expression (call);
    }
  else
    gcc_unreachable ();
}

void
pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_string (pp, "<valid-expression ");
  pp_cxx_left_paren (pp);
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_right_paren (pp);
  pp_string (pp, ">");
}

void
pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_string (pp, "<valid-type ");
  pp->type_id (TREE_OPERAND (t, 0));
  pp_string (pp, ">");
}

void
pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_string (pp, "<implicitly-conversion ");
  pp_cxx_left_paren (pp);
  pp->expression (ICONV_CONSTR_EXPR (t));
  pp_cxx_right_paren (pp);
  pp_cxx_ws_string (pp, "to");
  pp->type_id (ICONV_CONSTR_TYPE (t));
  pp_string (pp, ">");
}

void
pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_string (pp, "<argument-deduction ");
  pp_cxx_left_paren (pp);
  pp->expression (DEDUCT_CONSTR_EXPR (t));
  pp_cxx_right_paren (pp);
  pp_cxx_ws_string (pp, "as");
  pp->expression (DEDUCT_CONSTR_PATTERN (t));
  pp_string (pp, ">");
}

void
pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_ws_string (pp, "noexcept");
  pp_cxx_whitespace (pp);
  pp_cxx_left_paren (pp);
  pp->expression (TREE_OPERAND (t, 0));
  pp_cxx_right_paren (pp);
}

void
pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t)
{
  pp_left_paren (pp);
  pp_string (pp, "<requires ");
  if (tree parms = PARM_CONSTR_PARMS (t))
    {
	pp_cxx_parameter_declaration_clause (pp, parms);
      pp_cxx_whitespace (pp);
    }
  pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t));
  pp_string (pp, ">");
}

void
pp_cxx_conjunction (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
  pp_string (pp, " and ");
  pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
}

void
pp_cxx_disjunction (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
  pp_string (pp, " or ");
  pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
}

void
pp_cxx_constraint (cxx_pretty_printer *pp, tree t)
{
  if (t == error_mark_node)
    return pp->expression (t);

  switch (TREE_CODE (t))
    {
    case PRED_CONSTR:
      pp_cxx_predicate_constraint (pp, t);
      break;

    case CHECK_CONSTR:
      pp_cxx_check_constraint (pp, t);
      break;

    case EXPR_CONSTR:
      pp_cxx_expression_constraint (pp, t);
      break;

    case TYPE_CONSTR:
      pp_cxx_type_constraint (pp, t);
      break;

    case ICONV_CONSTR:
      pp_cxx_implicit_conversion_constraint (pp, t);
      break;

    case DEDUCT_CONSTR:
      pp_cxx_argument_deduction_constraint (pp, t);
      break;

    case EXCEPT_CONSTR:
      pp_cxx_exception_constraint (pp, t);
      break;

    case PARM_CONSTR:
      pp_cxx_parameterized_constraint (pp, t);
      break;

    case CONJ_CONSTR:
      pp_cxx_conjunction (pp, t);
      break;

    case DISJ_CONSTR:
      pp_cxx_disjunction (pp, t);
      break;

    case EXPR_PACK_EXPANSION:
      pp->expression (TREE_OPERAND (t, 0));
      break;

    default:
      gcc_unreachable ();
    }
}



typedef c_pretty_print_fn pp_fun;

/* Initialization of a C++ pretty-printer object.  */

cxx_pretty_printer::cxx_pretty_printer ()
  : c_pretty_printer (),
    enclosing_scope (global_namespace)
{
  type_specifier_seq = (pp_fun) pp_cxx_type_specifier_seq;
  parameter_list = (pp_fun) pp_cxx_parameter_declaration_clause;
}
