Anonymous Access, Accessibility & Access-to-Subprogram

Use anonymous access parameters and components, understand accessibility levels and the rule that prevents dangling pointers to local objects ('non-local pointer cannot point to local object'), 'Unchecked_Access, and access-to-subprogram types for callbacks and dispatch tables.

Ada statically prevents a whole class of dangling-pointer bugs through accessibility rules: an access value may not outlive the object it designates. Pointers to subprograms are just as safe — they are typed by the full parameter profile. This lesson covers anonymous access types, the accessibility rules that make them safe, and callbacks.

Accessibility Levels

Every access type and every object has an accessibility level — roughly, how deeply nested its declaration is. The rule: X'Access is legal only if X lives at least as long as the access type. Taking 'Access of a local variable and storing it in a library-level access type is rejected at compile time with 'non-local pointer cannot point to local object'. When the compiler cannot decide statically (for anonymous access parameters), a run-time check raises Program_Error. X'Unchecked_Access (GNAT and standard Ada) bypasses the check — use it only when you can prove the pointer will not outlive the object.

• An access value must not outlive its designated object. • Static violation → compile error; dynamic → Program_Error. • 'Unchecked_Access disables the check — your responsibility.

Anonymous Access Types

Instead of declaring a named access type, you can write 'access T' directly in a parameter, component, object or result: procedure Reset (Target : not null access Integer). Anonymous access parameters accept any compatible access value (from any named access type or 'Access of an aliased object). For tagged types, 'access T' parameters are controlling and dispatch like T parameters. In modern Ada, prefer 'in out' parameters where possible; anonymous access is most useful for components that link objects and for access-to-subprogram parameters.

• access T in parameters/components/objects without naming a type. • Anonymous access parameters accept any compatible pointer. • Prefer in out parameters when you just need to modify an object.

Access-to-Subprogram

'type Int_Function is access function (X : Integer) return Integer;' can designate any function with that exact profile: Double'Access. Calls through it look like ordinary calls: F (E) (or F.all (E), required when there are no parameters). Arrays of subprogram accesses make dispatch tables. An anonymous access-to-subprogram parameter ('Pred : not null access function (X : Integer) return Boolean') can accept nested subprograms that read local variables — a safe form of closure, because such a pointer can't be stored beyond the call.

• access function/procedure (...) types for callbacks. • F (Args) calls through the pointer; F.all for parameterless ones. • Anonymous access-to-subprogram parameters accept nested subprograms (downward closures).

Code Examples

Apply takes a named access-to-function; Count_If takes an anonymous one, which lets it receive Big — a nested function reading the local Threshold. Squaring 2 4 6 8 10 gives 4 16 36 64 100, of which four exceed 10. Output: 2 4 6 8 10 4 16 36 64 100 Elements > 10: 4 Value after Reset: 0

Storing a pointer to a local variable in a longer-lived access object. Real GNAT diagnostic: l31_err.adb:8:17: error: non-local pointer cannot point to local object

Global:= Local'Unchecked_Access; -- to silence the error -- allocate on the heap (new) or declare the object at library level

Silencing the accessibility check creates a real dangling pointer after the scope ends.

type Callback is access procedure;... CB; -- call A parameterless call through an access value needs explicit.all.

procedure Update (P: access Record_Type); -- just to modify procedure Update (P: in out Record_Type);

in out expresses modification without pointers.

What does 'non-local pointer cannot point to local object' prevent? A. Memory leaks B. Dangling pointers C. Null dereference D. Race conditions B. Dangling pointers.

How do you call a parameterless procedure through access value CB? A. CB; B. CB.all; C. CB(); D. call CB;

Why can an anonymous access-to-subprogram parameter accept a nested function? A. It copies the function B. The pointer cannot be stored beyond the call, so it cannot dangle C. Nested functions are global D. It can't

Which exception signals a failed dynamic accessibility check? A. Constraint_Error B. Program_Error C. Storage_Error D. Tasking_Error Why does this fail to compile?

Real GNAT output: l31_err.adb:8:17: error: non-local pointer cannot point to local object Local disappears when Keep returns, but Global would still point to it.

Write a procedure For_Each (A : Int_Array; Action : not null access procedure (X : Integer)).

procedure For_Each (A : Int_Array; Action : not null access procedure (X : Integer)) is begin for E of A loop Action (E); end loop; end For_Each;

An anonymous access-to-procedure parameter lets callers pass nested procedures that capture local state.

with Ada.Text_IO; use Ada.Text_IO;

procedure L31_Anon_Access is
   --  Access-to-subprogram types enable callbacks
   type Int_Function is access function (X : Integer) return Integer;

   type Int_Array is array (Positive range <>) of Integer;

   function Double (X : Integer) return Integer is (2 * X);
   function Square (X : Integer) return Integer is (X * X);

   procedure Apply (A : in out Int_Array; F : Int_Function) is
   begin
      for E of A loop
         E := F (E);          --  indirect call (F.all (E) also works)
      end loop;
   end Apply;

   --  Anonymous access-to-subprogram parameter: accepts nested subprograms too
   function Count_If (A : Int_Array;
                      Pred : not null access function (X : Integer) return Boolean)
                      return Natural is
      N : Natural := 0;
   begin
      for E of A loop
         if Pred (E) then
            N := N + 1;
         end if;
      end loop;
      return N;
   end Count_If;

   --  Anonymous access-to-object parameter
   procedure Reset (Target : not null access Integer) is
   begin
      Target.all := 0;
   end Reset;

   procedure Print (A : Int_Array) is
   begin
      for E of A loop
      
…
procedure L31_Err is
   type Int_Ptr is access all Integer;
   Global : Int_Ptr;

   procedure Keep is
      Local : aliased Integer := 5;
   begin
      Global := Local'Access;     --  Local dies when Keep returns
   end Keep;
begin
   Keep;
end L31_Err;