6

I am working my way through Barnes' book 'Programming in Ada 2012'. This is a code sample implementing a stack from section 12.5.

src/stacks.adb: (the main relevant file)


package body Stacks is

   procedure Push(S: in out Stack; X: in Integer) is
   begin
      S := new Cell'(S,X);
   end Push;

   procedure Pop(S: in out Stack; X: in out Integer) is
   begin
      X := S.Value;
      S := Stack(S.Next);
   end Pop;

   function "="(S, T: Stack) return Boolean is
      SS: access Cell := S;
      TT: access Cell := T;
   begin
      while SS /= null and TT /= null loop
         if SS.Value /= TT.Value then
            return false;
         end if;
         SS := SS.Next;
         TT := TT.Next;
      end loop;
      return SS = TT; -- error: implicit conversion of stand-alone anonymous access object not allowed
   end "=";

end Stacks;

I have added a comment containing the error that gnat gives me. Why am I not allowed to convert from one anonymous access Cell to another?

I can solve the problem by inverting the condition:

return not (SS /= TT);

It mystifies me as John Barnes states earlier that if you define a "=" operator returning a boolean, then the inverse "/=" is generated automatically for you, meaning the opposite.

Similarly, the loop condition can be inverted, in which case it fails to compile with the same message.

Finally, a side-note: the expected behaviour of the program, which it gives after changing to return not (SS /= TT) is to recurse infinitely and raise a storage_error due to stack overflow. The reason for that is better seen in this other SO question, and is not the subject of this question.

Why is the conversion disallowed by the compiler when I write "="? Why is it different when I write "/=", which I thought would always be the inverse?

The other files needed in order to compile the example for yourself:

src/stacks.ads:

package Stacks is

   type Stack is limited private;

   procedure Push(S: in out Stack; X: in Integer);
   procedure Pop(S: in out Stack; X: in out Integer);
   function "="(S, T: Stack) return Boolean;

private

   type Cell is
      record
         Next: access Cell;
         Value: Integer;
      end record;
   type Stack is access all Cell;

end;

src/main.adb:

with Ada.Text_IO; use Ada.Text_IO;
with Stacks; use Stacks;

procedure Main is
   A : Stack;
   B : Stack;
begin
   Push(A, 1);
   Push(B, 1);
   Push(A, 2);
   Push(B, 2);
   Push(A, 1);
   Push(B, 1);
   Push(A, 8);
   Push(B, 8);

   declare
      Same : Boolean := A = B;
      Text : String := (if Same then "They are the same" else "They are not the same");
   begin
      Put_Line(Text);
   end;
end Main;

stacks.gpr:

project stacks is
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("main.adb");
end stacks;

Makefile:

all:
    gprbuild -d -p -g

clean:
    rm -rf obj *.o *.ali

Or compile with gcc:

gcc -c src/*.adb
gnatbind main
gnatlink main

It gives the same results.

Frédéric Praca
  • 1,620
  • 15
  • 29
TamaMcGlinn
  • 2,840
  • 23
  • 34
  • 4
    Rejecting `SS = TT` but accepting `not (SS /= TT)` seems like a compiler bug. I found (GCC9, running under gdb) that the stack overflow happens on `while SS /= null and TT /= null loop`! – Simon Wright Dec 19 '19 at 22:29
  • 2
    Just for info, using `Standard."="` and `Standard."/="` compiles and works. – Simon Wright Dec 20 '19 at 12:49
  • 1
    See also [_How can I overload the '=' operator in Ada without creating a recursive function?_](https://stackoverflow.com/q/6086358/230513) – trashgod Dec 20 '19 at 18:38
  • 1
    Those who use anonymous access types get what they deserve. But you should be aware that anonymous access types don't have "=" defined for them, but may use the operators for universal_access (from Standard). Also, IIRC, anonymous access types may be implicitly converted to named access types with the same designated types. Type Stack is a named access with the same designated type as your anonymous access types, so probably they are being implicitly converted to Stack, which results in a recursive call to the "=" you're defining. That it accepts "/=" but not "=" must be a compiler error. – Jeffrey R. Carter Dec 20 '19 at 21:09
  • I agree that there appears to be a compiler bug for the /= case. The rules are defined in RM 4.5.2(9.1/2 - 9.4/2). My reading of these rules says that /= should not have been allowed. It is correct that = is not allowed, unless using Standard."=" as already suggested. Also relevant reading is AI95-00420-1. Another option that compiles would be to write explicit conversions as in; return Stack (TT) = Stack (SS); This should be legal. Another curious incantation that compiles but shouldn't be legal, I believe, is return not (not (TT = SS)); – B. Moore Dec 21 '19 at 07:18
  • @B.Moore Could you explain why adding the double negation is relevant? You say that `SS = TT` should not be legal, so it follows then in the same line that `not (not TT = SS))` should also be illegal, right? Does the compiler offer different opinions of these two expressions that are logically equivalent? – TamaMcGlinn Dec 22 '19 at 11:50
  • 5
    Yes, the GNAT compiler says that "SS = TT" is illegal, which is correct according to the rules, but it accepts "not (not (SS = TT)), which is logically equivalent, and should also be illegal, but isn't. – B. Moore Dec 24 '19 at 22:25

0 Answers0