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

Containers, dangling references

121 views
Skip to first unread message

Simon Wright

unread,
Mar 9, 2020, 12:43:58 PM3/9/20
to
I've been working on checking the upcoming FSF GCC 10 against existing
projects.

One case is a set of containers which include reference types (which,
under the hood, support for example the "for all X of Y" iteration
style).

The original version of the code looked like

type Reference_Type
(Element : not null access Element_Type)
is record
Dummy : Integer := raise Program_Error with "uninitialized reference";
end record;

(this is the full declaration; Dummy is there so that default
initialization will raise PE, as required, e.g. ARM A.18.2(147.4)).

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

with implementation

function Reference (C : aliased in out Container; Position : in Cursor)
return Reference_Type
is
pragma Unreferenced (C);
begin
return (Element => Position.The_Node.all.The_Element'Access, Dummy => 1);
end Reference;

which was fine with compilers up to FSF GCC 9, GNAT CE 2019. With GCC
10, we get

references.adb:8:26: access discriminant in return aggregate would be
a dangling reference

I was a bit puzzled by the Position.The_Node.all.The_Element'Access -
why the .all? It turns out that if you remove it, the compilers that
were happy are no longer. Perhaps this was some circuitry in GNAT to
suppress this error?

How do the Ada.Containers manage this? It turns out that GNAT's
version is more like

return R : constant Reference_Type :=
(Element => Position.The_Node.The_Element'Access, Dummy => 1)
do
null;
end return;

and this is fine in GCC 10 (but if you put in the .all the error
message returns).

I found where the error message is raised[1], but it's hard to tell
what the compiler is actually checking for.

[1] https://github.com/gcc-mirror/gcc/blob/master/gcc/ada/sem_ch6.adb#L858

Randy Brukardt

unread,
Mar 9, 2020, 7:19:32 PM3/9/20
to
"Simon Wright" <si...@pushface.org> wrote in message
news:lya74pb...@pushface.org...
...
> with implementation
>
> function Reference (C : aliased in out Container; Position : in Cursor)
> return Reference_Type
> is
> pragma Unreferenced (C);
> begin
> return (Element => Position.The_Node.all.The_Element'Access, Dummy =>
> 1);
> end Reference;

The language rules are designed to allow returning part of an aliased
parameter as the return from a function. But one could also return a part of
the designated object of a library-level access type.

Thus, what matters here is the type declaration of "The_Node", which would
need to be a library-level access type (or at least instance-level) in order
of this to work.

I note this implementation is missing the check that the cursor is for an
element in the provided container (and isn't null). Perhaps you simplified
the implementation for this presentation.

This sort of expression is a tough one to deal with for compilers. Janus/Ada
used to (and maybe still does :-) have problems with the accessibility of
expressions that have multiple access types. It would use the wrong type to
determine the accessibility. Perhaps something similar is happening for
GNAT. It's also suspicious that the explicit .all changes the result from
the implicit .all. That suggests a bug more than an intended implementation.

Randy.


Simon Wright

unread,
Mar 10, 2020, 2:07:27 PM3/10/20
to
"Randy Brukardt" <ra...@rrsoftware.com> writes:

> "Simon Wright" <si...@pushface.org> wrote in message
> news:lya74pb...@pushface.org...
> ...
>> with implementation
>>
>> function Reference (C : aliased in out Container; Position : in Cursor)
>> return Reference_Type
>> is
>> pragma Unreferenced (C);
>> begin
>> return (Element => Position.The_Node.all.The_Element'Access, Dummy =>
>> 1);
>> end Reference;
>
> The language rules are designed to allow returning part of an aliased
> parameter as the return from a function. But one could also return a
> part of the designated object of a library-level access type.

Is there any difference between an ordinary return & an extended return?
Looking at AARM 6.5(5.8/3), I think not?

> Thus, what matters here is the type declaration of "The_Node", which
> would need to be a library-level access type (or at least
> instance-level) in order of this to work.

There are all at library level.

type Node_Type is limited record
The_Element : aliased Element_Type;
end record;
type Node_Access is access Node_Type;

type Container is tagged record
A_Node : Node_Access;
end record;
type Container_Access is access all Container;

type Cursor is record
The_Container : Container_Access;
The_Node : Node_Access;
end record;

> I note this implementation is missing the check that the cursor is for
> an element in the provided container (and isn't null). Perhaps you
> simplified the implementation for this presentation.

A mixture of this and an incomplete original implementation.

> This sort of expression is a tough one to deal with for
> compilers. Janus/Ada used to (and maybe still does :-) have problems
> with the accessibility of expressions that have multiple access
> types. It would use the wrong type to determine the
> accessibility. Perhaps something similar is happening for GNAT. It's
> also suspicious that the explicit .all changes the result from the
> implicit .all. That suggests a bug more than an intended
> implementation.

That's what I thought.

Randy Brukardt

unread,
Mar 10, 2020, 4:28:36 PM3/10/20
to
"Simon Wright" <si...@pushface.org> wrote in message
news:ly5zfca...@pushface.org...
> "Randy Brukardt" <ra...@rrsoftware.com> writes:
...
>> The language rules are designed to allow returning part of an aliased
>> parameter as the return from a function. But one could also return a
>> part of the designated object of a library-level access type.
>
> Is there any difference between an ordinary return & an extended return?
> Looking at AARM 6.5(5.8/3), I think not?

Should not matter. We want it to be easily possible to replace one with the
other, since it often happens that one needs to do more (or less) during a
return when maintaining a function.

Randy.


0 new messages