Function results (GNAT Reference Manual) (original) (raw)
17.3.4.3 Function results ¶
function Get (X : Rec) return access T;
If the result subtype of a function is either an anonymous access (sub)type, a class-wide (sub)type, an unconstrained subtype with an access discriminant, or a type with an unconstrained subcomponent subtype that has at least one access discriminant (this last case is only possible if the access discriminant has a default value), then we say that the function result type “might require an anonymous-access-part accessibility check”. If a function has an access parameter, or a parameter whose subtype “might require an anonymous-access-part accessibility check”, then we say that the each such parameter “might be used to pass in an anonymous-access value”. If the first of these conditions holds for the result subtype of a function and the second condition holds for at least one parameter that function, then it is possible that a call to that function could return a result that contains anonymous-access values that were passed in via the parameter.
Given a function call where the result type “might require an anonymous-access-part accessibility check” and a formal parameter of that function that “might be used to pass in an anonymous-access value”, either the type of that formal parameter is an anonymous access type or it is not. If it is, and if a No_Dynamic_Access_Checks restriction is in effect, then the accessibility level of the type of the actual parameter shall be statically known to not be deeper than that of the master of the call. If it isn’t, then the accessibility level of the actual parameter shall be statically known to not be deeper than that of the master of the call.
Function result example:
declare type T is record Comp : aliased Integer; end record;
function Identity (Param : access Integer) return access Integer is begin return Param; -- Legal end;
function Identity_2 (Param : aliased Integer) return access Integer is begin return Param'Access; -- Legal end;
X : access Integer; begin X := Identity (X); -- Legal declare Y : access Integer; Z : aliased Integer; begin X := Identity (Y); -- Illegal since Y is too deep X := Identity_2 (Z); -- Illegal since Z is too deep end; end;
In order to avoid having to expand the definition of “might be used to pass in an anonymous-access value” to include any parameter of a tagged type, the No_Dynamic_Access_Checks restriction also imposes a requirement that a type extension cannot include the explicit definition of an access discriminant.
Here is an example of one such case of an upward conversion which would lead to a memory leak:
declare type T is tagged null record; type T2 (Disc : access Integer) is new T with null record; -- Must be illegal
function Identity (Param : aliased T'Class) return access Integer is begin return T2 (T'Class (Param)).Disc; -- Here P gets effectively returned and set to X end;
X : access Integer; begin declare P : aliased Integer; Y : T2 (P'Access); begin X := Identity (T'Class (Y)); -- Pass local variable P (via Y's discriminant), -- leading to a memory leak. end; end;
```
Thus we need to make the following illegal to avoid such situations:
```ada package Pkg1 is type T1 is tagged null record; function Func (X1 : T1) return access Integer is (null); end;
package Pkg2 is type T2 (Ptr1, Ptr2 : access Integer) is new Pkg1.T1 with null record; -- Illegal ... end;
In order to prevent upward conversions of anonymous function results (like below), we also would need to assure that the level of such a result (from the callee’s perspective) is statically deeper:
declare type Ref is access all Integer; Ptr : Ref; function Foo (Param : access Integer) return access Integer is begin return Result : access Integer := Param; do Ptr := Ref (Result); -- Not allowed end return; end; begin declare Local : aliased Integer; begin Foo (Local'Access).all := 123; end; end;