Version 1.1 of acs/ac-00254.txt

Unformatted version of acs/ac-00254.txt version 1.1
Other versions for file acs/ac-00254.txt

!standard A.5.3(47)          13-10-24 AC95-00254/00
!class confirmation 13-10-24
!status received no action 13-10-24
!status received 13-10-11
!subject Parenthesized expressions
!summary
!appendix

From: Steve Baird
Sent: Friday, October 11, 2013  7:55 PM

One reasonably substantive question followed by two corner case nits.

[Editor's note: Each question is handled separately.]

2) Does/should a parenthesized expression yield a constant view
    in the same way that a qualified expression does?

    procedure Foo is
      type Root is tagged null record;
      type Ref is access all Root;
      Ptr : Ref;
      Var : aliased Root;
    begin
       Ptr := Var'Access; -- legal
       Ptr := Root (Var)'Access; -- legal
       Ptr := Root'(Var)'Access; -- illegal
       Ptr := Root ((Var))'Access; -- ???
    end;

   Thanks to Randy for pointing out the parallel with qualified
   expressions.

***************************************************************

From: Randy Brukardt
Sent: Friday, October 18, 2013  6:26 PM

> One reasonably substantive question followed by two corner case nits.

Grumbling again about sticking a bunch of unrelated things together.

...

>    Thanks to Randy for pointing out the parallel with qualified
>    expressions.

Thinking about this tonight as I look at what to do with all of the open issues
in my inbox, I'm not so sure. It's clear that with the current definition of the
language, it is not a constant view. That's because there is an exhaustive list
of constant views in 3.3(15-22) and the words "parenthesized expression" aren't
there anywhere.

Then, based on 6.4.1(5), it would appear that the following has always been
legal in Ada:

    procedure Bar (P : in out Integer);

    Var : Integer;

    Bar (Integer((Var))); -- Apparently legal.

as Var is obviously a variable, 3.3 does not make a parenthesized expression of
a variable a constant, and Integer((Var)) is clearly a name. The rules in 6.2
clearly define the object associated with this call, so there is no semantic
problem here.

Note that in any case there is something wrong with 3.3(15-22), since at the
very least a parenthesized expression of a constant had better be a constant
view, and those words are missing from what is supposed to be an exhaustive
list. (Taken literally, Integer((2)) is a variable view, and I'm pretty sure we
can agree that this violates the Dewar rule.)

The interesting question is whether the case I gave above actually is expected
to be legal by compilers. If so, then we cannot consider making parenthesized
expressions to always be a constant view (as that would be incompatible, and we
would have no compelling justification for that incompatibility). Otherwise, of
course, we should make it a constant view so that the wording matches reality.

Someone ought to write a test and distribute it as a compiler survey (and
"someone" ought to be Steve, the original asker of the question).

***************************************************************

From: Tucker Taft
Sent: Friday, October 18, 2013  8:19 PM

AdaMagic and its derivatives (i.e. ObjectAda, GHS AdaMulti, and SHARKAda)
consider a parenthesized expression to be a constant:

     1 procedure paren_is_const(X : in out Integer) is
     2 begin
     3     paren_is_const(X);  -- no error
     4     paren_is_const((X));  -- error
                           *
*****Error: LRM:6.4.1(5) If the mode is IN OUT or OUT, the actual shall be a
*****        name that denotes a variable, Continuing
     5 end paren_is_const;

For what that is worth.

***************************************************************

From: Ed Schonberg
Sent: Saturday, October 19, 2013  7:46 AM

GNAT is of the same opinion:

Aracataca-2:ARG schonberg$ gcc -c -gnatv paren_is_const.adb

GNAT Pro 7.2.0w (20131015)
Copyright 1992-2013, Free Software Foundation, Inc.

Compiling: paren_is_const.adb (source file time stamp: 2013-10-19 12:42:23)

     3.      paren_is_const (X);  -- no error
             |
        >>> warning: possible infinite recursion
        >>> warning: Storage_Error may be raised at run time

     4.      paren_is_const ((X));  -- error
                                              |
        >>> actual for "X" must be a variable

 7 lines: 1 error, 2 warnings

***************************************************************

From: Randy Brukardt
Sent: Saturday, October 19, 2013  4:03 PM

