Discussion:
Renaming primitives.
(too old to reply)
Blady
2024-01-10 10:37:39 UTC
Permalink
Hello,

Is a procedure renaming equivalent to a simple call?
For instance:
procedure Log (Msg : String) is
begin
Ada.Text_IO.Put_Line (Msg);
end Log;
procedure My_Log_1 (Msg : String) is
begin
Log (Msg);
end My_Log_1;
procedure My_Log_2 (Msg : String) renames Log;

Almost, My_Log_1 is equivalent to My_Log_2, isn't it?

Is it the same with primitives?

For instance:
package Loggings is
type Logging is tagged record
Output : Ada.Text_IO.File_Access;
end record;
procedure Log (Handler : Logging; Msg : String);
end Loggings;
My_Handler : aliased Loggings.Logging := (Output =>
Ada.Text_IO.Current_Output);
My_Generic_Handler : access Loggings.Logging'Class := My_Handler'Access;
procedure My_Log_3 (Msg : String) renames My_Generic_Handler.Log;

To my surprise (well I hoped) My_Log_3 is compiled by GNAT.
Is it conformed to the Ada standard?

What is happening if My_Generic_Handler change?

For instance:
type Full_Logging is new Logging with null record;
procedure Log (Handler : Full_Logging; Msg : String);
...
My_Full_Handler : aliased Loggings.Full_Logging := (Output =>
Ada.Text_IO.Current_Output);
...
My_Generic_Handler := My_Full_Handler'Access;

Well, logically (?), My_Log_3 follows the change and outputs with
Full_Logging.

Unfortunately, GNAT claims renaming with several parameters.
I add:
procedure Log (Handler : Logging; Msg : String; Err : Natural);
...
procedure My_Log_4 (Msg : String; Err : Natural) renames
My_Generic_Handler.Log;

I got:
test_20240110_renproc.adb:47:14: error: too many arguments in call to "log"

Is it a correct or a GNAT issue?

Full source code on demand.

Thanks.
Happy New Year to all!
Pascal.
Jeffrey R.Carter
2024-01-10 11:38:25 UTC
Permalink
   package Loggings is
      type Logging is tagged record
         Output : Ada.Text_IO.File_Access;
      end record;
      procedure Log (Handler : Logging; Msg : String);
   end Loggings;
   My_Handler : aliased Loggings.Logging := (Output => Ada.Text_IO.Current_Output);
   My_Generic_Handler : access Loggings.Logging'Class := My_Handler'Access;
   procedure My_Log_3 (Msg : String) renames My_Generic_Handler.Log;
