Signed-off-by: Jose E. Marchesi <[email protected]>

gcc/ChangeLog

        * algol68/a68-low-clauses.cc: New file.
        * algol68/a68-low-decls.cc: Likewise.
---
 gcc/algol68/a68-low-clauses.cc | 1407 ++++++++++++++++++++++++++++++++
 gcc/algol68/a68-low-decls.cc   |  629 ++++++++++++++
 2 files changed, 2036 insertions(+)
 create mode 100644 gcc/algol68/a68-low-clauses.cc
 create mode 100644 gcc/algol68/a68-low-decls.cc

diff --git a/gcc/algol68/a68-low-clauses.cc b/gcc/algol68/a68-low-clauses.cc
new file mode 100644
index 00000000000..7ed026f7fc1
--- /dev/null
+++ b/gcc/algol68/a68-low-clauses.cc
@@ -0,0 +1,1407 @@
+/* Lower clauses to GENERIC.
+   Copyright (C) 2025 Jose E. Marchesi.
+
+   Written by Jose E. Marchesi.
+
+   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/>.  */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "tree.h"
+#include "fold-const.h"
+#include "diagnostic.h"
+#include "langhooks.h"
+#include "tm.h"
+#include "function.h"
+#include "cgraph.h"
+#include "toplev.h"
+#include "varasm.h"
+#include "predict.h"
+#include "stor-layout.h"
+#include "tree-iterator.h"
+#include "stringpool.h"
+#include "print-tree.h"
+#include "gimplify.h"
+#include "dumpfile.h"
+#include "convert.h"
+
+#include "a68.h"
+
+/* Given a serial_clause node P, return whether it performs dynamic stack
+   allocations.
+
+   This function allocates for the fact that the bottom-up parser generates
+   successively nested serial clauses like
+
+     SERIAL_CLAUSE
+       SERIAL_CLAUSE
+        ...
+
+   the outer of which corresponds to a single serial clause in the source code,
+   but it is the inner ones annotated by the dsa pass.  */
+
+static bool
+serial_clause_dsa (NODE_T *p)
+{
+  NODE_T *s = NEXT (SUB (p));
+
+  for (s = p; SUB (s) &&  IS (s, SERIAL_CLAUSE); s = SUB (s))
+    {
+      if (DYNAMIC_STACK_ALLOCS (s))
+       return true;
+    }
+
+  return false;
+}
+
+/* Lower one or more labels.
+
+     label : defining identifier, colon symbol;
+             label, defining identifier, colon symbol;
+
+   A label lowers into a LABEL_EXPR and the declaration of a LABEL_DECL in the
+   current block and bind.  Lists of labels get returned in nested compound
+   expressions.  */
+
+tree
+a68_lower_label (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree expr = NULL_TREE;
+
+  if (IS (SUB (p), LABEL))
+    expr = a68_lower_tree (SUB (p), ctx);
+
+  NODE_T *defining_identifier;
+
+  if (IS (SUB (p), DEFINING_IDENTIFIER))
+    defining_identifier = SUB (p);
+  else
+    {
+      gcc_assert (IS (NEXT (SUB (p)), DEFINING_IDENTIFIER));
+      defining_identifier = NEXT (SUB (p));
+    }
+
+  /* Create LABEL_DECL if necessary.  */
+  tree label_decl = TAX_TREE_DECL (TAX (defining_identifier));
+  if (label_decl == NULL_TREE)
+    {
+      label_decl = build_decl (a68_get_node_location (defining_identifier),
+                              LABEL_DECL,
+                              a68_get_mangled_identifier (NSYMBOL 
(defining_identifier)),
+                              void_type_node);
+      TAX_TREE_DECL (TAX (defining_identifier)) = label_decl;
+    }
+
+  a68_add_decl (label_decl);
+
+  /* Return the accummulated LABEL_EXPRs.  */
+  tree label_expr = build1 (LABEL_EXPR, void_type_node, label_decl);
+  if (expr)
+    return fold_build2_loc (a68_get_node_location (p),
+                           COMPOUND_EXPR,
+                           void_type_node,
+                           expr, label_expr);
+  else
+    return label_expr;
+}
+
+/* Lower a labeled unit.
+
+     labeled unit : label, unit.
+
+   Lower the label, then the unit.  Return them in a compound expression.  */
+
+tree
+a68_lower_labeled_unit (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree label_expr = a68_lower_tree (SUB (p), ctx);
+  tree unit_expr = a68_lower_tree (NEXT (SUB (p)), ctx);
+
+  return fold_build2_loc (a68_get_node_location (p),
+                         COMPOUND_EXPR,
+                         TREE_TYPE (unit_expr),
+                         label_expr, unit_expr);
+}
+
+/* Lower a completer.
+
+     exit_symbol
+
+   This handler replaces the last expression in stmt_list with a statement
+   assigning it to the clause result of the current serial clause, then jump to
+   the exit label of the current serial clause. Note that a completer is a
+   separator so stmt_list contains at least one expression at this point.  Note
+   that a completer can only appear inside a serial clause.
+
+   This function always returns NULL_TREE, so the traversing code shall always
+   be careful to travese on these nodes explicitly and ignore the returned
+   value.  */
+
+tree
+a68_lower_completer (NODE_T *p ATTRIBUTE_UNUSED, LOW_CTX_T ctx 
ATTRIBUTE_UNUSED)
+{
+  a68_add_completer ();
+  return NULL_TREE;
+}
+
+/* Lower an initialiser series.
+
+   Parse tree:
+
+   initialiser series : serial clause, semi symbol, declaration list;
+                        initialiser series, declaration list;
+                       initialiser series, semi symbol, unit;
+                       initialiser series, semi symbol, labeled unit;
+                       initialiser series, semi symbol, declaration list.
+
+   GENERIC:
+
+   Traverse subtree adding units and labels to STMT_LIST, and declarations to
+   BLOCK.
+
+   This function always returns NULL_TREE, so the traversing code shall always
+   be careful to travese on these nodes explicitly and ignore the returned
+   value.  */
+
+tree
+a68_lower_initialiser_series (NODE_T *p, LOW_CTX_T ctx)
+{
+  for (NODE_T *s = SUB (p); s != NO_NODE; FORWARD (s))
+    {
+      if (!IS (s, SEMI_SYMBOL))
+       a68_add_stmt (a68_lower_tree (s, ctx));
+    }
+  return NULL_TREE;
+}
+
+/* Lower a serial clause.
+
+     serial clause : labeled unit;
+                     unit;
+                    serial clause, semi symbol, unit;
+                    serial clause, exit symbol, labeled unit;
+                    serial clause, semi_symbol, declaration list;
+                    initialiser series, semi symbol, unit;
+                    initialiser series, semi symbol, labeled unit.
+
+   Ranges:
+
+     serial-clause
+     ------------- R1
+
+   See the function body to see the lowering actions.
+
+   This function always returns NULL_TREE, so the traversing code shall always
+   be careful to travese on these nodes explicitly and ignore the returned
+   value.  */
+
+tree
+a68_lower_serial_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  if (IS (SUB (p), SERIAL_CLAUSE))
+    {
+      /* Traverse down for side-effects.  */
+      (void) a68_lower_tree (SUB (p), ctx);
+
+      if (IS (NEXT (SUB (p)), EXIT_SYMBOL))
+       {
+         /* Traverse the completer for side-effects.  This turns the last
+            expression in the current statements list into an assignment.  */
+         (void) a68_lower_tree (NEXT (SUB (p)), ctx);
+         /* Now append the result of the labeled unit to the current
+            statements list.  */
+         a68_add_stmt (a68_lower_tree (NEXT (NEXT (SUB (p))), ctx));
+       }
+      else
+       {
+         /* Append the result of either the unit or the declarations list in
+            the current statements list.  */
+         a68_add_stmt (a68_lower_tree (NEXT (NEXT (SUB (p))), ctx));
+       }
+    }
+  else if (IS (SUB (p), INITIALISER_SERIES))
+    {
+      /* Traverse down for side-effects.  */
+      (void) a68_lower_tree (SUB (p), ctx);
+
+      /* Append the result of either the unit or the declarations list in the
+        current statements list.  */
+      a68_add_stmt (a68_lower_tree (NEXT (NEXT (SUB (p))), ctx));
+    }
+  else
+    {
+      /* Append the result of either the unit or labeled unit in the current
+        statements list.  */
+      a68_add_stmt (a68_lower_tree (SUB (p), ctx));
+    }
+
+  return NULL_TREE;
+}
+
+/* Lower a loop clause.
+
+     loop clause : for part, from part, by part, to part, while part, alt do 
part;
+                   for part, from part, by part, while part, alt do part;
+                  for part, from part, while part, alt do part;
+                  for part, by part, to part, while part, alt do part;
+                  for part, by part, to part, while part, alt do part;
+                  for part, by part, while part, alt do part;
+                  for part, while part, alt do part;
+                  for part, from part, by part, to part, alt do part;
+                  for part, from part, by part, alt do part;
+                  for part, from part, alt do part;
+                  for part, by part, to part, alt do part;
+                  for part, by part, alt do part;
+                  for part, to part, alt do part,
+                  for part, alt do part;
+                  from part, by part, to part, while part, alt do part;
+                  from part, by part, while aprt, alt do part;
+                  from part, to part, while aprt, alt do part;
+                  from part, while part, alt do part;
+                  from part, by part, to part, alt do part;
+                  from part, by part, alt do part;
+                  from part, to part, alt do part;
+                  from part, alt do part;
+                  by part, to part, while part, alt do part;
+                  by part, while part, alt do part;
+                  by part, to part, alt do part;
+                  by part, alt do part;
+                  to part, while part, alt do part;
+                  to part, alt do part;
+                  while part, alt do part;
+                  do part.
+*/
+
+tree
+a68_lower_loop_clause (NODE_T *p ATTRIBUTE_UNUSED,
+                      LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  NODE_T *s = SUB (p);
+  bool while_part = false;
+  bool has_iterator = false;
+  tree iterator = NULL_TREE;
+  tree while_condition = NULL_TREE;
+  tree do_part = NULL_TREE;
+  tree from_part = NULL_TREE;
+  tree by_part = NULL_TREE;
+  tree to_part = NULL_TREE;
+  tree overflow = NULL_TREE;
+  NODE_T *iterator_defining_identifier = NO_NODE;
+
+  if (IS (s, FOR_PART))
+    {
+      /* Get the defining identifier.  */
+      iterator_defining_identifier = NEXT (SUB (s));
+      has_iterator = true;
+      FORWARD (s);
+    }
+
+  if (IS (s, FROM_PART))
+    {
+      /* Lower the unit.  */
+      from_part = a68_lower_tree (NEXT (SUB (s)), ctx);
+      has_iterator = true;
+      FORWARD (s);
+    }
+
+  if (IS (s, BY_PART))
+    {
+      /* Lower the unit.  */
+      by_part = a68_lower_tree (NEXT (SUB (s)), ctx);
+      has_iterator = true;
+      FORWARD (s);
+    }
+
+  if (IS (s, TO_PART))
+    {
+      /* Lower the unit.  */
+      to_part = a68_lower_tree (NEXT (SUB (s)), ctx);
+      has_iterator = true;
+      FORWARD (s);
+    }
+
+  if (has_iterator)
+    {
+      /* Introduce a range that spans until the end of the loop clause.   */
+      a68_push_range (M_VOID);
+
+      /* Compute some defaults for not specified loop parts.  Note that to_part
+        defaults to max_int or min_int depending on the signedness of
+        by_part.  */
+      if (from_part == NULL_TREE)
+       from_part = integer_one_node;
+      if (by_part == NULL_TREE)
+       by_part = integer_one_node;
+      if (to_part == NULL_TREE)
+       {
+         to_part = fold_build3 (COND_EXPR,
+                                a68_bool_type,
+                                fold_build2 (LT_EXPR, a68_int_type, by_part,
+                                             build_int_cst (a68_int_type, 0)),
+                                a68_int_minval (a68_int_type),
+                                a68_int_maxval (a68_int_type));
+       }
+
+      /* If the user has specified an explicit iterator in the form of a
+        defining-identifier in a for-part, use it as the name in the iterator
+        declaration and install the resulting declaration in the taxes table
+        in order for applied identifiers in the rest of the loop to find it.
+        Otherwise, the iterator is not directly accessible by the
+        programmer.  */
+      const char *iterator_name = (iterator_defining_identifier == NO_NODE
+                                  ? "iterator%"
+                                  : NSYMBOL (iterator_defining_identifier));
+      iterator = a68_lower_tmpvar (iterator_name, a68_int_type, from_part);
+      if (iterator_defining_identifier != NO_NODE)
+       TAX_TREE_DECL (TAX (iterator_defining_identifier)) = iterator;
+
+      /* The from_part and to_part expressions shall be evaluated once and once
+        only.  The expression for from_part is evaluated only once in the
+        initialization expression for iterator% above, but we need to put
+        to_part in a temporary since it is used in the loop body.  */
+      to_part = a68_lower_tmpvar ("to_part%", TREE_TYPE (to_part), to_part);
+
+      /* We need to detect overflow/underflow of the iterator.  */
+      overflow = a68_lower_tmpvar ("overflow%", boolean_type_node,
+                                  boolean_false_node);
+    }
+
+  if (IS (s, WHILE_PART))
+    {
+      while_part = true;
+      /* Introduce a range that spans until the end of the loop clause.  */
+      a68_push_range (M_VOID);
+      /* Process the enquiry clause, which yields a BOOL.  */
+      a68_push_stmt_list (M_BOOL);
+      (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+      while_condition = a68_pop_stmt_list ();
+      FORWARD (s);
+    }
+
+  /* DO part.  */
+  gcc_assert (IS (s, ALT_DO_PART) || IS (s, DO_PART));
+
+  /* Build the loop's body.  */
+  a68_push_range (NULL);
+  {
+    /* First lower the loop exit condition.  */
+    if (has_iterator || while_part)
+      {
+       tree exit_condition = NULL_TREE;
+       /* IF overflow OREL (by_part < 0 THEN iterator < to_part ELSE iterator 
> to_part) FI */
+       if (has_iterator)
+         exit_condition = fold_build2 (TRUTH_ORIF_EXPR, boolean_type_node,
+                                       overflow,
+                                       fold_build3 (COND_EXPR,
+                                                    a68_bool_type,
+                                                    fold_build2 (LT_EXPR, 
a68_int_type, by_part,
+                                                                 build_int_cst 
(a68_int_type, 0)),
+                                                    fold_build2 (LT_EXPR, 
a68_int_type,
+                                                                 iterator, 
to_part),
+                                                    fold_build2 (GT_EXPR, 
a68_int_type,
+                                                                 iterator, 
to_part)));
+       /* NOT while_condition */
+       if (while_part)
+         {
+           tree while_exit_condition = fold_build1 (TRUTH_NOT_EXPR,
+                                                    a68_bool_type,
+                                                    while_condition);
+           if (has_iterator)
+             exit_condition = fold_build2 (TRUTH_ORIF_EXPR, a68_bool_type,
+                                           exit_condition, 
while_exit_condition);
+           else
+             exit_condition = while_exit_condition;
+         }
+
+       if (exit_condition != NULL_TREE)
+         a68_add_stmt (fold_build1 (EXIT_EXPR, void_type_node, 
exit_condition));
+      }
+
+    /* Serial clauses in DO .. OD do not yield any value.  */
+    bool dsa = serial_clause_dsa (NEXT (SUB (s)));
+    bool local = NON_LOCAL (NEXT (SUB (s))) == NO_TABLE;
+    a68_push_serial_clause_range (M_VOID, dsa && local);
+    (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+    do_part = a68_pop_serial_clause_range ();
+    a68_add_stmt (do_part);
+
+    if (has_iterator)
+      {
+       /* Increment the iterator by BY_PART.  Detect overflow.
+          Given a + b = sum, overflows = ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)
+          See OVERFLOW_SUM_SIGN in double-int.cc for an explanation
+          of this formula.
+       */
+       tree type = TREE_TYPE (iterator);
+       tree a = iterator;
+       tree b = save_expr (by_part);
+       tree sum = fold_build2 (PLUS_EXPR, type, a, b);
+       a68_add_stmt (fold_build2 (MODIFY_EXPR, boolean_type_node,
+                                  overflow,
+                                  fold_build2 (LT_EXPR, boolean_type_node,
+                                               fold_build2 (BIT_AND_EXPR, type,
+                                                            fold_build1 
(BIT_NOT_EXPR, type,
+                                                                         
fold_build2 (BIT_XOR_EXPR, type,
+                                                                               
       a, b)),
+                                                            fold_build2 
(BIT_XOR_EXPR, type,
+                                                                         a, 
sum)),
+                                               build_int_cst (a68_int_type, 
0))));
+       a68_add_stmt (fold_build2 (MODIFY_EXPR, type, iterator, sum));
+      }
+  }
+  tree loop_body = a68_pop_range ();
+
+  /* Finally build the LOOP_EXPR and exit the introduced ranges.  */
+  tree loop_clause = fold_build1_loc (a68_get_node_location (p),
+                                     LOOP_EXPR, a68_void_type, loop_body);
+  if (while_part)
+    {
+      a68_add_stmt (loop_clause);
+      loop_clause = a68_pop_range ();
+    }
+  if (has_iterator)
+    {
+      a68_add_stmt (loop_clause);
+      loop_clause = a68_pop_range ();
+    }
+
+  return loop_clause;
+}
+
+/* Lower a conformity clause.
+
+     conformity clause : case part, conformity in part, out part, esac symbol;
+                         case part, conformity in part, esac symbol;
+                        case part, conformity in part, conformity ouse part;
+                         open part, conformity choice, choice, close symbol;
+                         open part, conformity choice, close symbol;
+                        open part, conformity choice, brief conformity ouse 
part.
+
+     conformity choice : then bar symbol, specified unit list;
+                         then bar symbol, specified unit.
+
+     specified unit list : specified unit list, comma symbol, specified unit;
+                           specified unit list, specified unit.
+
+     specified unit : specifier, colon symbol, unit.
+
+     specifier : open symbol, declarer, identifier, close symbol;
+                 open symbol, declarer, close symbol;
+                open symbol, void symbol, close symbol.
+*/
+
+static void
+lower_unite_case_unit (NODE_T *p,
+                      tree enquiry, MOID_T *enquiry_mode,
+                      tree result, tree exit_label, LOW_CTX_T ctx)
+{
+  for (; p != NO_NODE; FORWARD (p))
+    {
+      if (IS (p, SPECIFIER))
+       {
+         MOID_T *spec_moid = MOID (NEXT (SUB (p)));
+         NODE_T *spec_identifier = NEXT (NEXT (SUB (p)));
+         NODE_T *spec_unit = NEXT (NEXT (p));
+         const char *specifier_identifier_name = NULL;
+         if (IS (spec_identifier, IDENTIFIER))
+           specifier_identifier_name = NSYMBOL (spec_identifier);
+
+         tree overhead = a68_union_overhead (enquiry);
+         tree spec_value = NULL_TREE;
+         tree entry_selected = NULL_TREE;
+         if (IS_UNION (spec_moid))
+           {
+             /* The spec_moid is an united mode, which must be unitable to the
+                enquiry_mode.  */
+             gcc_assert (a68_is_unitable (spec_moid, enquiry_mode,
+                                          SAFE_DEFLEXING));
+
+             /* Build the entry_selected expression.
+
+                For each mode in spec_moid, determine the corresponding index
+                in enquiry_mode and add a check for it to the expression.  */
+             for (PACK_T *pack = PACK (spec_moid); pack != NO_PACK; FORWARD 
(pack))
+               {
+                 int index = a68_united_mode_index (enquiry_mode, MOID (pack));
+                 tree expr = fold_build2 (EQ_EXPR,
+                                          boolean_type_node,
+                                          overhead,
+                                          build_int_cst (TREE_TYPE (overhead), 
index));
+                 if (entry_selected == NULL_TREE)
+                   entry_selected = expr;
+                 else
+                   entry_selected = fold_build2 (TRUTH_OR_EXPR,
+                                                 boolean_type_node,
+                                                 entry_selected,
+                                                 expr);
+               }
+
+             /* The spec_value is an union of mode spec_moid, with the
+                overhead translated from enquiry_mode.  */
+             tree spec_overhead
+               = a68_union_translate_overhead (enquiry_mode, overhead, 
spec_moid);
+             a68_push_range (spec_moid);
+             spec_value = a68_lower_tmpvar ("spec_value%",
+                                            CTYPE (spec_moid),
+                                            a68_get_skip_tree (spec_moid));
+             a68_add_stmt (a68_union_set_overhead (spec_value, spec_overhead));
+             tree from_cunion = a68_union_cunion (enquiry);
+             tree to_cunion = a68_union_cunion (spec_value);
+             a68_add_stmt (a68_lower_memcpy (fold_build1 (ADDR_EXPR,
+                                                          build_pointer_type 
(TREE_TYPE (to_cunion)),
+                                                          to_cunion),
+                                             fold_build1 (ADDR_EXPR,
+                                                          build_pointer_type 
(TREE_TYPE (from_cunion)),
+                                                          from_cunion),
+                                             size_in_bytes (TREE_TYPE 
(to_cunion))));
+             a68_add_stmt (spec_value);
+             spec_value = a68_pop_range ();
+           }
+         else
+           {
+             int index = a68_united_mode_index (enquiry_mode, spec_moid);
+             spec_value = a68_union_alternative (enquiry, index);
+             entry_selected = fold_build2 (EQ_EXPR,
+                                           TREE_TYPE (overhead),
+                                           overhead,
+                                           build_int_cst (TREE_TYPE 
(overhead), index));
+           }
+
+         a68_push_range (M_VOID);
+         {
+           /* If the enquiry value is ascribed to an identifier in the case
+              entry then create a suitable declaration and turn the identifier
+              into a defining identifier.  */
+           if (specifier_identifier_name)
+             {
+               tree united_value = a68_lower_tmpvar (specifier_identifier_name,
+                                                     CTYPE (spec_moid), 
spec_value);
+               TAX_TREE_DECL (TAX (spec_identifier)) = united_value;
+             }
+
+           /* Set result% to the lowering of the unit and jump to the end of
+              the enquiry clause.  */
+           a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (result),
+                                      result, a68_lower_tree (spec_unit, 
ctx)));
+           a68_add_stmt (fold_build1 (GOTO_EXPR,
+                                      void_type_node,
+                                      exit_label));
+           a68_add_stmt (a68_get_skip_tree (M_VOID));
+         }
+         tree process_entry = a68_pop_range ();
+
+         /* IF index = overhead THEN process entry FI */
+         a68_add_stmt (fold_build3 (COND_EXPR,
+                                    a68_void_type,
+                                    entry_selected,
+                                    process_entry,
+                                    a68_get_skip_tree (M_VOID)));
+
+         FORWARD (p); /* Skip specifier.  */
+         FORWARD (p); /* Skip unit.  */
+         /* The unit is skipped in the for loop post-action.  */
+       }
+      else
+       lower_unite_case_unit (SUB (p),
+                              enquiry, enquiry_mode,
+                              result, exit_label, ctx);
+    }
+}
+
+tree
+a68_lower_conformity_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  MOID_T *conformity_clause_mode = MOID (p);
+
+  /* CASE or OUSE.  */
+  NODE_T *s = SUB (p);
+  NODE_T *enquiry_node = NEXT (SUB (s));
+  MOID_T *enquiry_mode = MOID (SUB (s));
+
+  /* Push a binding environment for the enquiry clause.  */
+  a68_push_range (conformity_clause_mode);
+
+  /* Process the enquiry clause and put the resulting value in enquiry%.  */
+  a68_push_stmt_list (enquiry_mode);
+  (void) a68_lower_tree (enquiry_node, ctx);
+  tree enquiry = a68_lower_tmpvar ("enquiry%",
+                                  CTYPE (enquiry_mode),
+                                  a68_pop_stmt_list ());
+
+  /* Create a decl for result%.  */
+  tree result = a68_lower_tmpvar ("result%",
+                                 CTYPE (conformity_clause_mode),
+                                 a68_get_skip_tree (conformity_clause_mode));
+
+  /* Create an exit label.  */
+  tree exit_label = build_decl (UNKNOWN_LOCATION,
+                               LABEL_DECL,
+                               get_identifier ("exit_label%"),
+                               void_type_node);
+  DECL_CONTEXT (exit_label) = a68_range_context ();
+  a68_add_decl (exit_label);
+  a68_add_decl_expr (fold_build1 (DECL_EXPR, TREE_TYPE (exit_label), 
exit_label));
+
+  /* IN.  */
+  FORWARD (s);
+  lower_unite_case_unit (NEXT (SUB (s)),
+                        enquiry, enquiry_mode,
+                        result, exit_label, ctx);
+
+  /* OUT.  */
+  FORWARD (s);
+  switch (ATTRIBUTE (s))
+    {
+    case CHOICE:
+    case OUT_PART:
+      {
+       bool dsa = serial_clause_dsa (NEXT (SUB (s)));
+       bool local = NON_LOCAL (NEXT (SUB (s))) == NO_TABLE;
+       a68_push_serial_clause_range (conformity_clause_mode, dsa && local);
+
+       (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+       a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (result),
+                                  result, a68_pop_serial_clause_range ()));
+       a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+       break;
+      }
+    case CLOSE_SYMBOL:
+    case ESAC_SYMBOL:
+      a68_add_stmt (fold_build2 (MODIFY_EXPR,
+                                TREE_TYPE (result),
+                                result,
+                                a68_get_skip_tree (conformity_clause_mode)));
+      a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+      break;
+    default:
+      /* Recurse.
+
+        Note that the parser guarantees that the embedded CASE clause is a
+        conformity clause, and that its mode is the same than the containing
+        clause, but it doesn't annotate the mode in the tree node so we have
+        to do it here.  */
+      MOID (s) = conformity_clause_mode;
+      a68_add_stmt (fold_build2 (MODIFY_EXPR,
+                                TREE_TYPE (result),
+                                result,
+                                a68_lower_conformity_clause (s, ctx)));
+      a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+      break;
+    }
+
+  /* ESAC */
+  a68_add_stmt (build1 (LABEL_EXPR, void_type_node, exit_label));
+  a68_add_stmt (result);
+  return a68_pop_range ();
+}
+
+/* Lower a case clause.
+
+     case clause : open part, case choice clause, choice, close symbol;
+                   open part, case choice clause, close symbol;
+                  open part, case shoice clause, brief ouse part;
+                  case part, case in part, out part, esac symbol;
+                  case part, case in part, esac symbol;
+                  case part, case in part, case ouse part;
+*/
+
+static void
+lower_int_case_unit (NODE_T *p,
+                    tree enquiry, MOID_T *enquiry_mode,
+                    tree result, tree exit_label, int *count,
+                    LOW_CTX_T ctx)
+{
+  for (; p != NO_NODE; FORWARD (p))
+    {
+      if (IS (p, UNIT))
+       {
+         a68_push_range (M_VOID);
+         {
+           /* Set result% to the lowering of the unit and jump to the end of
+              the enquiry clause.  */
+           a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (result),
+                                      result, a68_lower_tree (p, ctx)));
+           a68_add_stmt (fold_build1 (GOTO_EXPR,
+                                      void_type_node,
+                                      exit_label));
+           a68_add_stmt (a68_get_skip_tree (M_VOID));
+         }
+         tree process_entry = a68_pop_range ();
+
+         /* IF count = enquiry THEN process entry FI */
+         a68_add_stmt (fold_build3 (COND_EXPR,
+                                    a68_void_type,
+                                    fold_build2 (EQ_EXPR,
+                                                 TREE_TYPE (enquiry),
+                                                 enquiry,
+                                                 build_int_cst (TREE_TYPE 
(enquiry), *count)),
+                                    process_entry,
+                                    a68_get_skip_tree (M_VOID)));
+         *count += 1;
+       }
+      else
+       lower_int_case_unit (SUB (p),
+                            enquiry, enquiry_mode,
+                            result, exit_label, count, ctx);
+    }
+}
+
+tree
+a68_lower_case_clause (NODE_T *p ATTRIBUTE_UNUSED,
+                      LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  MOID_T *case_clause_mode = MOID (p);
+
+  /* CASE or OUSE  */
+  NODE_T *s = SUB (p);
+  NODE_T *enquiry_node = NEXT (SUB (s));
+  MOID_T *enquiry_mode = M_INT;
+
+  /* Push a bingding environment fo the case clause.  */
+  a68_push_range (case_clause_mode);
+
+  /* Process the enquiry clause and put the result value in enquiry%.  */
+  a68_push_stmt_list (enquiry_mode);
+  (void) a68_lower_tree (enquiry_node, ctx);
+  tree enquiry = a68_lower_tmpvar ("enquiry%",
+                                  CTYPE (enquiry_mode),
+                                  a68_pop_stmt_list ());
+  /* Create a decl for result%.  */
+  tree result = a68_lower_tmpvar ("result%",
+                                 CTYPE (case_clause_mode),
+                                 a68_get_skip_tree (case_clause_mode));
+
+  /* Create an exit label.  */
+  tree exit_label = build_decl (UNKNOWN_LOCATION,
+                               LABEL_DECL,
+                               get_identifier ("exit_label%"),
+                               void_type_node);
+  DECL_CONTEXT (exit_label) = a68_range_context ();
+  a68_add_decl (exit_label);
+  a68_add_decl_expr (fold_build1 (DECL_EXPR, TREE_TYPE (exit_label), 
exit_label));
+
+  /* IN.  */
+  FORWARD (s);
+  int count = 1;
+  lower_int_case_unit (NEXT (SUB (s)),
+                      enquiry, enquiry_mode,
+                      result, exit_label, &count, ctx);
+
+  /* OUT.  */
+  FORWARD (s);
+  switch (ATTRIBUTE (s))
+    {
+    case CHOICE:
+    case OUT_PART:
+      {
+       bool dsa = serial_clause_dsa (NEXT (SUB (s)));
+       bool local = NON_LOCAL (NEXT (SUB (s))) == NO_TABLE;
+       a68_push_serial_clause_range (case_clause_mode, dsa && local);
+
+       (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+       a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (result),
+                                  result, a68_pop_serial_clause_range ()));
+       a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+       break;
+      }
+    case CLOSE_SYMBOL:
+    case ESAC_SYMBOL:
+      a68_add_stmt (fold_build2 (MODIFY_EXPR,
+                                TREE_TYPE (result),
+                                result,
+                                a68_get_skip_tree (case_clause_mode)));
+      a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+      break;
+    default:
+      /* Recurse.
+
+        Note that the parser guarantees that the embedded CASE clause has the
+        same mode than the containing clause, but it doesn't annotate the OUSE
+        node with its mode so we have to do it here.  */
+      MOID (s) = case_clause_mode;
+      a68_add_stmt (fold_build2 (MODIFY_EXPR,
+                                TREE_TYPE (result),
+                                result,
+                                a68_lower_case_clause (s, ctx)));
+      a68_add_stmt (fold_build1 (GOTO_EXPR, void_type_node, exit_label));
+      break;
+    }
+
+  /* ESAC */
+  a68_add_stmt (build1 (LABEL_EXPR, void_type_node, exit_label));
+  a68_add_stmt (result);
+  return a68_pop_range ();
+}
+
+/* Lower an enquiry clause.
+
+     enquiry clause : unit;
+                      enquiry clause, semi symbol, unit;
+                      enquiry clause, comma symbol, unit;
+                     initialiser series, semi symbol, unit.
+
+   The units and declarations in the enquiry clause get lowered into
+   expressions and declaration nodes which are added to the current serial
+   clause.
+
+   This function always returns NULL_TREE, so the traversing code shall always
+   be careful to travese on these nodes explicitly and ignore the returned
+   value.  */
+
+tree
+a68_lower_enquiry_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  if (IS (SUB (p), UNIT))
+    {
+      a68_add_stmt (a68_lower_tree (SUB (p), ctx));
+    }
+  else if (IS (SUB (p), ENQUIRY_CLAUSE))
+    {
+      (void) a68_lower_tree (SUB (p), ctx);
+      gcc_assert (IS (NEXT (NEXT (SUB (p))), UNIT));
+      a68_add_stmt (a68_lower_tree (NEXT (NEXT (SUB (p))), ctx));
+    }
+  else
+    {
+      gcc_assert (IS (SUB (p), INITIALISER_SERIES));
+      gcc_assert (IS (NEXT (NEXT (SUB (p))), UNIT));
+      (void) a68_lower_tree (SUB (p), ctx);
+      a68_add_stmt (a68_lower_tree (NEXT (NEXT (SUB (p))), ctx));
+    }
+
+  return NULL_TREE;
+}
+
+/* Lower a conditional clause.
+
+     conditional clause : open part, choice, choice, close symbol;
+                          open part, choice, close symbol;
+                         open part, choice, brief elif part;
+                         if part, then part, else part, fi symbol;
+                         if part, then part, elif part;
+                         if part, then part, fi symbol.
+
+     if part : if symbol, enquiry clause;
+               if symbol, initialiser series.
+
+     then part : then symbol, serial clause;
+                 then symbol, initialiser series.
+
+     elif part : elif if part, then part, else part, fi symbol;
+                 elif if part, then part, fi symbol;
+                elif if part, then part, elif part.
+
+     else part : else symbol, serial clause;
+                 else symbol, initialiser series.
+
+     elif if part : elif symbol, enquiry clause.
+
+     open part : open symbol, enquiry clause.
+
+     choice : then bar symbol, serial clause;
+              then bar symbol initialiser series.
+
+     brief elif part : else open part, choice, choice, close symbol;
+                       else open part, choice, close symbol;
+                      else open part, choice, bief elif part.
+
+     else open part : else bar symbol, enquiry clause;
+                      else bar symbol, initialiser series.
+
+   Ranges:
+
+     IF enquiry-clause THEN expr ELSE expr FI
+                            --- R2    ---- R3
+     ---------------------------------------- R1
+
+   The conditional clause lowers into:
+
+     BIND_EXPR
+       BIND_EXPR_VARS -> delcls in enquiry clause.
+       BIND_EXPR_BODY
+         STMT_LIST
+           enquiry% = ...;
+           COND_EXPR (enquiry%, then_expr, else_expr)  */
+
+tree
+a68_lower_conditional_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree then_expr = NULL_TREE;
+  tree else_expr = NULL_TREE;
+
+  MOID_T *conditional_clause_mode = MOID (p);
+  MOID_T *effective_rows_mode = NO_MOID;
+  bool is_rows = false;
+
+  /* Push a binding environment for the conditional.  */
+  a68_push_range (is_rows ? effective_rows_mode : conditional_clause_mode);
+
+  /* Create a decl for %enquiry and add it to the bind's declaration chain.  */
+  tree enquiry_decl = build_decl (UNKNOWN_LOCATION,
+                                 VAR_DECL,
+                                 NULL, /* Set below.  */
+                                 a68_bool_type);
+  char *enquiry_name = xasprintf ("enquiry%d%%", DECL_UID(enquiry_decl));
+  DECL_NAME (enquiry_decl) = get_identifier (enquiry_name);
+  free (enquiry_name);
+  DECL_INITIAL (enquiry_decl) = a68_get_skip_tree (M_BOOL);
+  a68_add_decl (enquiry_decl);
+
+  /* Add a DECL_EXPR for enquiry_decl%  */
+  a68_add_stmt (fold_build1 (DECL_EXPR, a68_bool_type, enquiry_decl));
+
+  /* IF or ELIF part.  */
+  NODE_T *s = SUB (p);
+
+  /* Process the enquiry clause.  */
+  (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+
+  /* Assignation enquiry% = .. expr ..
+     Note that since no completers are allowed in enquiry clauses,
+     the last statement in the statement list has to be the unit
+     yielding the boolean value.  */
+  tree_stmt_iterator si = tsi_last (a68_range_stmt_list ());
+  gcc_assert (TREE_TYPE (tsi_stmt (si)) != void_type_node);
+  a68_add_stmt (fold_build2 (MODIFY_EXPR, a68_bool_type, enquiry_decl, 
tsi_stmt (si)));
+  tsi_delink (&si);
+
+  /* THEN part.  */
+  FORWARD (s);
+  bool dsa = serial_clause_dsa (NEXT (SUB (s)));
+  bool local = NON_LOCAL (NEXT (SUB (s))) == NO_TABLE;
+  a68_push_serial_clause_range (is_rows ? effective_rows_mode : 
conditional_clause_mode,
+                               dsa && local);
+  (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+  then_expr = a68_pop_serial_clause_range ();
+
+  /* ELSE part  */
+  FORWARD (s);
+  switch (ATTRIBUTE (s))
+    {
+    case CHOICE:
+    case ELSE_PART:
+      {
+       bool dsa = serial_clause_dsa (NEXT (SUB (s)));
+       bool local = NON_LOCAL (NEXT (SUB (s))) == NO_TABLE;
+       a68_push_serial_clause_range (is_rows ? effective_rows_mode : 
conditional_clause_mode,
+                                     dsa && local);
+       (void) a68_lower_tree (NEXT (SUB (s)), ctx);
+       else_expr = a68_pop_serial_clause_range ();
+       break;
+      }
+    case CLOSE_SYMBOL:
+    case FI_SYMBOL:
+      {
+       else_expr = a68_get_skip_tree (is_rows ? effective_rows_mode : 
conditional_clause_mode);
+       break;
+      }
+    default:
+      {
+       /* ELIF part.  Recurse.  */
+       MOID (s) = conditional_clause_mode;
+       else_expr = a68_lower_conditional_clause (s, ctx);
+      }
+    }
+
+  /* Build the conditional clause's COND_EXPR.  */
+  a68_add_stmt (fold_build3_loc (a68_get_node_location (p),
+                                COND_EXPR,
+                                CTYPE (is_rows ? effective_rows_mode : 
conditional_clause_mode),
+                                enquiry_decl,
+                                then_expr, else_expr));
+
+  return a68_pop_range ();
+}
+
+/* Lower a comma separated list of zero, two, or more units
+
+     unit list : unit list, comma symbol, unit;
+                 unit list, unit.
+
+   The list of units lowers into appending the units into the current
+   statements list.
+
+   This function always returns NULL_TREE, so the traversing code shall always
+   be careful to traverse on these nodes explicitly and ignore the returned
+   value.  */
+
+tree
+a68_lower_unit_list (NODE_T *p, LOW_CTX_T ctx)
+{
+  if (IS (SUB (p), UNIT_LIST))
+    (void) a68_lower_tree (SUB (p), ctx);
+
+  for (NODE_T *s = SUB (p); s != NO_NODE; FORWARD (s))
+    {
+      if (IS (s, UNIT))
+       a68_add_stmt (a68_lower_tree (s, ctx));
+    }
+
+  return NULL_TREE;
+}
+
+/* Lower a collateral clause.
+
+     collateral clause : open symbol, unit list, close symbol;
+                         open symbol, close symbol;
+                         begin symbol, unit list, end symbol;
+                        begin symbol, end symbol.
+
+   An empty collateral clause lowers into EMPTY.  */
+
+tree
+a68_lower_collateral_clause (NODE_T *p ATTRIBUTE_UNUSED,
+                            LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  bool clause_is_empty = (ATTRIBUTE (NEXT (SUB (p))) != UNIT_LIST);
+  MOID_T *mode = MOID (p);
+
+  /* Lower the constituent units into a statements list.  */
+  a68_push_stmt_list (mode);
+  if (!clause_is_empty)
+    (void) a68_lower_tree (NEXT (SUB (p)), ctx);
+  tree units = a68_pop_stmt_list ();
+
+  /* The collateral clause lowers to different constructions depending on its
+     nature.  */
+  if (mode == M_VOID)
+    {
+      /* A VOID-collateral-clause lowers into a STMT_LIST containing all
+        the units.  Since there cannot be declarations in a collateral
+        clause, there is no need to introduce a new binding scope.  Note
+        that for now we are not really elaborating collaterally, but
+        sequentially.  */
+      return units;
+    }
+  else if (IS_FLEXETY_ROW (mode) || mode == M_STRING)
+    {
+      if (mode == M_STRING)
+       mode = M_FLEX_ROW_CHAR;
+
+      /* This is a row display.  It lowers to a multiple.  */
+      tree row_type = CTYPE (mode);
+      size_t dim = DIM (DEFLEX (mode));
+
+      if (clause_is_empty)
+       {
+         /* The clause is empty.  This lowers into a multiple with DIM
+            dimension, each dimension having bounds of 1:0, and no
+            elements.  */
+         tree element_pointer_type = a68_row_elements_pointer_type (row_type);
+         tree multiple_elements = build_int_cst (element_pointer_type, 0);
+         tree multiple_elements_size = size_zero_node;
+
+         tree *lower_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+         tree *upper_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+         tree ssize_one_node = fold_convert (ssizetype, size_one_node);
+         tree ssize_zero_node = fold_convert (ssizetype, size_zero_node);
+         for (size_t d = 0; d < dim; ++d)
+           {
+             lower_bounds[d] = ssize_one_node;
+             upper_bounds[d] = ssize_zero_node;
+           }
+
+         tree row = a68_row_value (row_type, dim,
+                                   multiple_elements,
+                                   multiple_elements_size,
+                                   lower_bounds, upper_bounds);
+         TREE_CONSTANT (row) = 1;
+         free (lower_bounds);
+         free (upper_bounds);
+         return row;
+       }
+
+      if (dim == 1)
+       {
+         /* Create a constructor with the multiple's elements.  */
+         vec <constructor_elt, va_gc> *ve = NULL;
+         int num_units = 0;
+         for (tree_stmt_iterator si = tsi_start (units); !tsi_end_p (si); 
tsi_next (&si))
+           {
+             tree unit = tsi_stmt (si);
+             if (A68_TYPE_HAS_ROWS_P (TREE_TYPE (unit)))
+               unit = a68_low_dup (unit);
+             CONSTRUCTOR_APPEND_ELT (ve, size_int (num_units), unit);
+             num_units += 1;
+           }
+
+         tree element_pointer_type = a68_row_elements_pointer_type (row_type);
+         tree array_constructor_type = build_array_type (TREE_TYPE 
(element_pointer_type),
+                                                         build_index_type 
(size_int (num_units - 1)));
+         tree array_constructor = build_constructor (array_constructor_type, 
ve);
+         tree multiple_elements = fold_build1 (ADDR_EXPR,
+                                               element_pointer_type,
+                                               array_constructor);
+         tree elements_type = TREE_TYPE (element_pointer_type);
+         tree multiple_elements_size = fold_build2 (MULT_EXPR, sizetype,
+                                                    size_int (num_units),
+                                                    size_in_bytes 
(elements_type));
+         tree lower_bound = fold_convert (ssizetype, size_one_node);
+         tree upper_bound = ssize_int (num_units);
+         tree row = a68_row_value (row_type, dim,
+                                   multiple_elements,
+                                   multiple_elements_size,
+                                   &lower_bound, &upper_bound);
+         return row;
+       }
+      else
+       {
+         gcc_assert (dim > 1);
+
+         /* The units in the collateral clause are multiples, whose elements
+            are to be copied consecutively in a new multiple.  The descriptor
+            of this multiple is constructed as follows:
+
+            The first dimension is:
+
+            - The lower bound is 1.
+            - The upper bound is the number of sub-multiples processed
+              here.
+             - The stride is the number of elements in each sub-multiple
+              multiplied by the element size.
+
+            Subsequent dimensions are taken from the first inner multiple.
+            All descriptors of the inner multiples shall be equal.  This is
+            checked at run-time, and in case of discrepancy a run-time error
+            is emitted.
+
+             Let's see an example.  Suppose in the stmt-list we have:
+
+               (1, 2, 3)
+                 {triplets: {lb: 1 ub: 3 stride: 1S} elements: {1, 2, 3}}
+               (4, 5, 6)
+                 {triplets: {lb: 1 ub: 3 stride: 1S} elements: {4, 5, 6}}
+
+             The resulting multiple would be:
+
+               ((1, 2, 3), (4, 5, 6))
+                 {triplets: {{lb: 1 ub: 2 stride: 3S}, {lb: 1 ub: 3 stride: 
1S}}
+                  elements: {1, 2, 3, 4, 5, 6}}  */
+
+         tree *lower_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+         tree *upper_bounds = (tree *) xmalloc (sizeof (tree) * dim);
+         size_t num_units = 0;
+
+         for (tree_stmt_iterator si = tsi_start (units); !tsi_end_p (si); 
tsi_next (&si))
+           num_units++;
+
+         a68_push_range (mode);
+
+         /* Process each sub-multiple.  The first sub-multiple establishes the
+            bounds that all subsequent sub-multiples shall match.  */
+         tree multiple_elements = NULL_TREE;
+         tree multiple_elements_size = NULL_TREE;
+         tree sub_multiple = NULL_TREE;
+         //      tree sub_multiple_lb = NULL_TREE;
+         //      tree sub_multiple_ub = NULL_TREE;
+         //      tree sub_multiple_stride = NULL_TREE;
+         tree index = a68_lower_tmpvar ("index%", sizetype, size_zero_node);
+         for (tree_stmt_iterator si = tsi_start (units); !tsi_end_p (si); 
tsi_next (&si))
+           {
+             if (sub_multiple == NULL)
+               sub_multiple = a68_lower_tmpvar ("sub_multiple%",
+                                                TREE_TYPE (tsi_stmt (si)),
+                                                tsi_stmt (si));
+             else
+               a68_add_stmt (fold_build2 (MODIFY_EXPR,
+                                          TREE_TYPE (tsi_stmt (si)),
+                                          sub_multiple,
+                                          tsi_stmt (si)));
+
+             if (si == tsi_start (units))
+               {
+#if 0
+                 tree ssize_zero_node = fold_convert (ssizetype, 
size_zero_node);
+                 /* The first sub-multiple establishes the bounds that all
+                    subsequent sub-multiples shall match.  */
+                 sub_multiple_lb = a68_lower_tmpvar ("sub_multiple_lb%",
+                                                     ssizetype,
+                                                     a68_multiple_lower_bound 
(sub_multiple,
+                                                                               
ssize_zero_node));
+                 sub_multiple_ub = a68_lower_tmpvar ("sub_multiple_ub%",
+                                                     ssizetype,
+                                                     a68_multiple_upper_bound 
(sub_multiple,
+                                                                               
ssize_zero_node));
+                 sub_multiple_stride = a68_lower_tmpvar 
("sub_multiple_stride%",
+                                                         sizetype,
+                                                         a68_multiple_stride 
(sub_multiple,
+                                                                              
size_zero_node));
+#endif
+                 /* Now we have enough information to calculate the size of
+                    the elements of the new multiple and allocate
+                    multiple_elements.  */
+                 tree sub_multiple_elements = a68_multiple_elements 
(sub_multiple);
+                 tree elements_pointer_type = TREE_TYPE 
(sub_multiple_elements);
+                 tree elements_type = TREE_TYPE (elements_pointer_type);
+                 multiple_elements_size = fold_build2 (MULT_EXPR, sizetype,
+                                                            size_int 
(num_units),
+                                                            size_in_bytes 
(elements_type));
+                 multiple_elements_size = fold_build2 (MULT_EXPR, sizetype,
+                                                       multiple_elements_size,
+                                                       a68_multiple_num_elems 
(sub_multiple));
+                 multiple_elements = a68_lower_tmpvar ("multiple_elements%",
+                                                       elements_pointer_type,
+                                                       a68_lower_alloca 
(elements_type,
+                                                                         
multiple_elements_size));
+
+                 /* We can also now calculate the bounds of the new multiple.
+                    The top-level triplet has lower bound 1, upper bound is
+                    num_units, and stride is the number of elements in each
+                    sub-multiple multiplied by the element size.  Bounds for
+                    the subsequent DIM-1 dimensions are copied from the
+                    sub-multiple's descriptor.  */
+                 lower_bounds[0] = fold_convert (ssizetype, size_one_node);
+                 upper_bounds[0] = ssize_int (num_units);
+                 for (size_t d = 1; d < dim; ++d)
+                   {
+                     lower_bounds[d] = a68_multiple_lower_bound (sub_multiple,
+                                                                 ssize_int (d 
- 1));
+                     upper_bounds[d] = a68_multiple_upper_bound (sub_multiple,
+                                                                 ssize_int (d 
- 1));
+                   }
+               }
+             else
+               {
+                 /* Check bounds of this sub-multiple.  Note that this is
+                    always done at run-time, since the interpretation of a row
+                    display depens on the target type, whether it is a row row
+                    or a row of rows, for example.  */
+                 // XXX use sub_multiple_lb, sub_multiple_ub and 
sub_multiple_stride
+               }
+
+             /* Copy the elements of a copy of the sub-multiple in the
+                elements of the multiple.  */
+             tree sub_multiple_elements = a68_multiple_elements (sub_multiple);
+             // XXX should we make a copy of the sub_multiple_elements here?
+             // We DO need to iterate slicing, because of strides: if
+             // the sub_multiple is a trimmer.
+             sub_multiple_elements = sub_multiple_elements;
+             tree sub_multiple_elements_type = TREE_TYPE 
(sub_multiple_elements);
+             tree sub_multiple_num_elems = a68_multiple_num_elems 
(sub_multiple);
+             tree sub_multiple_element_type = TREE_TYPE 
(sub_multiple_elements_type);
+             tree sub_multiple_elements_size = fold_build2 (MULT_EXPR, 
sizetype,
+                                                            
sub_multiple_num_elems,
+                                                            size_in_bytes 
(sub_multiple_element_type));
+
+             /* memcpy (multiple_elements[index], sub_multiple_elements)  */
+             a68_add_stmt (a68_lower_memcpy (fold_build2 (POINTER_PLUS_EXPR,
+                                                          
sub_multiple_elements_type,
+                                                          multiple_elements,
+                                                          index),
+                                             sub_multiple_elements,
+                                             sub_multiple_elements_size));
+             /* index += sub_multiple_elements_size */
+             a68_add_stmt (fold_build2 (MODIFY_EXPR, sizetype,
+                                        index,
+                                        fold_build2 (PLUS_EXPR, sizetype,
+                                                     index, 
sub_multiple_elements_size)));
+           }
+
+         tree multiple = a68_lower_tmpvar ("multiple%",
+                                           row_type,
+                                           a68_row_value (row_type, dim,
+                                                          multiple_elements,
+                                                          
multiple_elements_size,
+                                                          lower_bounds, 
upper_bounds));
+         free (lower_bounds);
+         free (upper_bounds);
+
+         /* Yield the multiple.  */
+         a68_add_stmt (multiple);
+         return a68_pop_range ();
+       }
+    }
+  else if (IS_STRUCT (mode))
+    {
+      /* This is a struct display.  There are as many units in the clause as
+        fields in the struct type.  Build a constructor with the values for
+        the fields.  */
+      vec <constructor_elt, va_gc> *ve = NULL;
+      tree_stmt_iterator si = tsi_start (units);
+      for (tree f = TYPE_FIELDS (CTYPE (mode)); f; f = DECL_CHAIN (f))
+       {
+         tree v = tsi_stmt (si);
+         gcc_assert (v != NULL_TREE);
+         v = a68_consolidate_ref (a68_type_moid (TREE_TYPE (f)) ,v);
+         CONSTRUCTOR_APPEND_ELT (ve, f, v);
+         tsi_next (&si);
+       }
+      tree ctor = build_constructor (CTYPE (mode), ve);
+      return ctor;
+    }
+  else
+    gcc_unreachable ();
+}
+
+/* Lower a parallel clause.
+
+     parallel clause : par symbol, collateral clause.
+*/
+
+tree
+a68_lower_parallel_clause (NODE_T *p ATTRIBUTE_UNUSED,
+                          LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  /* XXX For now treat like a VOID collateral clause.  */
+  return a68_lower_tree (NEXT (SUB (p)), ctx);
+}
+
+/* Lower a closed clause.
+
+     closed clause : open symbol, serial clause, close symbol;
+                     open symbol, initialiser series, close symbol;
+                    begin symbol, serial clause, end symbol;
+                    begin symbol, initialiser series, end symbol;
+
+  This function returns a BIND_EXPR.  */
+
+tree
+a68_lower_closed_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  /* Determine the mode of the closed clause.  */
+  MOID_T *clause_mode = MOID (p);
+  gcc_assert (clause_mode != NO_MOID);
+  gcc_assert (CTYPE (clause_mode) != NULL_TREE);
+
+  /* Lower the enclosed serial clause.
+
+     Note that a serial clause can be nested right inside another, and in that
+     case the range we are pushing corresponds to all of them, so we have to
+     keep this into account when determining whether using a DSA serial
+     range.  */
+
+  bool dsa = serial_clause_dsa (NEXT (SUB (p)));
+  bool local = NON_LOCAL (NEXT (SUB (p))) == NO_TABLE;
+  a68_push_serial_clause_range (clause_mode, dsa && local);
+  (void) a68_lower_tree (NEXT (SUB (p)), ctx);
+  return a68_pop_serial_clause_range ();
+}
+
+/* Lower an enclosed clause.
+
+     enclosed clause : parallel clause; closed clause;
+                       collateral clause; conditional clause;
+                      case clause; conformity clause;
+                      loop clause.
+*/
+
+tree
+a68_lower_enclosed_clause (NODE_T *p, LOW_CTX_T ctx)
+{
+  return a68_lower_tree (SUB (p), ctx);
+}
diff --git a/gcc/algol68/a68-low-decls.cc b/gcc/algol68/a68-low-decls.cc
new file mode 100644
index 00000000000..afc7284bd79
--- /dev/null
+++ b/gcc/algol68/a68-low-decls.cc
@@ -0,0 +1,629 @@
+/* Lower mode, identity and variable declarations to GENERIC.
+   Copyright (C) 2025 Jose E. Marchesi.
+
+   Written by Jose E. Marchesi.
+
+   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/>.  */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "tree.h"
+#include "fold-const.h"
+#include "diagnostic.h"
+#include "langhooks.h"
+#include "tm.h"
+#include "function.h"
+#include "cgraph.h"
+#include "toplev.h"
+#include "varasm.h"
+#include "predict.h"
+#include "stor-layout.h"
+#include "tree-iterator.h"
+#include "stringpool.h"
+#include "print-tree.h"
+#include "gimplify.h"
+#include "dumpfile.h"
+#include "convert.h"
+
+#include "a68.h"
+
+/* Lower one or more mode declarations.
+
+     mode declaration : mode symbol, defining indicant,
+                        equals symbol, declarer;
+                        mode symbol, defining indicant,
+                       equals symbol, void symbol;
+                        mode declaration, comma symbol,
+                       defining indicant, equals symbol, declarer;
+                        mode declaration, comma symbol,
+                       defining indicant, equals symbol, void symbol.
+
+   Each mode declaration lowers into a TYPE_DECL, which are chained in the
+   current block.  This function returns void_node.
+
+   Note that the defining indicant is already annotated with the declared mode
+   so there is no need to go hunting for the declarer in the subtree.  */
+
+tree
+a68_lower_mode_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  NODE_T *defining_indicant = NO_NODE;
+
+  if (IS (SUB (p), MODE_DECLARATION))
+    {
+      a68_lower_tree (SUB (p), ctx);
+      defining_indicant = NEXT (NEXT (SUB (p)));
+    }
+  else
+    {
+      gcc_assert (IS (SUB (p), MODE_SYMBOL));
+      defining_indicant = NEXT (SUB (p));
+    }
+
+  /* Create a TYPE_DECL declaration for the defined mode and chain it in the
+     current block.  */
+  tree ctype = CTYPE (MOID (defining_indicant));
+  tree decl_name = a68_get_mangled_identifier (NSYMBOL (defining_indicant));
+  tree decl = build_decl (a68_get_node_location (p),
+                         TYPE_DECL, decl_name, ctype);
+  SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+  TREE_PUBLIC (decl) = 1;
+  TYPE_CONTEXT (ctype) = DECL_CONTEXT (decl);
+  TYPE_NAME (ctype) = decl;
+  TYPE_STUB_DECL (ctype) = decl;
+  a68_add_decl (decl);
+
+  return void_node;
+}
+
+/* Lower one or more variable declarations.
+
+     variable declaration : qualifier, declarer, defining identifier,
+                            assign symbol, unit;
+                           qualifier, declarer, defining identiifer;
+                           qualifier, declarer, defining identifier;
+                           declarer, defining identifier, assign symbol, unit;
+                           declarer, defining identifier;
+                           variable declaration, comma symbol,
+                           defining identifier, assign symbol, unit;
+                           variable declaration, comma symbol,
+                           defining identifier;
+
+  Each variable declaration lowers into a VAR_DECL, which are chained in the
+  current block.  This function also returns an expression with code to
+  initialize the variable in case there is an initializer.
+
+  If the variable declaration implies a LOC generator then the VAR_DECL for REF
+  AMODE declares a value of type CTYPE (AMODE).  This is an optimization in
+  order to avoid indirect addressing.  If the variable declaration implies a
+  HEAP generator, however, then the VAR_DECL declares a value of type pointer
+  to CTYPE (AMODE0.  In this later case no optimization is possible and it has
+  exactly the same effect than an identity declaration `REF AMODE
+  defining_identifier = HEAP AMODE'.
+
+  Note that the defining identifier is annotated with its mode, so there is no
+  need to go hunting for the declarer in the subtree.  */
+
+tree
+a68_lower_variable_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  NODE_T *defining_identifier, *unit;
+  NODE_T *declarer = NO_NODE;
+
+  tree sub_expr = NULL_TREE;
+
+  if (IS (SUB (p), VARIABLE_DECLARATION))
+    {
+      LOW_CTX_T new_ctx = ctx;
+      new_ctx.declarer = &declarer;
+      sub_expr = a68_lower_tree (SUB (p), new_ctx);
+      defining_identifier = NEXT (NEXT (SUB (p)));
+    }
+  else if (IS (SUB (p), QUALIFIER))
+    {
+      /* The qualifier determines what kind of generator is used in the
+        variable declaration.  This is already annotated in the tax entry for
+        the definining identifier.  */
+      declarer = NEXT (SUB (p));
+      defining_identifier = NEXT (NEXT (SUB (p)));
+    }
+  else if (IS (SUB (p), DECLARER))
+    {
+      declarer = SUB (p);
+      defining_identifier = NEXT (SUB (p));
+    }
+  else
+    gcc_unreachable ();
+
+  /* Communicate declarer upward.  */
+  if (ctx.declarer != NULL)
+    *ctx.declarer = declarer;
+
+  /* See if this variable declaration features an initializing unit.  */
+  if (NEXT (defining_identifier) != NO_NODE)
+    {
+      gcc_assert (NEXT (defining_identifier)
+                 && IS (NEXT (defining_identifier), ASSIGN_SYMBOL)
+                 && NEXT (NEXT (defining_identifier)));
+      unit = NEXT (NEXT (defining_identifier));
+    }
+  else
+    unit = NO_NODE;
+
+  /* If not done already by an applied identifier in lower_identifier, create a
+     declaration for the defined entity and chain it in the current block.  The
+     declaration has an initial value of SKIP.  */
+  tree var_decl = TAX_TREE_DECL (TAX (defining_identifier));
+  if (var_decl == NULL_TREE)
+    {
+      var_decl = a68_make_variable_declaration_decl (defining_identifier);
+      TAX_TREE_DECL (TAX (defining_identifier)) = var_decl;
+    }
+
+  /* Chain declaration in current block and bind.  */
+  a68_add_decl (var_decl);
+
+  /* Add a decl_expr in the current range.  */
+  a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+                                     DECL_EXPR,
+                                     TREE_TYPE (var_decl),
+                                     var_decl));
+
+  tree expr = NULL_TREE;
+
+  /* If the variable is heap allocated or has rows, the var_decl created above
+     is a pointer.  Run a generator to get the memory with descriptors filled
+     in.  Note that we cannot set the pointer as the initial of the var_decl
+     because the bounds in the actual declarer shall be elaborated at the point
+     of the code where the declaration appears, not at the beginning of its
+     reach.  Note that the mode of the declarer will be always a REF since this
+     is a variable declaration: the referred mode is what we pass to
+     a68_low_generator.  */
+  bool heap = HEAP (TAX (defining_identifier)) == HEAP_SYMBOL;
+  if (heap || HAS_ROWS (SUB (MOID (defining_identifier))))
+    {
+      gcc_assert(IS_REF (MOID (declarer)));
+      expr = fold_build2 (MODIFY_EXPR, TREE_TYPE (var_decl),
+                         var_decl,
+                         a68_low_generator (declarer,
+                                            SUB (MOID (declarer)),
+                                            heap, ctx));
+    }
+
+  if (unit != NO_NODE)
+    {
+      tree rhs = a68_lower_tree (unit, ctx);
+      tree assignation = a68_low_assignation (p,
+                                             var_decl, MOID 
(defining_identifier),
+                                             rhs, MOID (unit));
+      if (expr != NULL_TREE)
+       expr = fold_build2_loc (a68_get_node_location (p),
+                               COMPOUND_EXPR,
+                               TREE_TYPE (assignation),
+                               expr, assignation);
+      else
+       expr = assignation;
+    }
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_expr != NULL_TREE)
+    {
+      if (expr != NULL_TREE)
+       expr = fold_build2_loc (a68_get_node_location (p),
+                               COMPOUND_EXPR,
+                               TREE_TYPE (var_decl),
+                               sub_expr,
+                               expr);
+      else
+       expr = sub_expr;
+    }
+
+  return expr;
+}
+
+/* Lower one or more identity declarations.
+
+     identity declaration : declarer, defining identifier,
+                            equals symbol, unit;
+                            identity declaration, comma symbol,
+                           defining identifier, equals symbol, unit;
+
+   Each identity declaration lowers into a declaration.
+
+   VAR_DECL with both TREE_CONSTANT and TREE_READONLY set.  Note that we cannot
+   use CONST_DECL because of two reasons.  First, CONST_DECL only works for
+   scalar modes.  Second, since Algol 68 allows usage of identifiers before
+   they get declared, each declaration adds a declaration with a SKIP initial
+   value, and also an assignation of the value at the declaration point.  This
+   function also returns an expression with code to initialize the declared
+   constant.  */
+
+tree
+a68_lower_identity_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree unit_tree = NULL_TREE;
+  tree sub_expr = NULL_TREE;
+
+  /* Note that the formal declarer in the construct is not used.  This is
+     because it is already reflected in the mode of the identity
+     declaration.  */
+
+  NODE_T *defining_identifier;
+  if (IS (SUB (p), IDENTITY_DECLARATION))
+    {
+      sub_expr = a68_lower_tree (SUB (p), ctx);
+      defining_identifier = NEXT (NEXT (SUB (p)));
+    }
+  else if (IS (SUB (p), DECLARER))
+    {
+      defining_identifier = NEXT (SUB (p));
+    }
+  else
+    gcc_unreachable ();
+
+  NODE_T *unit = NEXT (NEXT (defining_identifier));
+
+  /* If not done already by an applied identifier in lower_identifier, create a
+     declaration for the defined entity and chain it in the current block.  The
+     declaration has an initial value of SKIP.  */
+  tree id_decl = TAX_TREE_DECL (TAX (defining_identifier));
+  if (id_decl == NULL_TREE)
+    {
+      id_decl = a68_make_identity_declaration_decl (defining_identifier);
+      TAX_TREE_DECL (TAX (defining_identifier)) = id_decl;
+    }
+
+  /* Chain declaration in current block and bind.  */
+  a68_add_decl (id_decl);
+  /* Prepare the DECL_EXPR.  */
+  a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+                                     DECL_EXPR,
+                                     TREE_TYPE (id_decl),
+                                     id_decl));
+
+  unit_tree = a68_lower_tree (unit, ctx);
+  unit_tree = a68_consolidate_ref (MOID (unit), unit_tree);
+  tree expr = a68_low_ascription (MOID (defining_identifier),
+                                 id_decl, unit_tree);
+
+  /* If the ascribed value is constant, mark the declaration as constant.  */
+  TREE_CONSTANT (id_decl) = TREE_CONSTANT (unit_tree);
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_expr != NULL_TREE)
+    {
+      if (expr != NULL_TREE)
+       expr = fold_build2_loc (a68_get_node_location (p),
+                               COMPOUND_EXPR,
+                               TREE_TYPE (id_decl),
+                               sub_expr,
+                               expr);
+      else
+       expr = sub_expr;
+    }
+
+  return expr;
+}
+
+/* Lower a declarer.
+
+     declarer : indicant;
+               longety, indicant;
+               shortety, indicant;
+               flex symbol, declarer;
+               flex symbol, bounds, declarer;
+               flex symbol, formal bounds, declarer;
+               bounds, declarer;
+               formal bounds, declarer;
+                ref symbol, declarer;
+               struct symbol, structure pack;
+               union symbol, union pack;
+                proc symbol, declarer;
+               proc symbol, formal declarers, declarer;
+               proc symbol, formal declarers, void symbol;
+
+
+  This handler lowes a DECLARER tree into an expression that evaluates to the
+  size of the actual declarer.  Note that this is a self-contained handler and
+  it does traverse the sub-tree on its own.  */
+
+tree
+a68_lower_declarer (NODE_T *p ATTRIBUTE_UNUSED,
+                   LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
+/* Lower a declaration list.
+
+    declaration list : mode declaration;
+                       priority declaration;
+                       brief operator declaration;
+                       operator declaration;
+                       identity declaration;
+                       procedure declaration;
+                       procedure variable declaration;
+                       variable declaration;
+                       environ name;
+                      declaration list, comma symbol, declaration list;
+
+   Process the subtree, which produces declarations associated with the current
+   context and which get added to the current block.  The list of declarations
+   gets returned in nested compound expressions.  */
+
+tree
+a68_lower_declaration_list (NODE_T *p, LOW_CTX_T ctx)
+{
+  if (IS (SUB (p), DECLARATION_LIST))
+    {
+      tree left = a68_lower_tree (SUB (p), ctx);
+      tree right = a68_lower_tree (NEXT (NEXT (SUB (p))), ctx);
+
+      /* The trees `left' and `right' may be NULL_TREE if the declarations
+        under them didn't have an initializing expression.  In that case,
+        replace them by nops which are removed at fold time.  This is ugly,
+        but works.  */
+      if (left == NULL_TREE)
+       left = integer_zero_node;
+      if (right == NULL_TREE)
+       right = integer_zero_node;
+
+      return fold_build2_loc (a68_get_node_location (p),
+                             COMPOUND_EXPR,
+                             void_type_node,
+                             left, right);
+    }
+  else
+    return a68_lower_tree (SUB (p), ctx);
+}
+
+/* Lower a procedure declaration.
+
+     procedure declaration : proc symbol, defining identifier, assign symbol, 
routine text;
+                             procedure declaration, comma symbol,
+                            defining identifier, equals symbol, routine text.
+
+   Each procedure declaration lowers into a declaration.  */
+
+tree
+a68_lower_procedure_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree sub_func_decl = NULL_TREE;
+  NODE_T *defining_identifier;
+  if (IS (SUB (p), PROCEDURE_DECLARATION))
+    {
+      sub_func_decl = a68_lower_tree (SUB (p), ctx);
+      defining_identifier = NEXT (NEXT (SUB (p)));
+    }
+  else if (IS (SUB (p), PROC_SYMBOL))
+    {
+      defining_identifier = NEXT (SUB (p));
+    }
+  else
+    gcc_unreachable ();
+
+  NODE_T *routine_text = NEXT (NEXT (defining_identifier));
+
+  /* Lower the routine text to get a function decl.  */
+  ctx.proc_decl_identifier = defining_identifier;
+  tree func_decl = a68_lower_tree (routine_text, ctx);
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_func_decl != NULL_TREE)
+    {
+      if (func_decl != NULL_TREE)
+       func_decl = fold_build2_loc (a68_get_node_location (p),
+                                    COMPOUND_EXPR,
+                                    TREE_TYPE (func_decl),
+                                    sub_func_decl,
+                                    func_decl);
+      else
+       func_decl = sub_func_decl;
+    }
+
+  return func_decl;
+}
+
+/* Lower a procedure variable declaration.
+
+     procedure variable declaration
+       : proc symbol, defining identifier, assign symbol, routine text;
+         qualifier, proc symbol, defining identifier, assign symbol, routine 
text;
+        procedure variable declaration, comma symbol, defining identiier, 
assign symbol, routine text.
+
+   This lowers into the declaration of a VAR_DECL which is a pointer to the
+   free standing routine yielded by the routine text.  */
+
+tree
+a68_lower_procedure_variable_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree sub_decl = NULL_TREE;
+  NODE_T *defining_identifier;
+  if (IS (SUB (p), PROCEDURE_VARIABLE_DECLARATION))
+    {
+      sub_decl = a68_lower_tree (SUB (p), ctx);
+      defining_identifier = NEXT (NEXT (SUB (p)));
+    }
+  else if (IS (SUB (p), PROC_SYMBOL))
+    defining_identifier = NEXT (SUB (p));
+  else if (IS (SUB (p), QUALIFIER))
+    /* The qualifier determines what kind of generator is used in the variable
+       declaration.  This is already annotated in the tax entry for the
+       definining identifier.  */
+    defining_identifier = NEXT (NEXT (SUB (p)));
+  else
+    gcc_unreachable ();
+  NODE_T *routine_text = NEXT (NEXT (defining_identifier));
+
+  /* The routine text lowers into a pointer to function.  */
+  ctx.proc_decl_identifier = NO_NODE;
+  tree routine = a68_lower_tree (routine_text, ctx);
+
+  /* Create a declaration for the proc variable, if that hasn't been done
+     already.  */
+  tree decl = TAX_TREE_DECL (TAX (defining_identifier));
+  if (decl == NULL_TREE)
+    {
+      decl = a68_make_variable_declaration_decl (defining_identifier);
+      TAX_TREE_DECL (TAX (defining_identifier)) = decl;
+    }
+
+  /* Chain declaration in current block and bind.  */
+  a68_add_decl (decl);
+  /* Add a decl_expr in the current range.  */
+  a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+                                     DECL_EXPR,
+                                     TREE_TYPE (decl),
+                                     decl));
+  /* Initialize.
+
+     If the variable is heap allocated then the var_decl created above is a
+     pointer.  We don't allocate the actual function on the heap, because the
+     scope of procedures is not global.  */
+  bool heap = HEAP (TAX (defining_identifier)) == HEAP_SYMBOL;
+  a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (decl), decl,
+                            heap ? fold_build1 (ADDR_EXPR, TREE_TYPE (decl),
+                                                routine) : routine));
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_decl != NULL_TREE)
+    {
+      if (decl != NULL_TREE)
+       decl = fold_build2_loc (a68_get_node_location (p),
+                               COMPOUND_EXPR,
+                               TREE_TYPE (decl),
+                               sub_decl,
+                               decl);
+      else
+       decl = sub_decl;
+    }
+
+  return decl;
+}
+
+/* Lower a priority declaration.
+
+   This lowers to nothing.  Operator priority is fully handled by the parser in
+   order to decide which operator declaration corresponds to each applied
+   operator.  */
+
+tree
+a68_lower_priority_declaration (NODE_T *p ATTRIBUTE_UNUSED,
+                               LOW_CTX_T ctx ATTRIBUTE_UNUSED)
+{
+  return NULL_TREE;
+}
+
+/* Lower a brief operator declaration.
+
+     brief operator declaration
+       : op symbol, defining operator, equals symbol, routine text;
+         brief operator declaration, comma symbol, defining operator, equals 
symbol, routine text.
+
+   The declarations low in a series of FUNCTION_DECLs, one per declared
+   operator.  */
+
+tree
+a68_lower_brief_operator_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree sub_func_decl = NULL_TREE;
+  NODE_T *defining_operator;
+
+  if (IS (SUB (p), BRIEF_OPERATOR_DECLARATION))
+    {
+      sub_func_decl = a68_lower_tree (SUB (p), ctx);
+      defining_operator = NEXT (NEXT (SUB (p)));
+    }
+  else
+    defining_operator = NEXT (SUB (p));
+  NODE_T *routine_text = NEXT (NEXT (defining_operator));
+
+  /* Lower the routine text to get a function decl.  */
+  ctx.proc_decl_identifier = defining_operator;
+  tree func_decl = a68_lower_tree (routine_text, ctx);
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_func_decl != NULL_TREE)
+    {
+      if (func_decl != NULL_TREE)
+       func_decl = fold_build2_loc (a68_get_node_location (p),
+                                    COMPOUND_EXPR,
+                                    TREE_TYPE (func_decl),
+                                    sub_func_decl,
+                                    func_decl);
+      else
+       func_decl = sub_func_decl;
+    }
+
+  return func_decl;
+}
+
+/* Lower an operator declaration.
+
+     operator declaration : operator plan, defining operator, equals symbol, 
unit;
+                            operator declaration, comma symbol, defining 
operator, equals symbol, unit.
+
+   Each operator declaration lowers into a declaration.  */
+
+tree
+a68_lower_operator_declaration (NODE_T *p, LOW_CTX_T ctx)
+{
+  tree sub_op_decl = NULL_TREE;
+  NODE_T *defining_operator;
+
+  if (IS (SUB (p), OPERATOR_DECLARATION))
+    {
+      sub_op_decl = a68_lower_tree (SUB (p), ctx);
+      defining_operator = NEXT (NEXT (SUB (p)));
+    }
+  else
+    defining_operator = NEXT (SUB (p));
+  NODE_T *unit = NEXT (NEXT (defining_operator));
+
+  tree op_decl = TAX_TREE_DECL (TAX (defining_operator));
+  if (op_decl == NULL_TREE)
+    {
+      op_decl = a68_make_identity_declaration_decl (defining_operator);
+      TAX_TREE_DECL (TAX (defining_operator)) = op_decl;
+    }
+
+  /* Chain declaration in current block and bind and emit DECL_EXPR.  */
+  a68_add_decl (op_decl);
+  a68_add_decl_expr (fold_build1_loc (a68_get_node_location (p),
+                                     DECL_EXPR,
+                                     TREE_TYPE (op_decl),
+                                     op_decl));
+  /* Initialize.  */
+  a68_add_stmt (fold_build2 (MODIFY_EXPR, TREE_TYPE (op_decl), op_decl,
+                            a68_lower_tree (unit, ctx)));
+
+  /* Tail in a compound expression with sub declarations, if any.  */
+  if (sub_op_decl != NULL_TREE)
+    {
+      if (op_decl != NULL_TREE)
+       op_decl = fold_build2_loc (a68_get_node_location (p),
+                                  COMPOUND_EXPR,
+                                  TREE_TYPE (op_decl),
+                                  sub_op_decl,
+                                  op_decl);
+      else
+       op_decl = sub_op_decl;
+    }
+
+  return op_decl;
+}
-- 
2.30.2

Reply via email to