using string_operation::string_operation;
 
+  /* Return the underlying string.  */
+  const char *get_name () const
+  {
+    return std::get<0> (m_storage).c_str ();
+  }
+
   value *evaluate (struct type *expect_type,
                   struct expression *exp,
                   enum noside noside) override;
   { return OP_FUNCALL; }
 };
 
+/* An Ada assignment operation.  */
+class ada_assign_operation
+  : public assign_operation
+{
+public:
+
+  using assign_operation::assign_operation;
+
+  value *evaluate (struct type *expect_type,
+                  struct expression *exp,
+                  enum noside noside) override;
+
+  enum exp_opcode opcode () const override
+  { return BINOP_ASSIGN; }
+};
+
+/* This abstract class represents a single component in an Ada
+   aggregate assignment.  */
+class ada_component
+{
+public:
+
+  /* Assign to LHS, which is part of CONTAINER.  EXP is the expression
+     being evaluated.  INDICES, LOW, and HIGH indicate which
+     sub-components have already been assigned; INDICES should be
+     updated by this call.  */
+  virtual void assign (struct value *container,
+                      struct value *lhs, struct expression *exp,
+                      std::vector<LONGEST> &indices,
+                      LONGEST low, LONGEST high) = 0;
+
+  /* Same as operation::uses_objfile.  */
+  virtual bool uses_objfile (struct objfile *objfile) = 0;
+
+  /* Same as operation::dump.  */
+  virtual void dump (ui_file *stream, int depth) = 0;
+
+  virtual ~ada_component () = default;
+
+protected:
+
+  ada_component () = default;
+  DISABLE_COPY_AND_ASSIGN (ada_component);
+};
+
+/* Unique pointer specialization for Ada assignment components.  */
+typedef std::unique_ptr<ada_component> ada_component_up;
+
+/* An operation that holds a single component.  */
+class ada_aggregate_operation
+  : public tuple_holding_operation<ada_component_up>
+{
+public:
+
+  using tuple_holding_operation::tuple_holding_operation;
+
+  /* Assuming that LHS represents an lvalue having a record or array
+     type, evaluate an assignment of this aggregate's value to LHS.
+     CONTAINER is an lvalue containing LHS (possibly LHS itself).
+     Does not modify the inferior's memory, nor does it modify the
+     contents of LHS (unless == CONTAINER).  */
+
+  void assign_aggregate (struct value *container,
+                        struct value *lhs,
+                        struct expression *exp);
+
+  value *evaluate (struct type *expect_type,
+                  struct expression *exp,
+                  enum noside noside) override
+  {
+    error (_("Aggregates only allowed on the right of an assignment"));
+  }
+
+  enum exp_opcode opcode () const override
+  { return OP_AGGREGATE; }
+};
+
+/* A component holding a vector of other components to assign.  */
+class ada_aggregate_component : public ada_component
+{
+public:
+
+  explicit ada_aggregate_component (std::vector<ada_component_up> &&components)
+    : m_components (std::move (components))
+  {
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  std::vector<ada_component_up> m_components;
+};
+
+/* A component that assigns according to a provided index (which is
+   relative to the "low" value).  */
+class ada_positional_component : public ada_component
+{
+public:
+
+  ada_positional_component (int index, operation_up &&op)
+    : m_index (index),
+      m_op (std::move (op))
+  {
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  int m_index;
+  operation_up m_op;
+};
+
+/* A component which handles an "others" clause.  */
+class ada_others_component : public ada_component
+{
+public:
+
+  explicit ada_others_component (operation_up &&op)
+    : m_op (std::move (op))
+  {
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  operation_up m_op;
+};
+
+/* An interface that represents an association that is used in
+   aggregate assignment.  */
+class ada_association
+{
+public:
+
+  /* Like ada_component::assign, but takes an operation as a
+     parameter.  The operation is evaluated and then assigned into LHS
+     according to the rules of the concrete implementation.  */
+  virtual void assign (struct value *container,
+                      struct value *lhs,
+                      struct expression *exp,
+                      std::vector<LONGEST> &indices,
+                      LONGEST low, LONGEST high,
+                      operation_up &op) = 0;
+
+  /* Same as operation::uses_objfile.  */
+  virtual bool uses_objfile (struct objfile *objfile) = 0;
+
+  /* Same as operation::dump.  */
+  virtual void dump (ui_file *stream, int depth) = 0;
+
+  virtual ~ada_association () = default;
+
+protected:
+
+  ada_association () = default;
+  DISABLE_COPY_AND_ASSIGN (ada_association);
+};
+
+/* Unique pointer specialization for Ada assignment associations.  */
+typedef std::unique_ptr<ada_association> ada_association_up;
+
+/* A component that holds a vector of associations and an operation.
+   The operation is re-evaluated for each choice.  */
+class ada_choices_component : public ada_component
+{
+public:
+
+  explicit ada_choices_component (operation_up &&op)
+    : m_op (std::move (op))
+  {
+  }
+
+  /* Set the vector of associations.  This is done separately from the
+     constructor because it was simpler for the implementation of the
+     parser.  */
+  void set_associations (std::vector<ada_association_up> &&assoc)
+  {
+    m_assocs = std::move (assoc);
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  std::vector<ada_association_up> m_assocs;
+  operation_up m_op;
+};
+
+/* An association that uses a discrete range.  */
+class ada_discrete_range_association : public ada_association
+{
+public:
+
+  ada_discrete_range_association (operation_up &&low, operation_up &&high)
+    : m_low (std::move (low)),
+      m_high (std::move (high))
+  {
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high,
+              operation_up &op) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  operation_up m_low;
+  operation_up m_high;
+};
+
+/* An association that uses a name.  The name may be an expression
+   that evaluates to an integer (for arrays), or an Ada string or
+   variable value operation.  */
+class ada_name_association : public ada_association
+{
+public:
+
+  explicit ada_name_association (operation_up val)
+    : m_val (std::move (val))
+  {
+  }
+
+  void assign (struct value *container,
+              struct value *lhs, struct expression *exp,
+              std::vector<LONGEST> &indices,
+              LONGEST low, LONGEST high,
+              operation_up &op) override;
+
+  bool uses_objfile (struct objfile *objfile) override;
+
+  void dump (ui_file *stream, int depth) override;
+
+private:
+
+  operation_up m_val;
+};
+
 } /* namespace expr */
 
 #endif /* ADA_EXP_H */
 
   ada_evaluate_subexp (NULL, exp, pos, EVAL_SKIP);
 }
 
+namespace expr
+{
+
+bool
+check_objfile (const std::unique_ptr<ada_component> &comp,
+              struct objfile *objfile)
+{
+  return comp->uses_objfile (objfile);
+}
+
+/* Assign the result of evaluating ARG starting at *POS to the INDEXth
+   component of LHS (a simple array or a record).  Does not modify the
+   inferior's memory, nor does it modify LHS (unless LHS ==
+   CONTAINER).  */
+
+static void
+assign_component (struct value *container, struct value *lhs, LONGEST index,
+                 struct expression *exp, operation_up &arg)
+{
+  scoped_value_mark mark;
+
+  struct value *elt;
+  struct type *lhs_type = check_typedef (value_type (lhs));
+
+  if (lhs_type->code () == TYPE_CODE_ARRAY)
+    {
+      struct type *index_type = builtin_type (exp->gdbarch)->builtin_int;
+      struct value *index_val = value_from_longest (index_type, index);
+
+      elt = unwrap_value (ada_value_subscript (lhs, 1, &index_val));
+    }
+  else
+    {
+      elt = ada_index_struct_field (index, lhs, 0, value_type (lhs));
+      elt = ada_to_fixed_value (elt);
+    }
+
+  ada_aggregate_operation *ag_op
+    = dynamic_cast<ada_aggregate_operation *> (arg.get ());
+  if (ag_op != nullptr)
+    ag_op->assign_aggregate (container, elt, exp);
+  else
+    value_assign_to_component (container, elt,
+                              arg->evaluate (nullptr, exp,
+                                             EVAL_NORMAL));
+}
+
+bool
+ada_aggregate_component::uses_objfile (struct objfile *objfile)
+{
+  for (const auto &item : m_components)
+    if (item->uses_objfile (objfile))
+      return true;
+  return false;
+}
+
+void
+ada_aggregate_component::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sAggregate\n"), depth, "");
+  for (const auto &item : m_components)
+    item->dump (stream, depth + 1);
+}
+
+void
+ada_aggregate_component::assign (struct value *container,
+                                struct value *lhs, struct expression *exp,
+                                std::vector<LONGEST> &indices,
+                                LONGEST low, LONGEST high)
+{
+  for (auto &item : m_components)
+    item->assign (container, lhs, exp, indices, low, high);
+}
+
+void
+ada_aggregate_operation::assign_aggregate (struct value *container,
+                                          struct value *lhs,
+                                          struct expression *exp)
+{
+  struct type *lhs_type;
+  LONGEST low_index, high_index;
+
+  container = ada_coerce_ref (container);
+  if (ada_is_direct_array_type (value_type (container)))
+    container = ada_coerce_to_simple_array (container);
+  lhs = ada_coerce_ref (lhs);
+  if (!deprecated_value_modifiable (lhs))
+    error (_("Left operand of assignment is not a modifiable lvalue."));
+
+  lhs_type = check_typedef (value_type (lhs));
+  if (ada_is_direct_array_type (lhs_type))
+    {
+      lhs = ada_coerce_to_simple_array (lhs);
+      lhs_type = check_typedef (value_type (lhs));
+      low_index = lhs_type->bounds ()->low.const_val ();
+      high_index = lhs_type->bounds ()->high.const_val ();
+    }
+  else if (lhs_type->code () == TYPE_CODE_STRUCT)
+    {
+      low_index = 0;
+      high_index = num_visible_fields (lhs_type) - 1;
+    }
+  else
+    error (_("Left-hand side must be array or record."));
+
+  std::vector<LONGEST> indices (4);
+  indices[0] = indices[1] = low_index - 1;
+  indices[2] = indices[3] = high_index + 1;
+
+  std::get<0> (m_storage)->assign (container, lhs, exp, indices,
+                                  low_index, high_index);
+}
+
+bool
+ada_positional_component::uses_objfile (struct objfile *objfile)
+{
+  return m_op->uses_objfile (objfile);
+}
+
+void
+ada_positional_component::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sPositional, index = %d\n"),
+                   depth, "", m_index);
+  m_op->dump (stream, depth + 1);
+}
+
+/* Assign into the component of LHS indexed by the OP_POSITIONAL
+   construct, given that the positions are relative to lower bound
+   LOW, where HIGH is the upper bound.  Record the position in
+   INDICES.  CONTAINER is as for assign_aggregate.  */
+void
+ada_positional_component::assign (struct value *container,
+                                 struct value *lhs, struct expression *exp,
+                                 std::vector<LONGEST> &indices,
+                                 LONGEST low, LONGEST high)
+{
+  LONGEST ind = m_index + low;
+
+  if (ind - 1 == high)
+    warning (_("Extra components in aggregate ignored."));
+  if (ind <= high)
+    {
+      add_component_interval (ind, ind, indices);
+      assign_component (container, lhs, ind, exp, m_op);
+    }
+}
+
+bool
+ada_discrete_range_association::uses_objfile (struct objfile *objfile)
+{
+  return m_low->uses_objfile (objfile) || m_high->uses_objfile (objfile);
+}
+
+void
+ada_discrete_range_association::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sDiscrete range:\n"), depth, "");
+  m_low->dump (stream, depth + 1);
+  m_high->dump (stream, depth + 1);
+}
+
+void
+ada_discrete_range_association::assign (struct value *container,
+                                       struct value *lhs,
+                                       struct expression *exp,
+                                       std::vector<LONGEST> &indices,
+                                       LONGEST low, LONGEST high,
+                                       operation_up &op)
+{
+  LONGEST lower = value_as_long (m_low->evaluate (nullptr, exp, EVAL_NORMAL));
+  LONGEST upper = value_as_long (m_high->evaluate (nullptr, exp, EVAL_NORMAL));
+
+  if (lower <= upper && (lower < low || upper > high))
+    error (_("Index in component association out of bounds."));
+
+  add_component_interval (lower, upper, indices);
+  while (lower <= upper)
+    {
+      assign_component (container, lhs, lower, exp, op);
+      lower += 1;
+    }
+}
+
+bool
+ada_name_association::uses_objfile (struct objfile *objfile)
+{
+  return m_val->uses_objfile (objfile);
+}
+
+void
+ada_name_association::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sName:\n"), depth, "");
+  m_val->dump (stream, depth + 1);
+}
+
+void
+ada_name_association::assign (struct value *container,
+                             struct value *lhs,
+                             struct expression *exp,
+                             std::vector<LONGEST> &indices,
+                             LONGEST low, LONGEST high,
+                             operation_up &op)
+{
+  int index;
+
+  if (ada_is_direct_array_type (value_type (lhs)))
+    index = longest_to_int (value_as_long (m_val->evaluate (nullptr, exp,
+                                                           EVAL_NORMAL)));
+  else
+    {
+      ada_string_operation *strop
+       = dynamic_cast<ada_string_operation *> (m_val.get ());
+
+      const char *name;
+      if (strop != nullptr)
+       name = strop->get_name ();
+      else
+       {
+         ada_var_value_operation *vvo
+           = dynamic_cast<ada_var_value_operation *> (m_val.get ());
+         if (vvo != nullptr)
+           error (_("Invalid record component association."));
+         name = vvo->get_symbol ()->natural_name ();
+       }
+
+      index = 0;
+      if (! find_struct_field (name, value_type (lhs), 0,
+                              NULL, NULL, NULL, NULL, &index))
+       error (_("Unknown component name: %s."), name);
+    }
+
+  add_component_interval (index, index, indices);
+  assign_component (container, lhs, index, exp, op);
+}
+
+bool
+ada_choices_component::uses_objfile (struct objfile *objfile)
+{
+  if (m_op->uses_objfile (objfile))
+    return true;
+  for (const auto &item : m_assocs)
+    if (item->uses_objfile (objfile))
+      return true;
+  return false;
+}
+
+void
+ada_choices_component::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sChoices:\n"), depth, "");
+  m_op->dump (stream, depth + 1);
+  for (const auto &item : m_assocs)
+    item->dump (stream, depth + 1);
+}
+
+/* Assign into the components of LHS indexed by the OP_CHOICES
+   construct at *POS, updating *POS past the construct, given that
+   the allowable indices are LOW..HIGH.  Record the indices assigned
+   to in INDICES.  CONTAINER is as for assign_aggregate.  */
+void
+ada_choices_component::assign (struct value *container,
+                              struct value *lhs, struct expression *exp,
+                              std::vector<LONGEST> &indices,
+                              LONGEST low, LONGEST high)
+{
+  for (auto &item : m_assocs)
+    item->assign (container, lhs, exp, indices, low, high, m_op);
+}
+
+bool
+ada_others_component::uses_objfile (struct objfile *objfile)
+{
+  return m_op->uses_objfile (objfile);
+}
+
+void
+ada_others_component::dump (ui_file *stream, int depth)
+{
+  fprintf_filtered (stream, _("%*sOthers:\n"), depth, "");
+  m_op->dump (stream, depth + 1);
+}
+
+/* Assign the value of the expression in the OP_OTHERS construct in
+   EXP at *POS into the components of LHS indexed from LOW .. HIGH that
+   have not been previously assigned.  The index intervals already assigned
+   are in INDICES.  CONTAINER is as for assign_aggregate.  */
+void
+ada_others_component::assign (struct value *container,
+                             struct value *lhs, struct expression *exp,
+                             std::vector<LONGEST> &indices,
+                             LONGEST low, LONGEST high)
+{
+  int num_indices = indices.size ();
+  for (int i = 0; i < num_indices - 2; i += 2)
+    {
+      for (LONGEST ind = indices[i + 1] + 1; ind < indices[i + 2]; ind += 1)
+       assign_component (container, lhs, ind, exp, m_op);
+    }
+}
+
+struct value *
+ada_assign_operation::evaluate (struct type *expect_type,
+                               struct expression *exp,
+                               enum noside noside)
+{
+  value *arg1 = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
+
+  ada_aggregate_operation *ag_op
+    = dynamic_cast<ada_aggregate_operation *> (std::get<1> (m_storage).get ());
+  if (ag_op != nullptr)
+    {
+      if (noside != EVAL_NORMAL)
+       return arg1;
+
+      ag_op->assign_aggregate (arg1, arg1, exp);
+      return ada_value_assign (arg1, arg1);
+    }
+  /* Force the evaluation of the rhs ARG2 to the type of the lhs ARG1,
+     except if the lhs of our assignment is a convenience variable.
+     In the case of assigning to a convenience variable, the lhs
+     should be exactly the result of the evaluation of the rhs.  */
+  struct type *type = value_type (arg1);
+  if (VALUE_LVAL (arg1) == lval_internalvar)
+    type = NULL;
+  value *arg2 = std::get<1> (m_storage)->evaluate (type, exp, noside);
+  if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+    return arg1;
+  if (VALUE_LVAL (arg1) == lval_internalvar)
+    {
+      /* Nothing.  */
+    }
+  else
+    arg2 = coerce_for_assign (value_type (arg1), arg2);
+  return ada_value_assign (arg1, arg2);
+}
+
+} /* namespace expr */
+
 /* Add the interval [LOW .. HIGH] to the sorted set of intervals
    [ INDICES[0] .. INDICES[1] ],...  The resulting intervals do not
    overlap.  */