My_Generic_Handler.Log is shorthand for My_Generic_Handler.all.Log. According to
AARM 8.5.4(5.e/5) (http://www.ada-auth.org/standards/22aarm/html/AA-8-5-4.html),
the renaming also renames the prefixing object and passes it to calls to the
renamed subprogram: "The prefix in such a case is essentially renamed and passed
to any calls of the renamed subprogram."

So if I understand this correctly the renaming is roughly equivalent to

<anonymous> : Loggings.Logging renames My_Generic_Handler.all;
procedure My_Log_3 (Handler : Loggings.Logging := <anonymous>; Msg : String)
renames Loggings.Logging.Log;

except that My_Log_3 is called with only a String parameter even when using
positional notation.

Another way to look at it is that

My_Log_3 ("msg");

is implemented as

<anonymous>.Log (Msg => "msg");
What is happening if My_Generic_Handler change?
      type Full_Logging is new Logging with null record;
      procedure Log (Handler : Full_Logging; Msg : String);
...
   My_Full_Handler : aliased Loggings.Full_Logging := (Output =>
Ada.Text_IO.Current_Output);
...
   My_Generic_Handler := My_Full_Handler'Access;
Well, logically (?), My_Log_3 follows the change and outputs with Full_Logging.
No, My_Log_3 continues to call Log with My_Handler as the object, IIUC.
Unfortunately, GNAT claims renaming with several parameters.
      procedure Log (Handler : Logging; Msg : String; Err : Natural);
...
   procedure My_Log_4 (Msg : String; Err : Natural) renames
My_Generic_Handler.Log;
test_20240110_renproc.adb:47:14: error: too many arguments in call to "log"
Is it a correct or a GNAT issue?
I'm not sure what is going on here. This looks correct to me.
--
Jeff Carter
"Fundamental improvements in performance
are most often made by algorithm changes,
not by tuning."
Elements of Programming Style
201
Blady
2024-01-11 10:06:45 UTC
Permalink
Post by Jeffrey R.Carter
    procedure My_Log_3 (Msg : String) renames My_Generic_Handler.Log;
My_Generic_Handler.Log is shorthand for My_Generic_Handler.all.Log.
According to AARM 8.5.4(5.e/5)
(http://www.ada-auth.org/standards/22aarm/html/AA-8-5-4.html), the
renaming also renames the prefixing object and passes it to calls to the
renamed subprogram: "The prefix in such a case is essentially renamed
and passed to any calls of the renamed subprogram."
So if I understand this correctly the renaming is roughly equivalent to
<anonymous> : Loggings.Logging renames My_Generic_Handler.all;
String) renames Loggings.Logging.Log;
except that My_Log_3 is called with only a String parameter even when
using positional notation.
Another way to look at it is that
   My_Log_3 ("msg");
is implemented as
   <anonymous>.Log (Msg => "msg");
Thanks for your useful explanation.
Post by Jeffrey R.Carter
What is happening if My_Generic_Handler change?
       type Full_Logging is new Logging with null record;
       procedure Log (Handler : Full_Logging; Msg : String);
...
    My_Full_Handler : aliased Loggings.Full_Logging := (Output =>
Ada.Text_IO.Current_Output);
...
    My_Generic_Handler := My_Full_Handler'Access;
Well, logically (?), My_Log_3 follows the change and outputs with Full_Logging.
No, My_Log_3 continues to call Log with My_Handler as the object, IIUC.
Yet, the code:
My_Log_3 ("Hei");
My_Generic_Handler := My_Full_Handler'Access;
My_Log_3 ("Hei");
gives:
Hei
Full Hei

I thought that was correct, wasn't it?

Pascal.
Jeffrey R.Carter
2024-01-11 11:06:27 UTC
Permalink
   My_Log_3 ("Hei");
   My_Generic_Handler := My_Full_Handler'Access;
   My_Log_3 ("Hei");
Hei
Full Hei
I thought that was correct, wasn't it?
Not according to my understanding, and it seems according to Brukardt's
better-informed understanding.
--
Jeff Carter
"[A] brilliant military career that after thirty
years catapulted him to the rank of corporal."
Take the Money and Run
138
Dmitry A. Kazakov
2024-01-10 12:13:59 UTC
Permalink
Post by Blady
Is a procedure renaming equivalent to a simple call?
   procedure Log (Msg : String) is
   begin
      Ada.Text_IO.Put_Line (Msg);
   end Log;
   procedure My_Log_1 (Msg : String) is
   begin
      Log (Msg);
   end My_Log_1;
   procedure My_Log_2 (Msg : String) renames Log;
Almost, My_Log_1 is equivalent to My_Log_2, isn't it?
Is it the same with primitives?
   package Loggings is
      type Logging is tagged record
         Output : Ada.Text_IO.File_Access;
      end record;
      procedure Log (Handler : Logging; Msg : String);
   end Loggings;
   My_Handler : aliased Loggings.Logging := (Output =>
Ada.Text_IO.Current_Output);
   My_Generic_Handler : access Loggings.Logging'Class :=
My_Handler'Access;
   procedure My_Log_3 (Msg : String) renames My_Generic_Handler.Log;
To my surprise (well I hoped) My_Log_3 is compiled by GNAT.
Is it conformed to the Ada standard?
This looks like a bug to me. ARM 8.5.4 (2/3) states that the right part
of "renames" must be a name of a callable entity. My_Generic_Handler.Log
is a procedure call which is not a callable entity name.

Renaming a callable result of a function call might be legal [and quite
funny].

Disclaimer. I am not a language lawyer.

P.S. Specialization of subprograms would be a nice feature, though.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
Randy Brukardt
2024-01-11 02:09:57 UTC
Permalink
...
Post by Blady
To my surprise (well I hoped) My_Log_3 is compiled by GNAT.
Is it conformed to the Ada standard?
This looks like a bug to me. ARM 8.5.4 (2/3) states that the right part of
"renames" must be a name of a callable entity. My_Generic_Handler.Log is a
procedure call which is not a callable entity name.
A "prefixed view" is indeed a view of a callable entity. For instance, you
can pass those as generic formal subprograms or rename them.
Renaming a callable result of a function call might be legal [and quite
funny].
I believe that is legal as well.
Disclaimer. I am not a language lawyer.
I'll refrain from a pithy remark. ;-)
P.S. Specialization of subprograms would be a nice feature, though.
And Ada has had a limited form of it since Ada 2005. Limited because you can
only eliminate the first parameter. (Of course, you can also eliminate the
first two parameters, and so on. But you can't eliminate the third
parameter, although you can default it and effectively eliminate from calls.
Note that a renaming can change the defaults used for a subprogram call.)

Randy.
Randy Brukardt
2024-01-11 02:18:39 UTC
Permalink
Post by Blady
Is a procedure renaming equivalent to a simple call?
General point: "Equivalent" is never true in programming language design. If
construct A is equivalent in every way to construct B, then they have to be
the same construct.

For instance, in your example below, the two subprogram declarations have
different identifiers which have different visibility (two declarations
cannot be at exactly the same place), so there is at least one way that
they're not equivalent.

So, if you want to talk about "equivalence", you need to qualify that by
what properties you are interested in.

...
Post by Blady
What is happening if My_Generic_Handler change?
Nothing. The names in a renaming are evaluated when the renaming is
declared. If there are parts that could change, the compiler has to save the
values at the point of the renaming. That's true for all forms of renaming
(some names, like package names, can't change meaning so the example isn't
possible, of course).

...
Post by Blady
test_20240110_renproc.adb:47:14: error: too many arguments in call to "log"
Is it a correct or a GNAT issue?
Looks like a bug to me. Not too surprising, though, this sort of thing is a
special case in a compiler and often added to pass an ACATS test or user bug
report. Such fixes can often not be fully general.

Randy.
Blady
2024-01-11 10:09:38 UTC
Permalink
Post by Randy Brukardt
Post by Blady
Is a procedure renaming equivalent to a simple call?
General point: "Equivalent" is never true in programming language design. If
construct A is equivalent in every way to construct B, then they have to be
the same construct.
For instance, in your example below, the two subprogram declarations have
different identifiers which have different visibility (two declarations
cannot be at exactly the same place), so there is at least one way that
they're not equivalent.
So, if you want to talk about "equivalence", you need to qualify that by
what properties you are interested in.
Well, I hadn't find a better word :-(
Post by Randy Brukardt
...
Post by Blady
What is happening if My_Generic_Handler change?
Nothing. The names in a renaming are evaluated when the renaming is
declared. If there are parts that could change, the compiler has to save the
values at the point of the renaming. That's true for all forms of renaming
(some names, like package names, can't change meaning so the example isn't
possible, of course).
Yet, the code:
My_Log_3 ("Hei");
My_Generic_Handler := My_Full_Handler'Access;
My_Log_3 ("Hei");
gives:
Hei
Full Hei

I thought that was correct, wasn't it?
GNAT issue ?
Post by Randy Brukardt
...
Post by Blady
test_20240110_renproc.adb:47:14: error: too many arguments in call to "log"
Is it a correct or a GNAT issue?
Looks like a bug to me. Not too surprising, though, this sort of thing is a
special case in a compiler and often added to pass an ACATS test or user bug
report. Such fixes can often not be fully general.
Thanks, I'll report it.

Pascal.
Randy Brukardt
2024-01-12 03:59:05 UTC
Permalink
Post by Blady
Post by Randy Brukardt
Post by Blady
Is a procedure renaming equivalent to a simple call?
General point: "Equivalent" is never true in programming language design. If
construct A is equivalent in every way to construct B, then they have to be
the same construct.
For instance, in your example below, the two subprogram declarations have
different identifiers which have different visibility (two declarations
cannot be at exactly the same place), so there is at least one way that
they're not equivalent.
So, if you want to talk about "equivalence", you need to qualify that by
what properties you are interested in.
Well, I hadn't find a better word :-(
You have to say something like "equivalent in run-time behavior";
"equivalent" by itself is always False.
Post by Blady
Post by Randy Brukardt
...
Post by Blady
What is happening if My_Generic_Handler change?
Nothing. The names in a renaming are evaluated when the renaming is
declared. If there are parts that could change, the compiler has to save the
values at the point of the renaming. That's true for all forms of renaming
(some names, like package names, can't change meaning so the example isn't
possible, of course).
My_Log_3 ("Hei");
My_Generic_Handler := My_Full_Handler'Access;
My_Log_3 ("Hei");
Hei
Full Hei
I thought that was correct, wasn't it?
Shouldn't be correct. The "name" is evaluated during a renaming, and the
renamed entity does not change afterwards. So whatever subprogram My_Log_3
designates for the first call should be the same for the second call.
Post by Blady
GNAT issue ?
Appears to be (of course, without seeing the full code I can't say
definitively; people here often post snippets that aren't quite the same as
the actual code they tried to run. I wouldn't expect that with you, but we
all make mistakes. :-).

Randy.
J-P. Rosen
2024-01-12 09:47:41 UTC
Permalink
Post by Randy Brukardt
Shouldn't be correct. The "name" is evaluated during a renaming, and the
renamed entity does not change afterwards. So whatever subprogram My_Log_3
designates for the first call should be the same for the second call.
Just to make things perfectly clear (I hope):
Renames shouldn't be seen as a kind of macro (i.e. textual substitution
of the LHS by the RHS). It is rather like evaluating the address of the
RHS thing, and storing it into the LHS. A kind of precalculated
address/pointer/reference.
--
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
https://www.adalog.fr https://www.adacontrol.fr
Lawrence D'Oliveiro
2024-01-12 23:36:07 UTC
Permalink
Post by J-P. Rosen
Renames shouldn't be seen as a kind of macro (i.e. textual substitution
of the LHS by the RHS).
There is the wrong way of implementing macros--by textual substitution--
and there is the right way, as done in homoiconic languages such as Lisp.

