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.
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
Boolean
(check);Cell
(check); andCell
(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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With