Umm, "(X)" is not a Name, so that would fail that check. Meaning that test case
doesn't answer the question. You have to check it using a (view) type
conversion:

procedure paren_is_const(X : in out Integer) is begin
    paren_is_const(X);  -- OK
    paren_is_const((X));  -- Definitely illegal by RM - (X) is not a Name.
    paren_is_const(Integer((X))); -- ??? Not illegal by RM, (X) is not a constant view.
end paren_is_const;

-- ??? appears to be allowed by the RM, the question is whether it should be.

                      Randy.

P.S. Using the parameter itself this way is clever. I thought this would take a
substantially longer example...

P.P.S. Janus/Ada reports "Cannot modify a constant" for both of the lines above,
so it supports the conclusion that (X) should be a constant view.

P.P.P.S. Steve's original example of "Root ((Var))'Access;" causes Janus/Ada to
crash -- apparently the internal predicate Is_Aliased doesn't have code to allow
a parenthesized expression, so we get a variant failure.

***************************************************************

From: Ed Schonberg
Sent: Saturday, October 18, 2013  8:40 PM

> Umm, "(X)" is not a Name, so that would fail that check. Meaning that
> test case doesn't answer the question. You have to check it using a
> (view) type
> conversion:
>
> procedure paren_is_const(X : in out Integer) is begin
>    paren_is_const(X);  -- OK
>    paren_is_const((X));  -- Definitely illegal by RM - (X) is not a Name.
>    paren_is_const(Integer((X))); -- ??? Not illegal by RM, (X) is not
> a constant view.
> end paren_is_const;
>
> -- ??? appears to be allowed by the RM, the question is whether it
> should be.

Rejected as well by GNAT. I would say that if the construct is not a name, its
view conversion cannot make it into one.

Aside: Isn't parenthesizing an old FORTRAN trick to get a constant view of an
object? Given that parameter passing is always by reference, parenthesizing a
name  makes into an expression. As an actual an expression is always passed as a
temporary, and therefore the original name is protected from whatever
assignments might take place in the called subroutine.

***************************************************************

From: Van Snyder
Sent: Saturday, October 19, 2013  8:59 PM

> Aside: Isn't parenthesizing an old FORTRAN trick to get a constant
> view of an object? Given that parameter passing is always by
> reference, parenthesizing a name makes into an expression. As an
> actual an expression is always passed as a temporary, and therefore
> the original name is protected from whatever assignments might take
> place in the called subroutine.

Right.  An expression isn't a variable in Fortran, unless it's a reference to a
function that has a pointer result.

***************************************************************

From: Tucker Taft
Sent: Sunday, October 20, 2013  6:55 PM

> Rejected as well by GNAT. I would say that if the construct is not a name, its
> view conversion cannot make it into one.

That is certainly true.  A view conversion requires the operand to be
syntactically a "name" -- see 4.6(5/2).

AdaMagic rejects the type-conversion example as well.

***************************************************************

From: Steve Baird
Sent: Monday, October 21, 2013  10:34 AM

> Rejected as well by GNAT.
> I would say that if the construct is not a name, its view conversion cannot
> make it into one.

Sounds right to me, but GNAT accepts the example given in the original mail
message (after removing the other irrelevant cases):

    procedure Foo is
      type Root is tagged null record;
      type Ref is access all Root;
      Ptr : Ref;
      Var : aliased Root;
    begin
       Ptr := Root ((Var))'Access; -- accepted by GNAT
    end;

***************************************************************

From: Jon S. Squire
Sent: Monday, October 21, 2013  10:56 AM

Add a simple rule:
"Syntactically redundant parenthisis shall be removed prior to semantic
checking."
(((a)*(b))+c) should be users choice.

From NRG viewpoint.

***************************************************************

From: Randy Brukardt
Sent: Monday, October 21, 2013   5:19 PM

...
> That is certainly true.  A view conversion requires the operand to be
> syntactically a "name" -- see 4.6(5/2).

