Gem #10: Limited Types in Ada 2005 — Extended Return Statements

by Bob Duff —AdaCore

Let's get started…

A common idiom in Ada 95 is to build up a function result in a local object, and then return that object:

   function Sum (A : Array_Of_Natural) return Natural is
      Result : Natural := 0;
   begin
      for Index in A'Range loop
         Result := Result + A (Index);
      end loop;
      return Result;
   end Sum;

Ada 2005 allows a notation called the extended_return_statement, which allows you to declare the result object and return it as part of one statement. It looks like this:

   function Sum (A : Array_Of_Natural) return Natural is
   begin
      return Result : Natural := 0 do
         for Index in A'Range loop
            Result := Result + A (Index);
         end loop;
      end return;
   end Sum;

The return statement here creates Result, initializes it to 0, and executes the code between “do” and “end return”. When “end return” is reached, Result is automatically returned as the function result.

For most types, this is no big deal — it's just syntactic sugar. But for limited types, this syntax is almost essential:

   function Make_Task (Val : Integer) return Task_Type is
      Result : Task_Type (Discriminant => Val * 3);
   begin-- some statements
      return Result; -- Illegal!
   end Make_Task;

The return statement here is illegal, because Result is local to Make_Task, and returning it would involve a copy, which makes no sense (which is why task types are limited). In Ada 2005, we can write constructor functions for task types:

   function Make_Task (Val : Integer) return Task_Type is
   begin
      return Result : Task_Type (Discriminant => Val * 3) do-- some statements
      end return;
   end Make_Task;

If we call it like this:

    My_Task : Task_Type := Make_Task (Val => 42);

Result is created “in place” in My_Task. Result is temporarily considered local to Make_Task during the “… -- some statements” part, but as soon as Make_Task returns, the task becomes more global. Result and My_Task really are one and the same object.

When returning a task from a function, it is activated after the function returns. The “… -- some statements” part had better not try to call one of the task's entries, because that would deadlock. That is, the entry call would wait until the task reaches an accept statement, which will never happen, because the task will never be activated.

While the extended_return_statement was added to the language specifically to support limited constructor functions, it comes in handy whenever you want a local name for the function result:

   function Make_String (…) return String is
      Length : Natural := 10;
   begin
      ifthen
         Length := 12;
      end if;
      return Result : String (1..Length) do-- fill in the characters
         pragma Assert (Is_Good (Result)); null;
      end return;
   end Make_String;