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

GCC 11 bug? lawyer needed

195 views
Skip to first unread message

Simon Wright

unread,
May 3, 2021, 12:08:22 PM5/3/21
to
This code results in the error shown:

1. package Aliased_Tagged_Types is
2.
3. type T is tagged null record;
4.
5. function P (Param : aliased T) return Boolean
6. is (False);
7.
8. function F (Param : T) return Boolean
9. is (Param.P);
|
>>> actual for explicitly aliased formal is too short lived

10.
11. end Aliased_Tagged_Types;

The compiler code that results in this error is at sem_ch4.adb:1490, and
was introduced for Ada202x accessibiity checking reasons.

-- Check whether the formal is aliased and if the accessibility
-- level of the actual is deeper than the accessibility level
-- of the enclosing subprogam to which the current return
-- statement applies.

[...]

if Is_Explicitly_Aliased (Form)
and then Is_Entity_Name (Act)
and then Static_Accessibility_Level
(Act, Zero_On_Dynamic_Level)
> Subprogram_Access_Level (Current_Subprogram)
then
Error_Msg_N ("actual for explicitly aliased formal is too"
& " short lived", Act);
end if;

----

For those interested, this issue affects Alire.

Randy Brukardt

unread,
May 4, 2021, 11:54:46 PM5/4/21
to
We spent a lot of time and effort in the ARG talking about this case (see
AI12-0402-1). The Ada 2012 RM does indeed say this case is illegal. The
reason is that aliased parameters are designed so that one can return part
of of them in the return object of the function. And a normal parameter is
assumed to be local (since its accessibility is unknown) - that means it is
too local for an aliased parameter of a function that is used in some
non-local way (including being returned from a non-local function).

However, since one cannot return a part of a parameter for a function that
returns an elementary type (other than anonymous access returns, which have
special rules anyway), we added an exception to the rules for that case in
Ada 202x. (We tried a number of more liberal exceptions, but they were
complex and had [unlikely] holes.) So the most current rule is that the call
of P is legal.

That wasn't decided until the December ARG meeting, so it happened after the
GNATPro 21 release (and I expect that the GNAT CE is derived from that
version). And I'd guess that in Ada 2012 mode, this check would remain as it
is (the change was not made retroactively - not sure why).

Randy.

"Simon Wright" <si...@pushface.org> wrote in message
news:lyh7jjz...@pushface.org...

AdaMagica

unread,
May 5, 2021, 6:01:07 AM5/5/21
to
Randy Brukardt schrieb am Mittwoch, 5. Mai 2021 um 05:54:46 UTC+2:
> And a normal parameter is assumed to be local (since its accessibility is unknown) - that means it is
> too local for an aliased parameter of a function that is used in some non-local way

RM 3.10(9/3): Finally, a formal parameter or generic formal object of a tagged type is defined to be aliased.
RM 6.4.1(6/3): If the formal parameter is an explicitly aliased parameter, the type of the actual parameter shall be tagged or the actual parameter shall be an aliased view of an object.
Both of these conditions are fulfilled here.
There are many more places about explicitly aliased parameters in the RM. I've read them all. It left me wondering.

I do not see what aliasing a tagged parameter buys. A parameter of a tagged typed is aliased per se, or do I misread the RM.
I'm having big problems trying to understand the RM.
I will try to grock the AI.

AdaMagica

unread,
May 5, 2021, 12:10:03 PM5/5/21
to
AdaMagica schrieb am Mittwoch, 5. Mai 2021 um 12:01:07 UTC+2:
> I will try to grock the AI.
Hm, I'm still confused. Can anyone please come up with some examples that explain what this is all about?

Randy Brukardt

unread,
May 5, 2021, 8:39:14 PM5/5/21
to
"AdaMagica" <christ-u...@t-online.de> wrote in message
news:aaa58296-3298-4b70...@googlegroups.com...
See 6.4.1(6/3): there is an accessibility check on the actual parameter of
an aliased parameter. This allows an aliased parameter to have the
accessibility of the return object of a function, rather than local
accessibility. There's a bunch of rules in 3.10.2 that combine to have the
right effect.

You see the result in an operation like "Reference" in the containers. If
you have:

function Foo (A : in out Container; Idx : in Natural) return access
Element;

then an implementation of:

function Foo (A : in out Container) return access Element is
begin
return A.Data(Idx)'Access; -- (1)
end Foo;

(1) is illegal, as A has local to Foo accessibility, while the anonymous
access has the accessibility of the return object (the point of call), which
is necessarily outside of Foo.

You can change (1) to:
return A.Data(Idx)'Unchecked_Access; -- (1)
but now you can create a dangling pointer, for instance if Foo is assigned
to a library-level access type and the actual for A is not library-level.

But you can change the parameter to "aliased", then the accessibility check
is moved to the call site (where it must always suceeed for the vast
majority of calls). There's no accessibility check at (1) in that case
(which could be at best a dynamic check, which is a correctness hazard, and
also has an overhead cost). And you still have the safety of not being able
to create a dangling pointer.