For example, the latter can give you lexical binding. That is, the rename
still refers to the original definition, even in an inner scope where that
is masked by another entity with the same name. I assume that is how the
Ada mechanism works.

Blady
2024-01-12 10:29:40 UTC
Permalink
Post by Randy Brukardt
Post by Blady
Post by Randy Brukardt
Post by Blady
Is a procedure renaming equivalent to a simple call?
General point: "Equivalent" is never true in programming language design. If
construct A is equivalent in every way to construct B, then they have to be
the same construct.
For instance, in your example below, the two subprogram declarations have
different identifiers which have different visibility (two declarations
cannot be at exactly the same place), so there is at least one way that
they're not equivalent.
So, if you want to talk about "equivalence", you need to qualify that by
what properties you are interested in.
Well, I hadn't find a better word :-(
You have to say something like "equivalent in run-time behavior";
"equivalent" by itself is always False.
Thanks, I like it.
Post by Randy Brukardt
Post by Blady
Post by Randy Brukardt
...
Post by Blady
What is happening if My_Generic_Handler change?
Nothing. The names in a renaming are evaluated when the renaming is
declared. If there are parts that could change, the compiler has to save the
values at the point of the renaming. That's true for all forms of renaming
(some names, like package names, can't change meaning so the example isn't
possible, of course).
My_Log_3 ("Hei");
My_Generic_Handler := My_Full_Handler'Access;
My_Log_3 ("Hei");
Hei
Full Hei
I thought that was correct, wasn't it?
Shouldn't be correct. The "name" is evaluated during a renaming, and the
renamed entity does not change afterwards. So whatever subprogram My_Log_3
designates for the first call should be the same for the second call.
Post by Blady
GNAT issue ?
Appears to be (of course, without seeing the full code I can't say
definitively; people here often post snippets that aren't quite the same as
the actual code they tried to run. I wouldn't expect that with you, but we
all make mistakes. :-).
I quote you :-) and attached the full source code.
You might follow or comment on the created issue:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113350
Blady
2024-01-12 10:42:19 UTC
Permalink
Post by Blady
Post by Randy Brukardt
Post by Blady
test_20240110_renproc.adb:47:14: error: too many arguments in call to "log"
Is it a correct or a GNAT issue?
Looks like a bug to me. Not too surprising, though, this sort of thing is a
special case in a compiler and often added to pass an ACATS test or user bug
report. Such fixes can often not be fully general.
Thanks, I'll report it.
Submitted issue with attached source code:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113351
Loading...