[COMMITTED] algol68: warn for hidden firmly related operators

Jose E. Marchesi jemarch@gnu.org
Thu Jun 19 10:30:54 GMT 2025


Algol 68 doesn't allow to define several operators which are "firmly
related" in the same range.  This is because they would make
dispatching of overloaded operators more complicated and based on
arbitrary rules.

Consider for example:

  op + = (Amode a, b) Amode: ...;
  op + = (ref Amode a, b) Amode: ...;

  Amode x, y;
  x + y;

When the compiler finds the `x + y' expression, it must decide which
operator to use at compile time.  Both x and y are of mode ref Amode,
and since they appear in a firm syntactic context (the operands of a
dyadic operator) deferencing is allowed.  So at this point both
operators may be applied: the operator working on ref Amode values and
the operator working on Amode values.  Instead of fabricating ad-hoc
rules like "select the operator requiring the longer chain of
coercions", the language syntax forbids defining these operators in
the same range.  Should you try you will get errors like:

  error: PROC (AMODE, AMODE) AMODE "+" is firmly related to \
         PROC (REF AMODE, REF AMODE) AMODE "+"
    1 | begin op + = (Amode a, b) Amode: ...;
      |          ^
  error: PROC (REF AMODE, REF AMODE) AMODE "+" is firmly related to \
         PROC (AMODE, AMODE) AMODE "+"
    2 |       op + = (ref Amode a, b) Amode: ...;
      |          ^

This is all well, but the language actually allows to define firmly
related operators in different ranges.  Consider for example:

  mode Trilean = union (void,bool);
  op NOT = (Trilean a) Trilean: (a | (bool b): NOT b | empty);

The operator NOT above is firmly related to the standard monadic
operator NOT that operates on bools, because a bool can be united to
`union (void, bool).  This means that within the reach of our NOT
operator the standard operator on booleans is no longer
accessible. This leads to errors like:

  error: TRILEAN cannot be coerced to BOOL in a meek-ENQUIRY_CLAUSE
    30 |     if NOT compressible (v) then ... fi
                ^

The -Whidden-declarations command line option makes ga68 to warn when
a tag hides another tag with the same name defined in a larger reach.
This patch makes that warning to also cove operators hidding firmly
related operators defined in a larger reach.  This leads to warnings
like:

  warning: PROC (TRILEAN) TRILEAN "NOT" hides firmly related \
           PROC (BOOL) BOOL "NOT" with larger reach [-Whidden-declarations]
    4 |       op NOT = (Trilean a) Trilean:
      |          ^

  error: TRILEAN cannot be coerced to BOOL in a meek-ENQUIRY_CLAUSE
    30 |     if NOT compressible (v) then ... fi
                ^

Which makes the subsequent error much more understandable.  The
warning can also be useful even if there is no aplication of the
operator leading to errors.
---
 gcc/algol68/a68-parser-taxes.cc               | 28 ++++++++++++-------
 gcc/algol68/ga68.texi                         |  2 ++
 .../algol68/compile/hidden-operators-1.a68    | 11 ++++++++
 3 files changed, 31 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/algol68/compile/hidden-operators-1.a68

diff --git a/gcc/algol68/a68-parser-taxes.cc b/gcc/algol68/a68-parser-taxes.cc
index 72f3eb346c0..a02033f5ae6 100644
--- a/gcc/algol68/a68-parser-taxes.cc
+++ b/gcc/algol68/a68-parser-taxes.cc
@@ -593,17 +593,25 @@ test_firmly_related_ops_local (NODE_T *p, TAG_T *s)
 
 	  if (t != NO_TAG)
 	    {
-	      if (TAG_TABLE (t) == A68_STANDENV)
-		{
-		  a68_error (p, "M Z is firmly related to M Z",
-			     MOID (s), NSYMBOL (NODE (s)), MOID (t),
-			     NSYMBOL (NODE (t)));
-		}
-	      else
+	      a68_error (p, "M Z is firmly related to M Z",
+			 MOID (s), NSYMBOL (NODE (s)), MOID (t),
+			 NSYMBOL (NODE (t)));
+	    }
+
+	  /* Warn for hidden firmly related operators defined in outer ranges,
+	     if requested.  */
+	  for (TABLE_T *prev = PREVIOUS (TAG_TABLE (s));
+	       prev != NO_TABLE;
+	       prev = PREVIOUS (prev))
+	    {
+	      TAG_T *t = find_firmly_related_op (prev, NSYMBOL (NODE (s)), l, r,
+						 NO_TAG /* self */);
+	      if (t != NO_TAG)
 		{
-		  a68_error (p, "M Z is firmly related to M Z",
-			     MOID (s), NSYMBOL (NODE (s)), MOID (t),
-			     NSYMBOL (NODE (t)));
+		  a68_warning (p, OPT_Whidden_declarations,
+			       "M Z hides firmly related M Z with larger reach",
+			       MOID (s), NSYMBOL (NODE (s)),
+			       MOID (t), NSYMBOL (NODE (t)));
 		}
 	    }
 	}
diff --git a/gcc/algol68/ga68.texi b/gcc/algol68/ga68.texi
index b6807561add..11a6cea8042 100644
--- a/gcc/algol68/ga68.texi
+++ b/gcc/algol68/ga68.texi
@@ -225,6 +225,8 @@ Warn when a potential name scope violation is found.
 @opindex Wno-hidden-declarations
 @item -Whidden-declarations
 Warn when a declaration hides another declaration in a larger reach.
+This includes operators that hide firmly related operators defined in
+larger reach.
 @opindex Wextensions
 @opindex Wno-extensions
 @item -Wextensions
diff --git a/gcc/testsuite/algol68/compile/hidden-operators-1.a68 b/gcc/testsuite/algol68/compile/hidden-operators-1.a68
new file mode 100644
index 00000000000..d66242d67a6
--- /dev/null
+++ b/gcc/testsuite/algol68/compile/hidden-operators-1.a68
@@ -0,0 +1,11 @@
+{ dg-options {-Whidden-declarations} }
+
+begin mode Trilean = union (void,bool);
+
+      Trilean unknown = empty;
+      op NOT = (Trilean a) Trilean: { dg-warning "hides" }
+         skip;
+      op AND = (Trilean a,b) Trilean: { dg-warning "hides" }
+         skip;
+      skip
+end
-- 
2.30.2



More information about the Algol68 mailing list