Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading operator "=" for anonymous access types?

Tags:

ada

I am working my way through Barnes' excellent Ada book. This is a code sample for deep comparison of linked lists from section 11.7:

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

function "=" (L, R: access Cell) return Boolean is
begin
  if L = null or R = null then    -- universal =
    return L = R;                 -- universal = (Line A)
  elsif L.Value = R.Value then
    return L.Next = R.Next;       -- recurses OK (Line B)
  else
    return False;
  end if;
end "=";

I can't seem to wrap my head around why in Line A operator "=" of the universal_access type is called (because of the preference rule), on Line B, however, the user-defined operator "=" is called (which makes recursion possible in the first place), this time with no preference for operator "=" of universal_access.

Both L and R, as well as L.Next and R.Next are of the same anonymous type "access Cell". Why the difference in "dispatching"? Does it have to do with L and R being access parameters? If so, what is the rule there?

I did my best to find anything in the AARM, especially section 4.5.2, but could not make any sense of it.

Cheers.

like image 661
dacker Avatar asked Jan 11 '19 21:01

dacker


2 Answers

I will summarize my findings (with the help of Simon Wright and G_Zeus) as of now. Please correct me if I'm wrong:

According to the standard, L = null, R = null, L = R as well as L.Next = R.Next should each unambiguously call the user-defined operator =. universal_access operator = must not kick in at all here.

Reason:

The operands L, R, L.Next and R.Next violate the precondition in ARM 4.5.2(9.1-9.4) for interpreting = in these expressions as to mean operator = of the universal_access type:

The precondition says that neither of the operands are of an access-to-object type (access Cell) whose designated type is Cell (check), Cell has a user-defined primitive equality operator (check) such that

  • its result type is Boolean (check);
  • it is declared immediately within the same declaration list as Cell (check); and
  • at least one of its operands is an access parameter with designated type Cell (both operands are, check).

The preference rule for operator = of the universal_access type in ARM 8.6(29.1) does not apply here, since it requires "two acceptable interpretations". But because of 4.5.2, operator = of the universal_access type is not an acceptable interpretation.

So there is no choice: in all cases (even L = null) it has to be the user-defined operator =.

@Simon Wright: So "unbounded recursion" is actually the correct compiler behavior.

@G_Zeus: Issuing an ambiguity error for l = r is incorrect compiler behavior, the compiler should have picked Access_Equal."=".

The example should correctly read:

...

  if Standard."="(L, null) or Standard."="(R, null) then    -- universal =
    return Standard."="(L, R);                              -- universal =
  elsif L.Value = R.Value then
    return L.Next = R.Next;                                 -- recurses OK

...

Cheers.

like image 173
dacker Avatar answered Oct 01 '22 06:10

dacker


I do not have enough reputation to comment on OP, so I will write an answer instead.

Interestingly enough, I cannot compile such an example (I used Integer access instead, but I doubt it has any relevance) in Gnat 6.1.1. Gnat keeps telling me the use of inline "=" is ambiguous between the overloading "=" and the universal "=" in Standard. So I tried:

package body Access_Equal is
   function "=" (L,R : access Integer) return Boolean is
   begin
      return Standard."="(L, R) or L.all = R.all;
   end "=";
end Access_Equal;

And it seems to do the trick. I cannot use inline "=" in my code however, I have to use fully qualified names:

with Ada.Text_IO; use Ada.Text_IO;
with Access_Equal; use Access_Equal;
procedure Access_Equal_Test is
   l : access Integer := new Integer'(1);
   r : access Integer := new Integer'(1);
begin
   Put_Line(Boolean'Image(Standard."="(l, r))); -- FALSE
   Put_Line(Boolean'Image(Access_Equal."="(l, r))); -- TRUE
   Put_Line(Boolean'Image(l = r)); -- does not work
end Access_Equal_Test;

Note: using the Standard package might be a portability hazard, as it does not seem to be required to define universal "=". More information here.

like image 29
G_Zeus Avatar answered Oct 01 '22 04:10

G_Zeus