That triggers a resolution rule 4.6(7), which seems problematical, as it means
that you can distinguish overloading depending on whether a type conversion can
be a view conversion. That seems like something that is too "language-lawyerly"
to be a resolution rule -- the only cases where it would matter are pathologies.
It would make much more sense to me if it was a legality rule. I wonder if
compilers actually do this as per the standard (both for view conversions and
for the similar rule for regular parameter passing (6.4.1(4)) -- another rule
that surprises me as going too far.

I find the type conversion rule especially worrisome because it means that it's
never possible to write a conversion of a tagged value in Ada 95 or Ada 2005:

     T'Class ((X)); -- Illegal, can't be a view conversion.
     T'Class (NT'(Comp => ...)); -- Ought to be illegal in Ada 95 and Ada 2005, can't be a view conversion.

This latter form is used to pass a tagged value into a context where we need
dispatching; I'm pretty sure I've written such things and am surprised to find
out that they are illegal. Luckily (?), Ada 2012 makes qualified expressions a
name, so this is now legal. (Note that we said it was OK to make qualified
expressions a name because they could *always* be made into a name by wrapping
them in a type conversion, but that is clearly not true for tagged types because
of 4.6(5/2) and 4.6(7).)

(It's interesting that making qualified expressions into a name is potentially
incompatible or even inconsistent for this reason. This means that that change
is incorrectly documented in the Ada 2012 AARM as only an "extension".)

----------------------

Here's a test program using 4.6(7) and 6.4.1(4) for resolution (note that there
are several cases where the expected result changes for Ada 2012 compared to Ada
2005):

with Report;
procedure Tuck5 is
    -- Check whether 4.6(7) and 6.4.1(4) are properly used for resolution.


    function F return Integer is
    begin
        return 1;
    end F;

    function F return Character is
    begin
        return 'A';
    end F;

    procedure Proc1 (A : in Integer) is
    begin
        if A /= 1 then
          Report.Failed ("Unexpected parameter value (Proc1)");
        end if;
    end Proc1;

    procedure Proc1 (A : in out Character) is
    begin
        Report.Failed ("Character form resolved (Proc1)");
    end Proc1;

    procedure Proc2 (A, B : in Integer) is
    begin
        if A /= 1 then
            Report.Failed ("Unexpected parameter value (Proc2)");
        end if;
    end Proc2;

    procedure Proc2 (A : in out Integer; B : in Character) is
    begin
        Report.Failed ("Character form resolved (Proc2)");
    end Proc2;

    X : Integer := 10;

    package P is
      type T is tagged null record;

      procedure Proc3 (A : in T);
      XT : T;
    end P;

    package body P is
      procedure Proc3 (A : in T) is
      begin
          null;
      end Proc3;
    end P;

