Call to abstract procedure must be dispatching

I’m working on some old code and I noticed that I used this a lot:

This_C : Object'Class renames Object'Class (This);

And subsequently using This_C for all calls. At first I thought it was just a kink on an inexperienced Ada developer and used This instead. That worked just as fine. But then I came across this one:

src/adacl-sar-filter.adb|44 col 14 error| call to abstract procedure must be dispatching

with line 44 looking like this:

This.Filter_Line (Element);

Changing to one of the following and it works again:

Object'Class (This).Filter_Line (Element);
This_C.Filter_Line (Element);

Now I wonder if my younger self knew something I have forgotten and should indeed use This_C aka Object'Class (This) all the time to make sure calls are dispatching.

Maybe this paper.

1 Like

What you did is called redispatch.

Redispatch is arguably always a type error because the specific type has been established by the prior dispatch, so you must call primitive operations of exactly that type.

(One of the major advantages of Ada’s OO model over C++'s one is that you must explicitly force redispatch in Ada when C++ does that by default except for constructors and destructors. There type violation is so apparent, that they must make a special exemption for them!)

In your specific case you seem to call an abstract primitive operation from a non-abstract one. Consider making the second primitive operation a class-wide subprogram. The rationale: since its implementation dispatches, it has the same body for all derived types = acts on the whole class = class-wide.

Happy New Year!

1 Like

Also note that GNAT does seem to get confused sometimes depending on how the actual type for Thing gets declared. For example, completing a public extension of an synchronized interface with a protected type or task can sometimes generate that error when calling a function for the interface. I don’t know for sure if it is a GNAT bug or a bug in the RM (see here and here for some discourse I had on this exact error years ago).

1 Like

I think the legal code must look like this:

package Example is    
   type An_Interface is synchronized interface;
   procedure P1 (Self : in out An_Interface) is abstract;

   type Instance is synchronized new An_Interface with private;
   overriding procedure P1 (Self : in out Instance);
private
   -- Instance full view is a protected type
   protected type Instance is new An_Interface with
--    overriding procedure P1; -- Already overridden in the public view
   private
   end Instance;
end Example;
    
package body Example is
   protected body Instance is
      procedure P1 is
      begin
         Put_Line ("Did Something");
      end P1;
   end Instance;
end Example;

or like this:

package Example is    
   type An_Interface is synchronized interface;
   procedure P1 (Self : in out An_Interface) is abstract;

   type Instance is synchronized new An_Interface with private;
private
   -- Instance full view is a protected type
   protected type Instance is new An_Interface with
      overriding procedure P1; -- Private overriding is legal
   private
   end Instance;
end Example;
    
package body Example is
   protected body Instance is
      procedure P1 is
      begin
         Put_Line ("Did Something");
      end P1;
   end Instance;
end Example;

And v.p1 must compile regardless because the type of v is not abstract.

Happy New Year!

1 Like

Unfortunately the first fails with missing body for "P1" and the second fails with call to abstract procedure must be dispatching

Here’s an online example: Online Compiler and Editor/IDE for Java, C, C++, PHP, Python, Ruby, Perl - Code and Run Online

1 Like

Right, and IMO it is either language or GNAT bug, or both… :upside_down_face:

It can also be argued that it is not a type error, but exactly in accord with how the programmer designed the program. YMMV. (Dmitry and I have had this discussion several times… :-))

3 Likes

One does not contradict another… :sunglasses: