Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Ada 2012 : aliased parameters ?

754 views
Skip to first unread message

Yannick Duchêne (Hibou57)

unread,
Mar 28, 2011, 7:47:22 AM3/28/11
to
Hello,

I was looking at what the Ada 2012 reference looks like so far, and
especially at all the “Extensions to Ada 2005” sections. In “Subprogram
Declarations” there is such an extension.

Quote from Ada 2012:

Parameters can now be explicitly aliased, allowing parts of
function results to designate parameters and forcing
by-reference parameter passing.

I'm not sure I've understood. What was wrong with access type parameters ?
I don't see a reason why aliased parameters may be required (and don't
feel this can be clean). Well, why not in/out parameter for functions, as
things like test-and-set are common idioms, but I really can't figure why
this one in particular was required.

I may look at the related AI later, but it's long (so I will deffer it). A
quick overview of good reasons from someone who know ?


--
Si les chats miaulent et font autant de vocalises bizarres, c’est pas pour
les chiens.
“ c++; /* this makes c bigger but returns the old value */ ” [Anonymous]

Dmitry A. Kazakov

unread,
Mar 28, 2011, 7:56:19 AM3/28/11
to
On Mon, 28 Mar 2011 13:47:22 +0200, Yannick Duchêne (Hibou57) wrote:

> Quote from Ada 2012:
>
> Parameters can now be explicitly aliased, allowing parts of
> function results to designate parameters and forcing
> by-reference parameter passing.

Interesting. How to force by-reference something allocated in a register?

Yes one could copy the actual value and then pass a reference to the copy,
but that is not what I would call "by-reference."

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

AdaMagica

unread,
Mar 28, 2011, 7:56:51 AM3/28/11
to
Aliased parameters are meant to cure a deficiency in e.g. containers.
With current Ada, it's awkward to replace an element of a container.

Now if you make the curser an aliased parameter, the function result
may dereference safely the cursor, since the cursor and the result are
tightly coupled, i.e. the cursor cannot be changed as long as the
function result exists:

function Get (Pos: aliased Cursor) return access Element;

This is the idea. The syntax in this example might not be correct.

Randy Brukardt

unread,
Mar 28, 2011, 11:04:53 PM3/28/11
to
"Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
news:fkeyxg5zhwig.1hrc0cluxh1r2$.dlg@40tude.net...

> On Mon, 28 Mar 2011 13:47:22 +0200, Yannick Duchêne (Hibou57) wrote:
>
>> Quote from Ada 2012:
>>
>> Parameters can now be explicitly aliased, allowing parts of
>> function results to designate parameters and forcing
>> by-reference parameter passing.
>
> Interesting. How to force by-reference something allocated in a register?

The thing passed has to be aliased or tagged (of course), so the compiler
just has to avoid allocating them in a register. Standard stuff.

> Yes one could copy the actual value and then pass a reference to the copy,
> but that is not what I would call "by-reference."

Neither do we.

Randy.


Randy Brukardt

unread,
Mar 28, 2011, 11:16:45 PM3/28/11
to
"Yannick Duchêne (Hibou57)" <yannick...@yahoo.fr> wrote in message
news:op.vs1xo...@index.ici...
...

> Quote from Ada 2012:
>
> Parameters can now be explicitly aliased, allowing parts of
> function results to designate parameters and forcing
> by-reference parameter passing.
>
>I'm not sure I've understood. What was wrong with access type parameters ?

Two things: (1) need to explicitly write 'Access at every call site, when
all you are doing is passing a by-reference parameter; (2) anonymous access
requires a run-time accessibility level; that means that passing the wrong
thing (a local object, for instance) might cause Program_Error to be raised
later. This is an unnecessary hazard for the sorts of uses envisioned for
aliased parameters. Aliased parameters move the check to the call site,
where it almost always will succeed (there is one case involving function
calls inside of allocators where it might fail, and if it does it almost
always will fail at compile-time).

> I don't see a reason why aliased parameters may be required (and don't
> feel this can be clean). Well, why not in/out parameter for functions, as

Ada 2012 allows "in out" parameters on functions. The usual use of aliased
parameters is on an "in out" parameter.

>things like test-and-set are common idioms, but I really can't figure why
>this one in particular was required.

The motivating case is to make the containers better. Ada 2012 adds the
following to all of the containers:

function Reference (Container : aliased in out Vector; Position : in
Cursor)
return Reference_Type;

where Reference_Type is defined as:

type Reference_Type (Element : not null access Element_Type) is private
with
Implicit_Dereference => Element;

The aspect "Implicit_Dereference" lets you omit "Element.all" from uses of
this type, so the call:

My_Vector.Reference (My_Cursor).Comp := 10;

is legal (presuming the element type has a component "Comp"). And another
"feature" allows a form of user-defined indexing, so you actually can write:

My_Vector (My_Cursor).Comp := 10;

which is a big improvement over using Update_Element (which requires writing
a procedure to do an in-place element update).

Randy.


Maciej Sobczak

unread,
Mar 29, 2011, 3:34:49 AM3/29/11
to
On 29 Mar, 05:16, "Randy Brukardt" <ra...@rrsoftware.com> wrote:

> The motivating case is to make the containers better. Ada 2012 adds the
> following to all of the containers:
>
>     function Reference (Container : aliased in out Vector; Position : in
> Cursor)
>        return Reference_Type;

Out of curiosity - is it possible to leak the reference this way? I
mean - is it possible for the caller to make a copy of returned
reference and store it arbitrarily long?

Note that the "copy" might not be obvious, as in:

declare
My_Element : Vector_Type.Reference_Type renames
My_Vector.Reference (My_Cursor);
begin
My_Element.Comp := 10;
My_Element.Other_Comp := 3.14;
end;

The C++ equivalent of this is both a fantastic performance feature and
a deadly security hole. How is this solved in Ada?

--
Maciej Sobczak * http://www.msobczak.com * http://www.inspirel.com

Florian Weimer

unread,
Mar 29, 2011, 2:22:51 PM3/29/11
to
* AdaMagica:

> Aliased parameters are meant to cure a deficiency in e.g. containers.
> With current Ada, it's awkward to replace an element of a container.
>
> Now if you make the curser an aliased parameter, the function result
> may dereference safely the cursor, since the cursor and the result are
> tightly coupled,

But this is already the case in Ada 2005. The lifetime of passed-in
objects extends beyond the immediate need for evaluating the
expression:

| Leaving an execution happens immediately after its completion,
| except in the case of a _master_: the execution of a body other than
| a package_body; the execution of a statement; or the evaluation of
| an expression, function_call, or range that is not part of an
| enclosing expression, function_call, range, or simple_statement
| other than a simple_return_statement.

This is from 7.6.1(3/2).

I've written experimental code which depends on this, providing a
syntactically convenient and efficient form of variadic subprograms.

Shark8

unread,
Mar 29, 2011, 2:34:10 PM3/29/11
to
On Mar 29, 12:22 pm, Florian Weimer <f...@deneb.enyo.de> wrote:
>
> I've written experimental code which depends on this, providing a
> syntactically convenient and efficient form of variadic subprograms.

Could you post an example thereof?

Florian Weimer

unread,
Mar 29, 2011, 3:35:08 PM3/29/11
to
* Shark8:

I think I already did, in a previous thread, but here's my example
code. I don't know why Value'Size is so large.

with System;
with Ada.Finalization;

package Variadic is

type Value is limited private;

function "+" (V : Integer) return Value;
function "+" (V : Long_Float) return Value;
function "+" (V : String) return Value;

type Argument_Array is array (Positive range <>) of Value;

procedure Call (Target : String; Arguments : Argument_Array);

private

type Value_Kind is (None, Integer_Kind, Float_Kind, String_Kind);

type String_Holder is new Ada.Finalization.Limited_Controlled with record
Pointer : System.Address;
Length : Natural;
end record;

procedure Finalize (X : in out String_Holder);

type Value_Internal (Kind : Value_Kind := None) is record
case Kind is
when None =>
null;
when Integer_Kind =>
Integer_Value : Integer;
when Float_Kind =>
Float_Value : Long_Float;
when String_Kind =>
String_Value : String_Holder;
end case;
end record;