begin
    Report.Test ("Tuck5", "Check whether 4.6(7) and 6.4.1(4) are properly used for resolution");

    -- Test 6.4.1(4): The following expression should resolve
    -- because the parameter expression is not a name, and thus the call
    -- with an "in out" parameter will not match.
    Proc1 ((F));
    -- Proc1 (F); -- Ambiguous
    -- For Ada 95 and Ada 2005, the following expression should resolve
    -- because the parameter expression is not a name, and thus the call
    -- with an "in out" parameter will not match; but for Ada 2012, this *is* a
    -- name, and thus the call will be ambiguous.
    Proc2 (Integer'(X), F);
    -- Test 4.6(7): The following expression should resolve
    -- because the operand of the type conversion is not a name, and thus the
    -- conversion cannot be a view conversion, thus a call with an
    -- "in out" parameter will not match.
    Proc2 (Integer((X)), F);
    -- For Ada 95 and Ada 2005, the following expression should resolve
    -- because the operand of the type conversion is not a name, and thus the
    -- conversion cannot be a view conversion, thus a call with an
    -- "in out" parameter will not match; but for Ada 2012, this *is* a
    -- name, and thus the call will be ambiguous.
    Proc2 (Integer(Integer'(X)), F);
    -- For Ada 95 and Ada 2005, the following expression should not resolve
    -- because the operand of the type conversion is not a name, and thus the
    -- conversion cannot be a view conversion. But all conversions of tagged
    -- types are view conversions; but for Ada 2012, this *is* a
    -- name, and thus this is call is legal.
    P.Proc3 (P.T'Class (P.T'(null record)));
    -- The following expression should not resolve
    -- because the operand of the type conversion is not a name, and thus the
    -- conversion cannot be a view conversion. But all conversions of tagged
    -- types are view conversions.
    P.Proc3 (P.T'Class ((P.XT)));
    -- The following expression should resolve:
    P.Proc3 (P.T'Class (P.XT));

    Report.Result;
end Tuck5;

For what it's worth, Janus/Ada rejects all of the Proc1 and Proc2 calls as
ambiguous, and allows all of the Proc3 calls. (Ergo, it does not use the
differentiation between "name" and "expression" for resolution purposes.) It
would be interesting to find out if we were the only ones to be sloppy in this
way or whether this is widespread (and especially if the language mode changes
the behavior as it is supposed to).

P.S. The fact that the above resembles an ACATS C-Test is not coincidental; it
seems to me if we find this behavior important enough to require, it should be
tested in the ACATS. Unless we think it is pathological -- in which case we
ought not be using it to reason about parenthesis.

***************************************************************

From: Tucker Taft
Sent: Monday, October 21, 2013  7:34 PM

> Tucker Taft writes:
> ...
>> That is certainly true.  A view conversion requires the operand to be
>> syntactically a "name" -- see 4.6(5/2). ...

Actually, I wasn't agreeing with Ed completely.  All conversions are names,
syntactically. Ed said a view conversion can't turn a non-name into a name.  The
real rule is that a view conversion *requires* that its operand be syntactically
a name.  A *value conversion* can turn something that is not a name
syntactically into a name, but in so doing it creates a name of a *value* rather
than an object (a value conversion is never considered an object per 3.3(2-12)).
Note that a parenthesized expression is also not an object (according to
3.3(2-12) again).

Ed's "rule" might better be stated that a view conversion cannot change the name
of a non-object into the name of an object (again per 3.3(2-12)), and only
objects can be variables.

***************************************************************

From: Randy Brukardt
Sent: Monday, October 21, 2013  7:59 PM

Yes, I figured all of that out. What does that have to do with my questioning as
to why that is a *resolution* rule rather than a *legality rule*?? By making it
a resolution rule, there are all kinds of weird programs that are (supposedly)
allowed.

I think the differentiation between view conversions and value conversions (and
between "name" and "expression") is too subtle to be a resolution rule; I wrote
a program to show some of the weird results (it would be interesting to see what
AdaMagic and GNAT for Ada 2012 does on it -- the older GNAT I have installed
gives the same results as Janus/Ada -- name vs. expression is *not* used for
resolution).

***************************************************************

From: Tucker Taft
Sent: Monday, October 21, 2013  8:16 PM

The RM resolution rule in 4.6 says:

   The operand of a view conversion is interpreted only as a name;
   the operand of a value conversion is interpreted as an expression.

This is not the same as a resolution rule saying "the operand of a view
conversion *shall* be a name" which would imply you use it for overload
resolution.  Instead it is saying that *if* you have an operand that could be
interpreted *semantically* either as a name or as an expression, in a view
conversion it is interpreted only as a name.  So this is resolving an ambiguity
of resolution, so it is reasonable to call it a resolution rule, but it isn't
like most of the other resolution rules.

***************************************************************

From: Randy Brukardt
Sent: Monday, October 21, 2013  9:29 PM

One of the nice things about Ada is that one rarely has the feeling that rules
are being made up on the fly. Not only is the language usually clear, but the
rules are separated in the categories with clearly different meanings, and
exceptions to the usual rules are almost always documented in the AARM.

Unfortunately, the above sounds much too much like making up the rules on the
fly. I don't know of anywhere where this apparently hair-splitting difference
between "shall be" and "interpreted as" is documented. Certainly, if I see
something given as a resolution rule, I expect it to be usable in resolution --
otherwise there is no need to give the rule. (Especially as most resolution
rules don't use "shall" anyway; they talk about expected types.)

I can't for the life of me figure out why it would matter how the expression is
interpreted. The AARM note says that it "formally resolves the ambiguity", but
that only begs the question why there is an ambiguity in the syntax in the first
place (name always being a subset of expression).

Anyway, there seems to be a tiny bit of support for your interpretation in
6.4.1(4.a) [there's none in 4.6(7.a)], in that it says without any justification
that "we don't actually require that the actual be a name". I say without
justification, because I don't see any difference between "shall be" and
"interpreted as" in an English sense, so for there to be any difference, there
would have to be a rule somewhere saying that it doesn't matter.

Do you know of any justification for this hair-splitting? (I.e. some existing RM
or AARM language that says this?) I would have expected a giant RM Ramification:
"This rule has no impact on resolution; the fact that some operand is not a name
cannot be used to eliminate possibilities from a set of possible solutions." or
something like that on these rules to make sure no one thinks that they ought to
be used for resolution purposes.

P.S. At least, it appears that we agree that these shouldn't be used for
resolution purposes. In that case, the original question is irrelevant, as (X)
is not (a view of) an object, and as such, it can't be either a constant or
variable (object or view). It's kinda like asking if a carrot is male or female.
:-) As such, T((X))'access is illegal because the prefix is not an object. Not
that compilers necessarily can figure that out, nobody has ever dreamt up this
particular oddity before.

***************************************************************

From: Tucker Taft
Sent: Tuesday, October 22, 2013  12:29 AM

> Unfortunately, the above sounds much too much like making up the rules
> on the fly. ...

It is true that a name is a subset of an expression syntactically, but when a
name is interpreted as a primary, there is an implicit evaluation, as implied by
4.4(10):

   The value of a primary that is a name denoting an object is the value of the
   object.

We want to suppress that for a view conversion, particularly for a view
conversion that is an OUT parameter.  That is why we are saying the operand of a
view conversion is interpreted "only as a name."

In any case, as you point out, we basically agree about the original issue.  I
believe I am correctly explaining the history behind the wording on view
conversion, and I agree it is probably a unique sort of resolution rule.

***************************************************************

From: Ed Schonberg
Sent: Tuesday, October 22, 2013  10:14 AM

> For what it's worth, Janus/Ada rejects all of the Proc1 and Proc2
> calls as ambiguous, and allows all of the Proc3 calls. (Ergo, it does
> not use the differentiation between "name" and "expression" for
> resolution purposes.) It would be interesting to find out if we were
> the only ones to be sloppy in this way or whether this is widespread
> (and especially if the language mode changes the behavior as it is supposed to).

Maybe it's worth more if GNAT does the same?  GNAT rejects as ambiguous the
calls to Proc1 and Proc2 as well.  I would never have thought that 6.4.1 (4) was
a resolution rule, and obviously no one around here ever thought that  this
might be the case. A change here would be a major surprise to Ada users
(fortunately no one ever wrote code that depended on this reading).

***************************************************************

From: Randy Brukardt
Sent: Tuesday, October 22, 2013  1:25 PM

Thanks for checking, Ed. I think I'll make a note to add an AARM note to this
effect after both 4.6(7) and 6.4.1(4) - it would be good if there is no more
confusion on this point (even if it is primarily from language laywers).

***************************************************************

From: Randy Brukardt
Sent: Thursday, October 24, 2013  9:42 PM

> > Rejected as well by GNAT.
> > I would say that if the construct is not a name, its view conversion
> > cannot make it into one.
>
> Sounds right to me, but GNAT accepts the example given in the original
> mail message (after removing the other irrelevant cases):
>
>     procedure Foo is
>       type Root is tagged null record;
>       type Ref is access all Root;
>       Ptr : Ref;
>       Var : aliased Root;
>     begin
>        Ptr := Root ((Var))'Access; -- Error: accepted by GNAT
>     end;

To belabor a point (mostly to get it on the record, so the conclusion blasts
people over the head), the above is illegal because "(Var)" is not an object, a
type conversion of a value is a value conversion (which is not an object,
either), so the prefix here is not an object (or a subprogram), failing the
first criteria for the prefix of 'Access.

The similar example:

     procedure CFoo is
       type Root is tagged null record;
       type CRef is access constant Root;
       Ptr : CRef;
       Var : aliased Root;
     begin
        Ptr := Root ((Var))'Access; -- Error
     end;

is also illegal for the same reason. "(Var)" is neither a constant nor a
variable because it is not a view of an object. An analogy is that a carrot is
neither male nor female because it's not human.

I believe that this is a definitive answer to the original (somewhat
nonsense) question, so this topic is being closed and filed as an AC.

***************************************************************

Questions? Ask the ACAA Technical Agent