/* Miscellaneous MOID routines. Copyright (C) 2001-2023 J. Marcel van der Veer. Copyright (C) 2025 Jose E. Marchesi. Original implementation by J. Marcel van der Veer. Adapted for GCC 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "options.h" #include "a68.h" /* * MODE checker routines. */ /* Absorb nested series modes recursively. */ void a68_absorb_series_pack (MOID_T **p) { bool siga; do { PACK_T *z = NO_PACK; siga = false; for (PACK_T *t = PACK (*p); t != NO_PACK; FORWARD (t)) { if (MOID (t) != NO_MOID && IS (MOID (t), SERIES_MODE)) { siga = true; for (PACK_T *s = PACK (MOID (t)); s != NO_PACK; FORWARD (s)) a68_add_mode_to_pack (&z, MOID (s), NO_TEXT, NODE (s)); } else a68_add_mode_to_pack (&z, MOID (t), NO_TEXT, NODE (t)); } PACK (*p) = z; } while (siga); } /* Make SERIES (u, v). */ MOID_T * a68_make_series_from_moids (MOID_T *u, MOID_T *v) { MOID_T *x = a68_new_moid (); ATTRIBUTE (x) = SERIES_MODE; a68_add_mode_to_pack (&(PACK (x)), u, NO_TEXT, NODE (u)); a68_add_mode_to_pack (&(PACK (x)), v, NO_TEXT, NODE (v)); a68_absorb_series_pack (&x); DIM (x) = a68_count_pack_members (PACK (x)); (void) a68_register_extra_mode (&TOP_MOID (&A68_JOB), x); if (DIM (x) == 1) return MOID (PACK (x)); else return x; } /* Absorb firmly related unions in mode. For instance invalid UNION (PROC REF UNION (A, B), A, B) -> valid UNION (A, B), which is used in balancing conformity clauses. */ MOID_T * a68_absorb_related_subsets (MOID_T * m) { /* For instance invalid UNION (PROC REF UNION (A, B), A, B) -> valid UNION (A, B), which is used in balancing conformity clauses. */ bool siga; do { PACK_T *u = NO_PACK; siga = false; for (PACK_T *v = PACK (m); v != NO_PACK; FORWARD (v)) { MOID_T *n = a68_depref_completely (MOID (v)); if (IS (n, UNION_SYMBOL) && a68_is_subset (n, m, SAFE_DEFLEXING)) { /* Unpack it. */ for (PACK_T *w = PACK (n); w != NO_PACK; FORWARD (w)) a68_add_mode_to_pack (&u, MOID (w), NO_TEXT, NODE (w)); siga = true; } else a68_add_mode_to_pack (&u, MOID (v), NO_TEXT, NODE (v)); } PACK (m) = a68_absorb_union_pack (u); } while (siga); return m; } /* Absorb nested series and united modes recursively. */ void a68_absorb_series_union_pack (MOID_T **p) { bool siga; do { PACK_T *z = NO_PACK; siga = false; for (PACK_T *t = PACK (*p); t != NO_PACK; FORWARD (t)) { if (MOID (t) != NO_MOID && (IS (MOID (t), SERIES_MODE) || IS (MOID (t), UNION_SYMBOL))) { siga = true; for (PACK_T *s = PACK (MOID (t)); s != NO_PACK; FORWARD (s)) a68_add_mode_to_pack (&z, MOID (s), NO_TEXT, NODE (s)); } else a68_add_mode_to_pack (&z, MOID (t), NO_TEXT, NODE (t)); } PACK (*p) = z; } while (siga); } /* Make united mode, from mode that is a SERIES (..). */ MOID_T * a68_make_united_mode (MOID_T *m) { if (m == NO_MOID) return M_ERROR; else if (ATTRIBUTE (m) != SERIES_MODE) return m; /* Do not unite a single UNION. */ if (DIM (m) == 1 && IS (MOID (PACK (m)), UNION_SYMBOL)) return MOID (PACK (m)); /* Straighten the series. */ a68_absorb_series_union_pack (&m); /* Copy the series into a UNION. */ MOID_T *u = a68_new_moid (); ATTRIBUTE (u) = UNION_SYMBOL; PACK (u) = NO_PACK; for (PACK_T *w = PACK (m); w != NO_PACK; FORWARD (w)) a68_add_mode_to_pack (&(PACK (u)), MOID (w), NO_TEXT, NODE (m)); /* Absorb and contract the new UNION. */ a68_absorb_series_union_pack (&u); DIM (u) = a68_count_pack_members (PACK (u)); PACK (u) = a68_absorb_union_pack (PACK (u)); a68_contract_union (u); DIM (u) = a68_count_pack_members (PACK (u)); /* A UNION of one mode is that mode itself. */ if (DIM (u) == 1) return MOID (PACK (u)); else return a68_register_extra_mode (&TOP_MOID (&A68_JOB), u); } /* Make SOID data structure. */ void a68_make_soid (SOID_T *s, int sort, MOID_T *type, int attribute) { ATTRIBUTE (s) = attribute; SORT (s) = sort; MOID (s) = type; CAST (s) = false; } /* Whether mode is not well defined. */ bool a68_is_mode_isnt_well (MOID_T *p) { if (p == NO_MOID) return true; else if (!A68_IF_MODE_IS_WELL (p)) return true; else if (PACK (p) != NO_PACK) { for (PACK_T *q = PACK (p); q != NO_PACK; FORWARD (q)) { if (!A68_IF_MODE_IS_WELL (MOID (q))) return true; } } return false; } /* Add SOID data to free chain. */ void a68_free_soid_list (SOID_T *root) { if (root != NO_SOID) { SOID_T *q = root; for (; NEXT (q) != NO_SOID; FORWARD (q)) ; NEXT (q) = A68 (top_soid_list); A68 (top_soid_list) = root; } } /* Add SOID data structure to soid list. */ void a68_add_to_soid_list (SOID_T **root, NODE_T *where, SOID_T *soid) { if (*root != NO_SOID) a68_add_to_soid_list (&(NEXT (*root)), where, soid); else { SOID_T *new_one; if (A68 (top_soid_list) == NO_SOID) new_one = (SOID_T *) ggc_cleared_alloc (); else { new_one = A68 (top_soid_list); FORWARD (A68 (top_soid_list)); } a68_make_soid (new_one, SORT (soid), MOID (soid), 0); NODE (new_one) = where; NEXT (new_one) = NO_SOID; *root = new_one; } } /* Pack soids in moid, gather resulting moids from terminators in a clause. */ MOID_T * a68_pack_soids_in_moid (SOID_T *top_sl, int attribute) { MOID_T *x = a68_new_moid (); PACK_T *t, **p; ATTRIBUTE (x) = attribute; DIM (x) = 0; SUB (x) = NO_MOID; EQUIVALENT (x) = NO_MOID; SLICE (x) = NO_MOID; DEFLEXED (x) = NO_MOID; NAME (x) = NO_MOID; NEXT (x) = NO_MOID; PACK (x) = NO_PACK; p = &(PACK (x)); for (; top_sl != NO_SOID; FORWARD (top_sl)) { t = a68_new_pack (); MOID (t) = MOID (top_sl); TEXT (t) = NO_TEXT; NODE (t) = NODE (top_sl); NEXT (t) = NO_PACK; DIM (x)++; *p = t; p = &NEXT (t); } (void) a68_register_extra_mode (&TOP_MOID (&A68_JOB), x); return x; } /* Whether P is compatible with Q. */ bool a68_is_equal_modes (MOID_T *p, MOID_T *q, int deflex) { if (deflex == FORCE_DEFLEXING) return DEFLEX (p) == DEFLEX (q); else if (deflex == ALIAS_DEFLEXING) { if (IS (p, REF_SYMBOL) && IS (q, REF_SYMBOL)) return (p == q || a68_prove_moid_equivalence (p, q) || a68_prove_moid_equivalence (DEFLEX (p), q) || DEFLEX (p) == q); else if (!IS (p, REF_SYMBOL) && !IS (q, REF_SYMBOL)) return (DEFLEX (p) == DEFLEX (q) || a68_prove_moid_equivalence (DEFLEX (p), DEFLEX (q))); } else if (deflex == SAFE_DEFLEXING) { if (!IS (p, REF_SYMBOL) && !IS (q, REF_SYMBOL)) return (DEFLEX (p) == DEFLEX (q) || a68_prove_moid_equivalence (DEFLEX (p), DEFLEX (q))); } return (p == q || a68_prove_moid_equivalence (p, q)); } /* Whether mode is deprefable, i.e. whether it can be either deferred or deprocedured. */ bool a68_is_deprefable (MOID_T *p) { if (IS_REF (p)) return true; else return (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK); } /* Deref or deproc the mode P once. */ MOID_T * a68_depref_once (MOID_T *p) { if (IS_REF_FLEX (p)) return SUB_SUB (p); else if (IS_REF (p)) return SUB (p); else if (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK) return SUB (p); else return NO_MOID; } /* Depref mode completely. */ MOID_T * a68_depref_completely (MOID_T *p) { while (a68_is_deprefable (p)) p = a68_depref_once (p); return p; } /* Deproc_completely. */ MOID_T * a68_deproc_completely (MOID_T *p) { while (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK) p = a68_depref_once (p); return p; } /* Depref rows. */ MOID_T * a68_depref_rows (MOID_T *p, MOID_T *q) { if (q == M_ROWS) { while (a68_is_deprefable (p)) p = a68_depref_once (p); return p; } else return q; } /* Derow mode, strip FLEX and BOUNDS. */ MOID_T * a68_derow (MOID_T *p) { if (IS_ROW (p) || IS_FLEX (p)) return a68_derow (SUB (p)); else return p; } /* Whether rows type. */ bool a68_is_rows_type (MOID_T *p) { switch (ATTRIBUTE (p)) { case ROW_SYMBOL: case FLEX_SYMBOL: return true; case UNION_SYMBOL: { PACK_T *t = PACK (p); bool siga = true; while (t != NO_PACK && siga) { siga &= a68_is_rows_type (MOID (t)); FORWARD (t); } return siga; } default: return false; } } /* Whether mode is PROC (REF FILE) VOID or FORMAT. */ bool a68_is_proc_ref_file_void_or_format (MOID_T *p) { if (p == M_PROC_REF_FILE_VOID) return true; else if (p == M_FORMAT) return true; else return false; } /* Whether mode can be transput. */ bool a68_is_transput_mode (MOID_T *p, char rw) { if (p == M_INT) return true; else if (p == M_SHORT_INT) return true; else if (p == M_SHORT_SHORT_INT) return true; else if (p == M_LONG_INT) return true; else if (p == M_LONG_LONG_INT) return true; else if (p == M_REAL) return true; else if (p == M_LONG_REAL) return true; else if (p == M_LONG_LONG_REAL) return true; else if (p == M_BOOL) return true; else if (p == M_CHAR) return true; else if (p == M_BITS) return true; else if (p == M_SHORT_BITS) return true; else if (p == M_SHORT_SHORT_BITS) return true; else if (p == M_LONG_BITS) return true; else if (p == M_LONG_LONG_BITS) return true; else if (p == M_COMPLEX) return true; else if (p == M_LONG_COMPLEX) return true; else if (p == M_LONG_LONG_COMPLEX) return true; else if (p == M_ROW_CHAR) return true; else if (p == M_STRING) return true; else if (IS (p, UNION_SYMBOL) || IS (p, STRUCT_SYMBOL)) { for (PACK_T *q = PACK (p); q != NO_PACK; FORWARD (q)) { if (!(a68_is_transput_mode (MOID (q), rw) || a68_is_proc_ref_file_void_or_format (MOID (q)))) return false; } return true; } else if (IS_FLEX (p)) { if (SUB (p) == M_ROW_CHAR) return true; else return (rw == 'w' ? a68_is_transput_mode (SUB (p), rw) : false); } else if (IS_ROW (p)) return (a68_is_transput_mode (SUB (p), rw) || a68_is_proc_ref_file_void_or_format (SUB (p))); else return false; } /* Whether mode is printable. */ bool a68_is_printable_mode (MOID_T *p) { if (a68_is_proc_ref_file_void_or_format (p)) return true; else return a68_is_transput_mode (p, 'w'); } /* Whether mode is readable. */ bool a68_is_readable_mode (MOID_T *p) { if (a68_is_proc_ref_file_void_or_format (p)) return true; else if (IS_REF (p)) return a68_is_transput_mode (SUB (p), 'r'); else if (IS_UNION (p)) { for (PACK_T *q = PACK (p); q != NO_PACK; FORWARD (q)) { if (!IS_REF (MOID (q))) return false; else if (!a68_is_transput_mode (SUB (MOID (q)), 'r')) return false; } return true; } else return false; } /* Whether name struct. */ bool a68_is_name_struct (MOID_T *p) { return (NAME (p) != NO_MOID ? IS (DEFLEX (SUB (p)), STRUCT_SYMBOL) : false); } /* Yield mode to unite to. */ MOID_T * a68_unites_to (MOID_T *m, MOID_T *u) { /* Uniting U (m). */ MOID_T *v = NO_MOID; if (u == M_SIMPLIN || u == M_SIMPLOUT) return m; for (PACK_T *p = PACK (u); p != NO_PACK; FORWARD (p)) { /* Prefer []->[] over []->FLEX []. */ if (m == MOID (p)) v = MOID (p); else if (v == NO_MOID && DEFLEX (m) == DEFLEX (MOID (p))) v = MOID (p); } return v; } /* Whether moid in pack. */ bool a68_is_moid_in_pack (MOID_T *u, PACK_T *v, int deflex) { for (; v != NO_PACK; FORWARD (v)) { if (a68_is_equal_modes (u, MOID (v), deflex)) return true; } return false; } /* Whether a rows type in pack. */ bool a68_is_rows_in_pack (PACK_T *v) { for (; v != NO_PACK; FORWARD (v)) { if (a68_is_rows_type (MOID (v))) return true; } return false; } /* Whether P is a subset of Q. */ bool a68_is_subset (MOID_T *p, MOID_T *q, int deflex) { bool j =true; for (PACK_T *u = PACK (p); u != NO_PACK && j; FORWARD (u)) j = (j && a68_is_moid_in_pack (MOID (u), PACK (q), deflex)); return j; } /* Whether P can be united to UNION Q. */ bool a68_is_unitable (MOID_T *p, MOID_T *q, int deflex) { if (IS (q, UNION_SYMBOL)) { if (IS (p, UNION_SYMBOL)) return a68_is_subset (p, q, deflex); else if (p == M_ROWS) return a68_is_rows_in_pack (PACK (q)); else return a68_is_moid_in_pack (p, PACK (q), deflex); } return false; } /* Whether all or some components of U can be firmly coerced to a component mode of V.. */ void a68_investigate_firm_relations (PACK_T *u, PACK_T *v, bool *all, bool *some) { *all = true; *some = true; for (; v != NO_PACK; FORWARD (v)) { bool k = false; for (PACK_T *w = u; w != NO_PACK; FORWARD (w)) k |= a68_is_coercible (MOID (w), MOID (v), FIRM, FORCE_DEFLEXING); *some |= k; *all &= k; } } /* Whether there is a soft path from P to Q. */ bool a68_is_softly_coercible (MOID_T *p, MOID_T *q, int deflex) { if (a68_is_equal_modes (p, q, deflex)) return true; else if (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK) return a68_is_softly_coercible (SUB (p), q, deflex); else return false; } /* Whether there is a weak path from P to Q. */ bool a68_is_weakly_coercible (MOID_T * p, MOID_T * q, int deflex) { if (a68_is_equal_modes (p, q, deflex)) return true; else if (a68_is_deprefable (p)) return a68_is_weakly_coercible (a68_depref_once (p), q, deflex); else return false; } /* Whether there is a meek path from P to Q. */ bool a68_is_meekly_coercible (MOID_T *p, MOID_T *q, int deflex) { if (a68_is_equal_modes (p, q, deflex)) return true; else if (a68_is_deprefable (p)) return a68_is_meekly_coercible (a68_depref_once (p), q, deflex); else return false; } /* Whether there is a firm path from P to Q. */ bool a68_is_firmly_coercible (MOID_T *p, MOID_T *q, int deflex) { if (a68_is_equal_modes (p, q, deflex)) return true; else if (q == M_ROWS && a68_is_rows_type (p)) return true; else if (a68_is_unitable (p, q, deflex)) return true; else if (a68_is_deprefable (p)) return a68_is_firmly_coercible (a68_depref_once (p), q, deflex); else return false; } /* Whether firm. */ bool a68_is_firm (MOID_T *p, MOID_T *q) { return (a68_is_firmly_coercible (p, q, SAFE_DEFLEXING) || a68_is_firmly_coercible (q, p, SAFE_DEFLEXING)); } /* Whether P widens to Q. This function returns: The destination mode Q if P, or Some other mode which is an intermediate step from P to Q, or NO_MOID if P cannot be widened to Q. This means that if P is known to widen to Q (a68_is_widenable (P,Q) return true) this function can be invoked repeteadly and it will eventually return Q. */ MOID_T * a68_widens_to (MOID_T *p, MOID_T *q) { if (p == M_INT) { if (q == M_REAL || q == M_COMPLEX) { return M_REAL; } else { return NO_MOID; } } else if (p == M_LONG_INT) { if (q == M_LONG_REAL) { return M_LONG_REAL; } else { return NO_MOID; } } else if (p == M_LONG_LONG_INT) { if (q == M_LONG_LONG_REAL || q == M_LONG_LONG_COMPLEX) return M_LONG_LONG_REAL; else return NO_MOID; } else if (p == M_REAL) { if (q == M_COMPLEX) { return M_COMPLEX; } else { return NO_MOID; } } else if (p == M_LONG_REAL) { if (q == M_LONG_COMPLEX) return M_LONG_COMPLEX; else return NO_MOID; } else if (p == M_LONG_LONG_REAL) { if (q == M_LONG_LONG_COMPLEX) return M_LONG_LONG_COMPLEX; else return NO_MOID; } else if (p == M_BITS) { if (q == M_ROW_BOOL) return M_ROW_BOOL; else if (q == M_FLEX_ROW_BOOL) return M_FLEX_ROW_BOOL; else return NO_MOID; } else if (p == M_SHORT_BITS) { if (q == M_ROW_BOOL) return M_ROW_BOOL; else if (q == M_FLEX_ROW_BOOL) return M_FLEX_ROW_BOOL; else return NO_MOID; } else if (p == M_SHORT_SHORT_BITS) { if (q == M_ROW_BOOL) return M_ROW_BOOL; else if (q == M_FLEX_ROW_BOOL) return M_FLEX_ROW_BOOL; else return NO_MOID; } else if (p == M_LONG_BITS) { if (q == M_ROW_BOOL) return M_ROW_BOOL; else if (q == M_FLEX_ROW_BOOL) return M_FLEX_ROW_BOOL; else return NO_MOID; } else if (p == M_LONG_LONG_BITS) { if (q == M_ROW_BOOL) return M_ROW_BOOL; else if (q == M_FLEX_ROW_BOOL) return M_FLEX_ROW_BOOL; else return NO_MOID; } else if (p == M_BYTES && q == M_ROW_CHAR) return M_ROW_CHAR; else if (p == M_LONG_BYTES && q == M_ROW_CHAR) return M_ROW_CHAR; else if (p == M_BYTES && q == M_FLEX_ROW_CHAR) return M_FLEX_ROW_CHAR; else if (p == M_LONG_BYTES && q == M_FLEX_ROW_CHAR) return M_FLEX_ROW_CHAR; else return NO_MOID; } /* Whether P widens to Q. */ bool a68_is_widenable (MOID_T *p, MOID_T *q) { MOID_T *z = a68_widens_to (p, q); if (z != NO_MOID) return (z == q ? true : a68_is_widenable (z, q)); else return false; } /* Whether P is a REF ROW. */ bool a68_is_ref_row (MOID_T *p) { return (NAME (p) != NO_MOID ? IS_ROW (DEFLEX (SUB (p))) : false); } /* Whether strong name. */ bool a68_is_strong_name (MOID_T *p, MOID_T *q) { if (p == q) return true; else if (a68_is_ref_row (q)) return a68_is_strong_name (p, NAME (q)); else return false; } /* Whether strong slice. */ bool a68_is_strong_slice (MOID_T *p, MOID_T *q) { if (p == q || a68_is_widenable (p, q)) return true; else if (SLICE (q) != NO_MOID) return a68_is_strong_slice (p, SLICE (q)); else if (IS_FLEX (q)) return a68_is_strong_slice (p, SUB (q)); else if (a68_is_ref_row (q)) return a68_is_strong_name (p, q); else return false; } /* Whether strongly coercible. */ bool a68_is_strongly_coercible (MOID_T *p, MOID_T *q, int deflex) { /* Keep this sequence of statements. */ if (a68_is_equal_modes (p, q, deflex)) return true; else if (q == M_VOID) return true; else if ((q == M_SIMPLIN || q == M_ROW_SIMPLIN) && a68_is_readable_mode (p)) return true; else if (q == M_ROWS && a68_is_rows_type (p)) return true; else if (a68_is_unitable (p, a68_derow (q), deflex)) return true; if (a68_is_ref_row (q) && a68_is_strong_name (p, q)) return true; else if (SLICE (q) != NO_MOID && a68_is_strong_slice (p, q)) return true; else if (IS_FLEX (q) && a68_is_strong_slice (p, q)) return true; else if (a68_is_widenable (p, q)) return true; else if (a68_is_deprefable (p)) return a68_is_strongly_coercible (a68_depref_once (p), q, deflex); else if (q == M_SIMPLOUT || q == M_ROW_SIMPLOUT) return a68_is_printable_mode (p); else return false; } /* Basic coercions. */ bool a68_basic_coercions (MOID_T *p, MOID_T *q, int c, int deflex) { if (a68_is_equal_modes (p, q, deflex)) return true; else if (c == NO_SORT) return (p == q); else if (c == SOFT) return a68_is_softly_coercible (p, q, deflex); else if (c == WEAK) return a68_is_weakly_coercible (p, q, deflex); else if (c == MEEK) return a68_is_meekly_coercible (p, q, deflex); else if (c == FIRM) return a68_is_firmly_coercible (p, q, deflex); else if (c == STRONG) return a68_is_strongly_coercible (p, q, deflex); else return false; } /* Whether coercible stowed. */ bool a68_is_coercible_stowed (MOID_T *p, MOID_T *q, int c, int deflex) { if (c != STRONG) /* Such construct is always in a strong position, is it not? */ return false; else if (q == M_VOID) return true; else if (IS_FLEX (q)) { bool j = true; for (PACK_T *u = PACK (p); u != NO_PACK && j; FORWARD (u)) j &= a68_is_coercible (MOID (u), SLICE (SUB (q)), c, deflex); return j; } else if (IS_ROW (q)) { bool j = true; for (PACK_T *u = PACK (p); u != NO_PACK && j; FORWARD (u)) j &= a68_is_coercible (MOID (u), SLICE (q), c, deflex); return j; } else if (IS (q, PROC_SYMBOL) || IS (q, STRUCT_SYMBOL)) { if (DIM (p) != DIM (q)) return false; else { PACK_T *u = PACK (p), *v = PACK (q); bool j = true; while (u != NO_PACK && v != NO_PACK && j) { j &= a68_is_coercible (MOID (u), MOID (v), c, deflex); FORWARD (u); FORWARD (v); } return j; } } else return false; } /* Whether coercible series. */ bool a68_is_coercible_series (MOID_T *p, MOID_T *q, int c, int deflex) { if (c == NO_SORT) return false; else if (p == NO_MOID || q == NO_MOID) return false; else if (IS (p, SERIES_MODE) && PACK (p) == NO_PACK) return false; else if (IS (q, SERIES_MODE) && PACK (q) == NO_PACK) return false; else if (PACK (p) == NO_PACK) return a68_is_coercible (p, q, c, deflex); else { bool j = true; for (PACK_T *u = PACK (p); u != NO_PACK && j; FORWARD (u)) { if (MOID (u) != NO_MOID) j &= a68_is_coercible (MOID (u), q, c, deflex); } return j; } } /* Whether P can be coerced to Q in a C context. If P is a STOWED modes serie (A, B, ...) and Q is a routine mode like `proc (X, Y, ...)' then this routine determines whether A can be coerced to X, B to Y, etc. */ bool a68_is_coercible (MOID_T *p, MOID_T *q, int c, int deflex) { if (a68_is_mode_isnt_well (p) || a68_is_mode_isnt_well (q)) return true; else if (a68_is_equal_modes (p, q, deflex)) return true; else if (p == M_HIP) return true; else if (IS (p, STOWED_MODE)) return a68_is_coercible_stowed (p, q, c, deflex); else if (IS (p, SERIES_MODE)) return a68_is_coercible_series (p, q, c, deflex); else if (p == M_VACUUM && IS_ROW (DEFLEX (q))) return true; else return a68_basic_coercions (p, q, c, deflex); } /* Whether coercible in context. */ bool a68_is_coercible_in_context (SOID_T *p, SOID_T *q, int deflex) { if (SORT (p) != SORT (q)) return false; else if (MOID (p) == MOID (q)) return true; else return a68_is_coercible (MOID (p), MOID (q), SORT (q), deflex); } /* Whether list Y is balanced. */ bool a68_is_balanced (NODE_T *n, SOID_T *y, int sort) { if (sort == STRONG) return true; else { bool k = false; for (; y != NO_SOID && !k; FORWARD (y)) k = (!IS (MOID (y), STOWED_MODE)); if (k == false) a68_error (n, "construct has no unique mode"); return k; } } /* A moid from M to which all other members can be coerced. If no fulcrum of the balance is found, return NO_MOID. */ MOID_T * a68_get_balanced_mode_or_no_mode (MOID_T *m, int sort, bool return_depreffed, int deflex) { MOID_T *common_moid = NO_MOID; if (m != NO_MOID && !a68_is_mode_isnt_well (m) && IS (m, UNION_SYMBOL)) { int depref_level; bool siga = true; /* Test for increasing depreffing. */ for (depref_level = 0; siga; depref_level++) { siga = false; /* Test the whole pack. */ for (PACK_T *p = PACK (m); p != NO_PACK; FORWARD (p)) { /* HIPs are not eligible of course. */ if (MOID (p) != M_HIP) { MOID_T *candidate = MOID (p); int k; /* Depref as far as allowed. */ for (k = depref_level; k > 0 && a68_is_deprefable (candidate); k--) candidate = a68_depref_once (candidate); /* Only need testing if all allowed deprefs succeeded. */ if (k == 0) { MOID_T *to = (return_depreffed ? a68_depref_completely (candidate) : candidate); bool all_coercible = true; siga = true; for (PACK_T *q = PACK (m); q != NO_PACK && all_coercible; FORWARD (q)) { MOID_T *from = MOID (q); if (p != q && from != to) all_coercible &= a68_is_coercible (from, to, sort, deflex); } /* If the pack is coercible to the candidate, we mark the candidate. We continue searching for longest series of REF REF PROC REF. */ if (all_coercible) { MOID_T *mark = (return_depreffed ? MOID (p) : candidate); if (common_moid == NO_MOID) common_moid = mark; else if (IS_FLEX (candidate) && DEFLEX (candidate) == common_moid) /* We prefer FLEX. */ common_moid = mark; } } } } } } return common_moid; } /* A moid from M to which all other members can be coerced. If no fulcrum of the balance is found, return M. */ MOID_T * a68_get_balanced_mode (MOID_T *m, int sort, bool return_depreffed, int deflex) { MOID_T *common_moid = a68_get_balanced_mode_or_no_mode (m, sort, return_depreffed, deflex); return common_moid == NO_MOID ? m : common_moid; } /* Whether we can search a common mode from a clause or not. */ bool a68_clause_allows_balancing (int att) { switch (att) { case CLOSED_CLAUSE: case CONDITIONAL_CLAUSE: case CASE_CLAUSE: case SERIAL_CLAUSE: case CONFORMITY_CLAUSE: return true; } return false; } /* A unique mode from Z. */ MOID_T * a68_determine_unique_mode (SOID_T *z, int deflex) { if (z == NO_SOID) return NO_MOID; else { MOID_T *x = MOID (z); if (a68_is_mode_isnt_well (x)) return M_ERROR; /* If X is a series containing one union, a68_make_united_mode will return that union (because 'union (union (...))' is the same than 'union (...)') and then a68_get_balanced_mode below will try to balance the modes in that union. Not what we want. */ if (ATTRIBUTE (x) == SERIES_MODE && DIM (x) == 1 && IS (MOID (PACK (x)), UNION_SYMBOL)) return MOID (PACK (x)); x = a68_make_united_mode (x); if (a68_clause_allows_balancing (ATTRIBUTE (z))) return a68_get_balanced_mode (x, STRONG, A68_NO_DEPREF, deflex); else return x; } } /* Insert coercion A in the tree. */ void a68_make_coercion (NODE_T *l, enum a68_attribute a, MOID_T *m) { a68_make_sub (l, l, a); MOID (l) = a68_depref_rows (MOID (l), m); } /* Make widening coercion. */ static void make_widening_coercion (NODE_T *n, MOID_T *p, MOID_T *q) { MOID_T *z = a68_widens_to (p, q); a68_make_coercion (n, WIDENING, z); if (z != q) make_widening_coercion (n, z, q); } /* Make ref rowing coercion. */ void a68_make_ref_rowing_coercion (NODE_T *n, MOID_T *p, MOID_T *q) { if (DEFLEX (p) != DEFLEX (q)) { if (a68_is_widenable (p, q)) make_widening_coercion (n, p, q); else if (a68_is_ref_row (q)) { a68_make_ref_rowing_coercion (n, p, NAME (q)); a68_make_coercion (n, ROWING, q); } } } /* Make rowing coercion. */ void a68_make_rowing_coercion (NODE_T *n, MOID_T *p, MOID_T *q) { if (DEFLEX (p) != DEFLEX (q)) { if (a68_is_widenable (p, q)) make_widening_coercion (n, p, q); else if (SLICE (q) != NO_MOID) { a68_make_rowing_coercion (n, p, SLICE (q)); a68_make_coercion (n, ROWING, q); } else if (IS_FLEX (q)) a68_make_rowing_coercion (n, p, SUB (q)); else if (a68_is_ref_row (q)) a68_make_ref_rowing_coercion (n, p, q); } } /* Make uniting coercion. */ void a68_make_uniting_coercion (NODE_T *n, MOID_T *q) { a68_make_coercion (n, UNITING, a68_derow (q)); if (IS_ROW (q) || IS_FLEX (q)) a68_make_rowing_coercion (n, a68_derow (q), q); } /* Make depreffing coercion to coerce node N from mode P to mode Q in a strong context. */ void a68_make_depreffing_coercion (NODE_T *n, MOID_T *p, MOID_T *q) { if (DEFLEX (p) == DEFLEX (q)) return; else if (q == M_SIMPLOUT && a68_is_printable_mode (p)) a68_make_coercion (n, UNITING, q); else if (q == M_ROW_SIMPLOUT && a68_is_printable_mode (p)) { a68_make_coercion (n, UNITING, M_SIMPLOUT); a68_make_coercion (n, ROWING, M_ROW_SIMPLOUT); } else if (q == M_SIMPLIN && a68_is_readable_mode (p)) a68_make_coercion (n, UNITING, q); else if (q == M_ROW_SIMPLIN && a68_is_readable_mode (p)) { a68_make_coercion (n, UNITING, M_SIMPLIN); a68_make_coercion (n, ROWING, M_ROW_SIMPLIN); } else if (q == M_ROWS && a68_is_rows_type (p)) { a68_make_coercion (n, UNITING, M_ROWS); MOID (n) = M_ROWS; } else if (a68_is_widenable (p, q)) make_widening_coercion (n, p, q); else if (a68_is_unitable (p, a68_derow (q), SAFE_DEFLEXING)) a68_make_uniting_coercion (n, q); else if (a68_is_ref_row (q) && a68_is_strong_name (p, q)) a68_make_ref_rowing_coercion (n, p, q); else if (SLICE (q) != NO_MOID && a68_is_strong_slice (p, q)) a68_make_rowing_coercion (n, p, q); else if (IS_FLEX (q) && a68_is_strong_slice (p, q)) a68_make_rowing_coercion (n, p, q); else if (IS_REF (p)) { MOID_T *r = a68_depref_once (p); a68_make_coercion (n, DEREFERENCING, r); a68_make_depreffing_coercion (n, r, q); } else if (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK) { MOID_T *r = SUB (p); a68_make_coercion (n, DEPROCEDURING, r); a68_make_depreffing_coercion (n, r, q); } else if (p != q) a68_cannot_coerce (n, p, q, NO_SORT, SKIP_DEFLEXING, 0); } /* Whether p is a nonproc mode (that is voided directly). */ bool a68_is_nonproc (MOID_T *p) { if (IS (p, PROC_SYMBOL) && PACK (p) == NO_PACK) return false; else if (IS_REF (p)) return a68_is_nonproc (SUB (p)); else return true; } /* Voiden in an appropriate way. */ void a68_make_void (NODE_T *p, MOID_T *q) { switch (ATTRIBUTE (p)) { case ASSIGNATION: case IDENTITY_RELATION: case GENERATOR: case CAST: case DENOTATION: a68_make_coercion (p, VOIDING, M_VOID); return; default: break; } /* MORFs are an involved case. */ switch (ATTRIBUTE (p)) { case SELECTION: case SLICE: case ROUTINE_TEXT: case FORMULA: case CALL: case IDENTIFIER: /* A nonproc moid value is eliminated directly. */ if (a68_is_nonproc (q)) { a68_make_coercion (p, VOIDING, M_VOID); return; } else { /* Descend the chain of e.g. REF PROC .. until a nonproc moid remains. */ MOID_T *z = q; while (!a68_is_nonproc (z)) { if (IS_REF (z)) a68_make_coercion (p, DEREFERENCING, SUB (z)); if (IS (z, PROC_SYMBOL) && NODE_PACK (p) == NO_PACK) a68_make_coercion (p, DEPROCEDURING, SUB (z)); z = SUB (z); } if (z != M_VOID) a68_make_coercion (p, VOIDING, M_VOID); return; } default: break; } /* All other is voided straight away. */ a68_make_coercion (p, VOIDING, M_VOID); } /* Make strong coercion of node N from mode P to mode Q. */ void a68_make_strong (NODE_T *n, MOID_T *p, MOID_T *q) { if (q == M_VOID && p != M_VOID) a68_make_void (n, p); else a68_make_depreffing_coercion (n, p, q); }