It is a bit weird that this property is tied to "aliased" parameters. This
property came first, and we discussed the syntax to use for a long time.
Eventually it was decided to call them "aliased" parameters, but of course
that meant it was necessary to generalize the usages.

This special rule does have the downside of being able to fail in some safe
cases, like the one noted by the OP. That doesn't happen for procedures,
since aliased parameters have no special semantics for procedures. We
decided to remove the special semantics for functions for which it is
impossible to return a part of the parameter (that is, any
elementary-returning function), as that special semantics provides no
benefit in such a case (but it does have a cost).

I agree that the original author of that program should not have used
"aliased" in the way that they did (they don't need the special semantics),
but we realize that some people would prefer to *explicitly* mark things as
aliased when they are going to take 'Access (and not worry about the type of
the parameter -- after all, it could change). That is, they don't want to
depend on the implicit behavior of tagged types -- or perhaps they don't
even know about it. Which leads to the problem that occurs here, as
"aliased" has slightly different meanings for functions (now just composite
functions) and procedures.

Since this is real code that didn't work as expected, it seemed to make
sense to reduce the problem with a minor language tweak.

Randy.




AdaMagica

unread,
May 6, 2021, 9:07:25 AM5/6/21
to
Thank you, Randy, for the nice explanation. There're still some hazy places, but I begin to see the big picture.
Christoph

Simon Wright

unread,
May 6, 2021, 4:02:58 PM5/6/21
to
"Randy Brukardt" <ra...@rrsoftware.com> writes:

> I agree that the original author of that program should not have used
> "aliased" in the way that they did (they don't need the special semantics),
> but we realize that some people would prefer to *explicitly* mark things as
> aliased when they are going to take 'Access (and not worry about the type of
> the parameter -- after all, it could change). That is, they don't want to
> depend on the implicit behavior of tagged types -- or perhaps they don't
> even know about it. Which leads to the problem that occurs here, as
> "aliased" has slightly different meanings for functions (now just composite
> functions) and procedures.

The original code, from the Alire project, had (I've edited it slightly)

package Holders
is new Ada.Containers.Indefinite_Holders (Node'Class);

type Tree is
new Holders.Holder
and ...

function Root (This : Tree) return Node'Class is
(This.Constant_Reference);

where that Constant_Reference is inherited (eventually) from
Ada.Containers.Indefinite_Holders.Holder,

function Constant_Reference
(Container : aliased Holder) return Constant_Reference_Type;
pragma Inline (Constant_Reference);

Shame it had to be there.

I've just tried splattering 'aliased' wherever the compiler told me it
was needed; it's now spreading into other packages. Ugh.

The solution might just be using composition rather than inheritance.

Dmitry A. Kazakov

unread,
May 6, 2021, 4:51:44 PM5/6/21
to
In my experience mixing handles with target types does not work anyway
regardless accessibility rules mess.

I tend to use interfaces instead:

type Abstract_Node_Interface is interface ...;

Then both the handle and the target type implement
Abstract_Node_Interface. The target type goes into hiding, the client
need not to see it.

This requires manual delegation in all primitive operations of handles:
dereference + call. But in the end it pays off. Especially with trees,
because in mutator operations I can check the reference count of the
node and choose to clone it (and maybe the subtree) if there are
multiple external handles to it.

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

Randy Brukardt

unread,
May 6, 2021, 7:59:31 PM5/6/21
to
"Simon Wright" <si...@pushface.org> wrote in message
news:lylf8ry...@pushface.org...
Constant_Reference is the case for which these semantics was designed. Hard
to avoid it there. ;-)

Note that by returning Node'Class rather than an elementary type, you don't
get to use the new rule tweak. Since all tagged types are by-reference (not
by copy), the "Root" routine has to return the object that it has, which
ultimately is part of Tree. So you actually need "aliased" on Root, since
you are (ultimately) returning a part of the formal parameter (and which
could become dangling if you pass in an object which is too local).

> I've just tried splattering 'aliased' wherever the compiler told me it
> was needed; it's now spreading into other packages. Ugh.

I think you need to make a copy of the return object somewhere; the obvious
answer is to replace function Constant_Reference with function Element. Of
course, if the return object is large enough, that could be expensive. (That
doesn't work if you want to write the node, but the use of
Constant_Reference doesn't allow that anyway, so in this case it doesn't
matter.)

> The solution might just be using composition rather than inheritance.

Yeah, or using handles more as Dmitry says. In any case, it seems like some
redesign is necessary.

Randy.


Simon Wright

unread,
May 8, 2021, 6:17:21 AM5/8/21
to
"Randy Brukardt" <ra...@rrsoftware.com> writes:

> I think you need to make a copy of the return object somewhere; the
> obvious answer is to replace function Constant_Reference with function
> Element.

That appears to be a fine workround! Thanks!
0 new messages