type Value is limited record
Internal : Value_Internal;
end record;

end Variadic;

with Interfaces.C;
with Ada.Text_IO;

package body Variadic is

function "+" (V : Integer) return Value is
begin
return (Internal => (Kind => Integer_Kind, Integer_Value => V));
end "+";

function "+" (V : Long_Float) return Value is
begin
return (Internal => (Kind => Float_Kind, Float_Value => V));
end "+";

function "+" (V : String) return Value is
function C_Malloc (Size : Interfaces.C.Size_T) return System.Address;
pragma Import (C, C_Malloc, "malloc");
Pointer : constant System.Address := C_Malloc(V'Length);
subtype Str is String (V'Range);
S : Str;
for S'Address use Pointer;
begin
S := V;
return (Internal => (Kind => String_Kind,
String_Value =>
(Ada.Finalization.Limited_Controlled with
Pointer => Pointer,
Length => V'Length)));
end "+";

procedure Call (Target : String; Arguments : Argument_Array) is
use Ada.Text_IO;

package Integer_IO is new Ada.Text_IO.Integer_IO (Integer);
use Integer_IO;

package Float_IO is new Ada.Text_IO.Float_IO (Long_Float);
use Float_IO;

begin
Put ("CALL: ");
Put_Line (Target);
for J in Arguments'Range loop
Put (" ");
declare
Arg : Value_Internal renames Arguments (J).Internal;

begin
case Arg.Kind is
when None =>
Put ("NONE");
when Integer_Kind =>
Put ("INTEGER ");
Put (Arg.Integer_Value);
when Float_Kind =>
Put ("FLOAT ");
Put (Arg.Float_Value);
when String_Kind =>
Put ("STRING ");
declare
subtype Str is String (1 .. Arg.String_Value.Length);
S : Str;
for S'Address use Arg.String_Value.Pointer;
begin
Put (S);
end;
end case;
end;
New_Line;
end loop;
end Call;

procedure Finalize (X : in out String_Holder) is
procedure C_Free (Ptr : System.address);
pragma Import (C, C_Free, "free");
begin
C_Free (X.Pointer);
X.Pointer := System.Null_Address;
end Finalize;

end Variadic;

with Ada.Text_IO; use Ada.Text_IO;
with Variadic; use Variadic;

procedure Variadic_Test is
begin
Put ("Value'Size: ");
Put_Line (Integer'Image (Value'Size));
Call ("Foo", (+ 1, + 2.0, + "3."));
end Variadic_Test;

Randy Brukardt

unread,
Mar 29, 2011, 8:09:47 PM3/29/11
to
"Maciej Sobczak" <see.my....@gmail.com> wrote in message
news:f0c752a7-993e-4cee...@f18g2000yqd.googlegroups.com...

On 29 Mar, 05:16, "Randy Brukardt" <ra...@rrsoftware.com> wrote:

>> The motivating case is to make the containers better. Ada 2012 adds the
>> following to all of the containers:
>>
>> function Reference (Container : aliased in out Vector; Position : in
>> Cursor)
>> return Reference_Type;

>Out of curiosity - is it possible to leak the reference this way? I
>mean - is it possible for the caller to make a copy of returned
>reference and store it arbitrarily long?

No, because the attempt to make the copy will fail the accessibility check.

Specifically, the access discriminant has the lifetime of the containing
object. So if the object is short-lived (as most return objects are), the
access discriminant cannot be assigned into anything that lives longer.
OTOH, if the object is long-lived, there is no problem, because as long as
the object lives, attempting to add or remove elements from the container is
not allowed and must raise Program_Error.

There is are a couple of small holes that occur by using
Unchecked_Deallocation, but no one is going to do that by accident, and if
there is any sort of management (or sense) on a project, the end-around will
be easily detected.

>Note that the "copy" might not be obvious, as in:
>
>declare
> My_Element : Vector_Type.Reference_Type renames
> My_Vector.Reference (My_Cursor);
>begin
> My_Element.Comp := 10;
> My_Element.Other_Comp := 3.14;
>end;

This isn't a leak, because the Reference object has to continue to exist
until the renames goes away (and thus the reference). Instead, My_Vector is
locked against "tampering" so long as that object exists. So any attempt to
delete this element in this block body will raise Program_Error.

>The C++ equivalent of this is both a fantastic performance feature and
>a deadly security hole. How is this solved in Ada?

See above.
Randy.


Randy Brukardt

unread,
Mar 29, 2011, 8:12:43 PM3/29/11
to
"Florian Weimer" <f...@deneb.enyo.de> wrote in message
news:87lizxi...@mid.deneb.enyo.de...

>* AdaMagica:
>
>> Aliased parameters are meant to cure a deficiency in e.g. containers.
>> With current Ada, it's awkward to replace an element of a container.
>>
>> Now if you make the curser an aliased parameter, the function result
>> may dereference safely the cursor, since the cursor and the result are
>> tightly coupled,
>
> But this is already the case in Ada 2005. The lifetime of passed-in
> objects extends beyond the immediate need for evaluating the
> expression:
>
> | Leaving an execution happens immediately after its completion,
> | except in the case of a _master_: the execution of a body other than
> | a package_body; the execution of a statement; or the evaluation of
> | an expression, function_call, or range that is not part of an
> | enclosing expression, function_call, range, or simple_statement
> | other than a simple_return_statement.

This works in your example because you avoid the type system completely and
thus the accessibility checks. It also would work if you don't care that it
can raise Program_Error if someone passes in a local variable. That runtime
source of failure is not acceptable, and the use of alised parameters makes
it a compile-time check.

Randy.


Randy Brukardt

unread,
Mar 30, 2011, 3:44:33 PM3/30/11
to
"Randy Brukardt" <ra...@rrsoftware.com> wrote in message
news:imtscf$c6u$1...@munin.nbi.dk...
...

> There is are a couple of small holes that occur by using
> Unchecked_Deallocation, but no one is going to do that by accident, and if
> there is any sort of management (or sense) on a project, the end-around
> will be easily detected.

I should mention that there is a relatively easy way to defeat these
accessibility checks: simply use .all'Unchecked_Access. More generally, any
unchecked programming can of course default the protection. Nothing new
about that - that's true of any Ada construct. But of course it is obvious
that unchecked programming is being used, so again that is a management
problem.

Randy.


Florian Weimer

unread,
Apr 23, 2011, 2:47:16 PM4/23/11
to
* Randy Brukardt:

> The motivating case is to make the containers better. Ada 2012 adds the
> following to all of the containers:
>
> function Reference (Container : aliased in out Vector; Position : in
> Cursor)
> return Reference_Type;
>
> where Reference_Type is defined as:
>
> type Reference_Type (Element : not null access Element_Type) is private
> with
> Implicit_Dereference => Element;

Is it necessary that Element is a discriminant? If the aliased
business works with fields, you could write something like this:

type String_Reference is record
Data : access String;
end record;

function "+" (S: aliased String) return String_Reference is
begin
return String_Reference'(Data => S'Access);
end "+";

String_Reference could be part of an array, so we would get ragged
arrays as a side effect.

In any case, it seems to me that the definition of "master" in 7.6.1
needs updating.

Randy Brukardt

unread,
Apr 25, 2011, 3:19:35 AM4/25/11
to
"Florian Weimer" <f...@deneb.enyo.de> wrote in message
news:87aafge...@mid.deneb.enyo.de...

>* Randy Brukardt:
>
>> The motivating case is to make the containers better. Ada 2012 adds the
>> following to all of the containers:
>>
>> function Reference (Container : aliased in out Vector; Position : in
>> Cursor)
>> return Reference_Type;
>>
>> where Reference_Type is defined as:
>>
>> type Reference_Type (Element : not null access Element_Type) is
>> private
>> with
>> Implicit_Dereference => Element;
>
> Is it necessary that Element is a discriminant?

Yes, because access discriminants have special accessibility rules which
happen to have the right effect.

> If the aliased business works with fields, you could write something like
> this:
>
> type String_Reference is record
> Data : access String;
> end record;

You can write this, but you lose all accessibility checking if you do. The
accessibility of a normal component is that of the type, which is typically
library level. OTOH, a discriminant in a returned object has the
accessibility of the point of call; combined with the rules for aliased
parameters, such a discriminant will always succeed (no runtime checks or
overhead needed) and will always be safe (can't copy it into anything with a
longer lifetime, which is essentially anything).

I'm no fan of accessibility checks, but in this case at least they don't get
in the way beyond preventing operations that we don't want to allow in the
first place.

>In any case, it seems to me that the definition of "master" in 7.6.1 needs
>updating.

It did, but only for bugs. The access discriminant semantics is from Ada 95,
although it was never defined properly (probably still isn't, although not
for the lack to trying). We've just found a good use for the strange
semantics.

Randy.


Florian Weimer

unread,
Apr 28, 2011, 3:47:37 PM4/28/11
to
* Randy Brukardt:

>> Is it necessary that Element is a discriminant?
>
> Yes, because access discriminants have special accessibility rules which
> happen to have the right effect.

This is unfortunate because it means that this cannot be used to make
variadic argument list trick safer and less of a hack.

> It did, but only for bugs. The access discriminant semantics is from Ada 95,
> although it was never defined properly (probably still isn't, although not
> for the lack to trying). We've just found a good use for the strange
> semantics.

I don't think the difference is observable in Ada 95 because you
couldn't return new objects of limited type.

By the way, how tight are the access level checks? Is it relatively
safe to assume that if an Ada 2005 compiler compiles a program which
makes heavy use of anonymous access types and runs it without
exceptions, then there are no dangling pointers? (Ignoring unchecked
deallocation, of course.)

Randy Brukardt

unread,
Apr 28, 2011, 7:54:52 PM4/28/11
to
"Florian Weimer" <f...@deneb.enyo.de> wrote in message
news:87mxjaf...@mid.deneb.enyo.de...

>* Randy Brukardt:
>
>>> Is it necessary that Element is a discriminant?
>>
>> Yes, because access discriminants have special accessibility rules which
>> happen to have the right effect.
>
> This is unfortunate because it means that this cannot be used to make
> variadic argument list trick safer and less of a hack.

One could argue that variadic arguments are themselves a hack. :-)

This feature is intended for one particular use (and any other uses are a
happy accident): providing safe user-defined dereferencing. What is needed
for it to be safe is to prevent any copying of the access value while still
allowing it to be dereferenced (including assigning into it).

We originally had some syntax to define the accessibility of the returned
access type, but it was eventually pointed out that access discriminants
already had the appropriate accessibility. (Anonymous access return types
also have this same accessibility.) Thus we changed the mechanism to use the
discriminants rather than inventing a new feature.

The advantage of the aliased parameters is that they eliminate the runtime
checks by forcing the checks to the call site (where they can be statically
made 99% of the time).

>> It did, but only for bugs. The access discriminant semantics is from Ada
>> 95,
>> although it was never defined properly (probably still isn't, although
>> not
>> for the lack to trying). We've just found a good use for the strange
>> semantics.
>
> I don't think the difference is observable in Ada 95 because you
> couldn't return new objects of limited type.

Could be.

> By the way, how tight are the access level checks? Is it relatively
> safe to assume that if an Ada 2005 compiler compiles a program which
> makes heavy use of anonymous access types and runs it without
> exceptions, then there are no dangling pointers? (Ignoring unchecked
> deallocation, of course.)

The intent is that it is impossible to create a dangling pointer if no
unchecked programming is used. (Unchecked_Deallocation, 'Unchecked_Access,
Unchecked_Conversion, Address_to_Access_Conversions, abuse of
Unchecked_Unions, etc.) That goes for all access types (not just anonymous
ones). The problem, of course, is that it is impractical to do much without
using one of those things. (I've only succeeded in using 'Access once in one
of my programs; in all other cases I had to use 'Unchecked_Access.)

We're constantly fixing holes in the model, and it is easy to use the
unchecked things, so I wouldn't consider it impossible to get a dangling
pointer. (Personally, I prefer to hide pointers as much as possible, as in
the container cursors, so that dangling pointer detection becomes much more
possible, and their creation becomes less likely.)

Randy.


Florian Weimer

unread,
Apr 30, 2011, 2:32:04 PM4/30/11
to
* Randy Brukardt:

>> This is unfortunate because it means that this cannot be used to make
>> variadic argument list trick safer and less of a hack.
>
> One could argue that variadic arguments are themselves a hack. :-)

It would make it possible to call this little gem, PostgreSQL's main
client function for executing SQL statements,

PGresult *PQexecParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);

without any allocations and in a type-safe manner (for a predefined
set of types). For such untyped external interfaces, varidic
subprograms are often handy.

> The advantage of the aliased parameters is that they eliminate the runtime
> checks by forcing the checks to the call site (where they can be statically
> made 99% of the time).

I'm wondering if it is necessary that the returned limited record is
controlled, so that a reference counter can be incremented and later
decremented to ensure that the access discriminant does not become
dangling. That would make the whole thing a bit clumsy to use, and
come with quite a bit of run-time overhead.

> The intent is that it is impossible to create a dangling pointer if no
> unchecked programming is used. (Unchecked_Deallocation, 'Unchecked_Access,
> Unchecked_Conversion, Address_to_Access_Conversions, abuse of
> Unchecked_Unions, etc.) That goes for all access types (not just anonymous
> ones). The problem, of course, is that it is impractical to do much without
> using one of those things. (I've only succeeded in using 'Access once in one
> of my programs; in all other cases I had to use 'Unchecked_Access.)

Anonymous access types seem to help quite a bit. I use 'Access for
access discriminants, creating proxies, to fake the in-out parameter
mode for functions, and on locally defined callback functions.

> (Personally, I prefer to hide pointers as much as possible, as in
> the container cursors, so that dangling pointer detection becomes
> much more possible, and their creation becomes less likely.)

And implicit deference could make them even safer to use.

Randy Brukardt

unread,
Apr 30, 2011, 7:46:57 PM4/30/11
to
"Florian Weimer" <f...@deneb.enyo.de> wrote in message
news:87y62ra...@mid.deneb.enyo.de...
...

>> The advantage of the aliased parameters is that they eliminate the
>> runtime
>> checks by forcing the checks to the call site (where they can be
>> statically
>> made 99% of the time).
>
> I'm wondering if it is necessary that the returned limited record is
> controlled, so that a reference counter can be incremented and later
> decremented to ensure that the access discriminant does not become
> dangling. That would make the whole thing a bit clumsy to use, and
> come with quite a bit of run-time overhead.

It's not required, but that is the way it will be used in the containers (so
that the tampering check can apply only so long as the access exists). One
hopes that compilers will work to minimize the overhead in this case (the
non-list finalization implementation that GNAT is supposedly getting will be
ideal for such cases).

>> The intent is that it is impossible to create a dangling pointer if no
>> unchecked programming is used. (Unchecked_Deallocation,
>> 'Unchecked_Access,
>> Unchecked_Conversion, Address_to_Access_Conversions, abuse of
>> Unchecked_Unions, etc.) That goes for all access types (not just
>> anonymous
>> ones). The problem, of course, is that it is impractical to do much
>> without
>> using one of those things. (I've only succeeded in using 'Access once in
>> one
>> of my programs; in all other cases I had to use 'Unchecked_Access.)
>
> Anonymous access types seem to help quite a bit. I use 'Access for
> access discriminants, creating proxies, to fake the in-out parameter
> mode for functions, and on locally defined callback functions.

Well, of course for "in out", just use that if you are using Ada 2012 -- no
need to fake it.

The other uses of course will remain.

>> (Personally, I prefer to hide pointers as much as possible, as in
>> the container cursors, so that dangling pointer detection becomes
>> much more possible, and their creation becomes less likely.)
>
> And implicit deference could make them even safer to use.

Exactly. And more convinient, too.

I would like to see containers use to be as easy as using access types; if
that is true, then there is little reason to use the less safe access types
to create lists and trees (and maps and sets). There always will be cases
not covered by containers or where performance needs are ultra-critical --
but those should be the unusual cases. Ada 2012 definitely moves us closer
to that goal.

Randy.


0 new messages