Gem #33: Accessibility Checks (Part I: Ada95)

by Ramón Fernández —AdaCore

Let's get started…

Ada is a block-structured language, which means the programmer can nest blocks of code inside other blocks. At the end of a block, all objects declared inside of it go out of scope, meaning they no longer exist, so the language disallows pointers to objects in blocks with a deeper nesting level.

In order to prevent dangling references, every entity is associated with a number, called its "accessibility level", according to a Ada's accessibility rules. When certain references are made to an entity of an access type (Ada's parlance for pointer), the accessibility level of the entity is checked against the level allowed by the context so that no dangling pointers can occur.

Consider the following example:

     procedure Static_Check is
        type Global is access all Integer;
        X : Global;

        procedure Init is
           Y : aliased Integer := 0;
        begin
           X := Y'Access; -- Illegal!
        end Init;
   
     begin
        Init;
        ...
     end Static_Check;

The assignment is illegal because when the procedure Init finishes, the object Y no longer exists, thus making X a danging pointer. The compiler will detect this situation and flag the error.

The beauty of the accessibility rules is that most of them can be checked and enforced at compile time, just by using statically known accessibility levels.

However, there are cases when it is not possible to statically determine the accessibility level that an entity will have during program execution. In these cases, the compiler will insert a run-time check to raise an exception if a dangling pointer can be created:

     procedure Access_Params is
        type Integer_Access is access all Integer;
        Data : Integer_Access;

        procedure Init_Data (Value : access Integer) is
        begin
           Data := Integer_Access (Value);
           -- this conversion performs a dynamic accessibility check
        end;

        X : aliased Integer := 1;

     begin
        Init_Data (X'Access); -- This is OK 

        declare  
           Y : aliased Integer := 2;
        begin
           Init_Data (Y'Access); --  Trouble!
        end;
	--  Y no longer exists!

	Process (Data);
     end;

In the example above, we cannot know at compile time the accessibility level of the object that will be passed to Init_Data, so the compiler inserts a run-time check to make sure that the assignment 'Data := ...' does not cause a dangling reference -- and to raise an exception if it would.

In summary, when it comes to dangling references, Ada makes it very hard for you to shoot yourself in the foot!


About the Author

Ramón Fernández is a senior software engineer at AdaCore. Since joining the company in 2001 he has been involved in a wide variety of areas including customer support, GNAT Pro front-end and run-time development, real-time systems, quality assurance, embedded systems, production infrastructure, and IT management. Ramón has an MSc in Computer Science from New York University, and a Telecommunications Engineering degree from ETSIT-UPM (Technical University of Madrid, Spain).