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:

   <b>function</b> Sum (A : Array_Of_Natural) <b>return</b> Natural <b>is</b>
      Result : Natural := 0;
   <b>begin</b>
      <b>for</b> Index <b>in</b> A'<b>Range</b> <b>loop</b>
         Result := Result + A (Index);
      <b>end</b> <b>loop</b>;
      <b>return</b> Result;
   <b>end</b> 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:

   <b>function</b> Sum (A : Array_Of_Natural) <b>return</b> Natural <b>is</b>
   <b>begin</b>
      <b>return</b> Result : Natural := 0 <b>do</b>
         <b>for</b> Index <b>in</b> A'<b>Range</b> <b>loop</b>
            Result := Result + A (Index);
         <b>end</b> <b>loop</b>;
      <b>end</b> <b>return</b>;
   <b>end</b> 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:

   <b>function</b> Make_Task (Val : Integer) <b>return</b> Task_Type <b>is</b>
      Result : Task_Type (Discriminant => Val * 3);
   <b>begin</b>
      … <i>-- some statements</i>
      <b>return</b> Result; <i>-- Illegal!</i>
   <b>end</b> 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:

   <b>function</b> Make_Task (Val : Integer) <b>return</b> Task_Type <b>is</b>
   <b>begin</b>
      <b>return</b> Result : Task_Type (Discriminant => Val * 3) <b>do</b>
         … <i>-- some statements</i>
      <b>end</b> <b>return</b>;
   <b>end</b> 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:

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

Last Updated: 10/26/2017
Posted on: 9/24/2007