/* Type stack for GDB parser.

   Copyright (C) 1986-2022 Free Software Foundation, Inc.

   This file is part of GDB.

   This program 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 of the License, or
   (at your option) any later version.

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

#include "defs.h"
#include "type-stack.h"

#include "gdbtypes.h"
#include "parser-defs.h"

/* See type-stack.h.  */

void
type_stack::insert (enum type_pieces tp)
{
  union type_stack_elt element;
  int slot;

  gdb_assert (tp == tp_pointer || tp == tp_reference
	      || tp == tp_rvalue_reference || tp == tp_const
	      || tp == tp_volatile || tp == tp_restrict
	      || tp == tp_atomic);

  /* If there is anything on the stack (we know it will be a
     tp_pointer), insert the qualifier above it.  Otherwise, simply
     push this on the top of the stack.  */
  if (!m_elements.empty () && (tp == tp_const || tp == tp_volatile
			       || tp == tp_restrict))
    slot = 1;
  else
    slot = 0;

  element.piece = tp;
  insert_into (slot, element);
}

/* See type-stack.h.  */

void
type_stack::insert (struct expr_builder *pstate, const char *string)
{
  union type_stack_elt element;
  int slot;

  /* If there is anything on the stack (we know it will be a
     tp_pointer), insert the address space qualifier above it.
     Otherwise, simply push this on the top of the stack.  */
  if (!m_elements.empty ())
    slot = 1;
  else
    slot = 0;

  element.piece = tp_space_identifier;
  insert_into (slot, element);
  element.int_val
    = address_space_name_to_type_instance_flags (pstate->gdbarch (),
						 string);
  insert_into (slot, element);
}

/* See type-stack.h.  */

type_instance_flags
type_stack::follow_type_instance_flags ()
{
  type_instance_flags flags = 0;

  for (;;)
    switch (pop ())
      {
      case tp_end:
	return flags;
      case tp_const:
	flags |= TYPE_INSTANCE_FLAG_CONST;
	break;
      case tp_volatile:
	flags |= TYPE_INSTANCE_FLAG_VOLATILE;
	break;
      case tp_atomic:
	flags |= TYPE_INSTANCE_FLAG_ATOMIC;
	break;
      case tp_restrict:
	flags |= TYPE_INSTANCE_FLAG_RESTRICT;
	break;
      default:
	gdb_assert_not_reached ("unrecognized tp_ value in follow_types");
      }
}

/* See type-stack.h.  */

struct type *
type_stack::follow_types (struct type *follow_type)
{
  int done = 0;
  int make_const = 0;
  int make_volatile = 0;
  type_instance_flags make_addr_space = 0;
  bool make_restrict = false;
  bool make_atomic = false;
  int array_size;

  while (!done)
    switch (pop ())
      {
      case tp_end:
	done = 1;
	goto process_qualifiers;
	break;
      case tp_const:
	make_const = 1;
	break;
      case tp_volatile:
	make_volatile = 1;
	break;
      case tp_space_identifier:
	make_addr_space = (enum type_instance_flag_value) pop_int ();
	break;
      case tp_atomic:
	make_atomic = true;
	break;
      case tp_restrict:
	make_restrict = true;
	break;
      case tp_pointer:
	follow_type = lookup_pointer_type (follow_type);
	goto process_qualifiers;
      case tp_reference:
	follow_type = lookup_lvalue_reference_type (follow_type);
	goto process_qualifiers;
      case tp_rvalue_reference:
	follow_type = lookup_rvalue_reference_type (follow_type);
      process_qualifiers:
	if (make_const)
	  follow_type = make_cv_type (make_const,
				      TYPE_VOLATILE (follow_type),
				      follow_type, 0);
	if (make_volatile)
	  follow_type = make_cv_type (TYPE_CONST (follow_type),
				      make_volatile,
				      follow_type, 0);
	if (make_addr_space)
	  follow_type = make_type_with_address_space (follow_type,
						      make_addr_space);
	if (make_restrict)
	  follow_type = make_restrict_type (follow_type);
	if (make_atomic)
	  follow_type = make_atomic_type (follow_type);
	make_const = make_volatile = 0;
	make_addr_space = 0;
	make_restrict = make_atomic = false;
	break;
      case tp_array:
	array_size = pop_int ();
	/* FIXME-type-allocation: need a way to free this type when we are
	   done with it.  */
	follow_type =
	  lookup_array_range_type (follow_type,
				   0, array_size >= 0 ? array_size - 1 : 0);
	if (array_size < 0)
	  follow_type->bounds ()->high.set_undefined ();
	break;
      case tp_function:
	/* FIXME-type-allocation: need a way to free this type when we are
	   done with it.  */
	follow_type = lookup_function_type (follow_type);
	break;

      case tp_function_with_arguments:
	{
	  std::vector<struct type *> *args = pop_typelist ();

	  follow_type
	    = lookup_function_type_with_arguments (follow_type,
						   args->size (),
						   args->data ());
	}
	break;

      case tp_type_stack:
	{
	  struct type_stack *stack = pop_type_stack ();
	  follow_type = stack->follow_types (follow_type);
	}
	break;
      default:
	gdb_assert_not_reached ("unrecognized tp_ value in follow_types");
      }
  return follow_type;
}