Version 1.1 of acs/ac-00150.txt

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

!standard B.1(22)          07-10-24 AC95-00150/01
!class confirmation 07-10-24
!status received no action 07-10-24
!status received 07-09-26
!subject Import pragma in package body and overloading
!summary
!appendix

!topic Import pragma in package body, and overloading
!reference B.1(22)
!from Adam Beneschan 07-10-11
!discussion

We've been discussion a situation where it's possible that the rules
in B.1(22) don't produce the intended result:

   package Pak1 is
       procedure Proc (X : Integer; Y : String);
   end Pak1;

   package body Pak1 is
       procedure Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr);
       pragma Import (C, Proc, "proc");

       ...
   end Pak1;

where, say, the implementation of Proc in the spec will call the
imported version.  The language rules make this illegal, however.
B.1(22) says:

    A pragma Import shall be the completion of a declaration.
    Notwithstanding any rule to the contrary, a pragma Import may
    serve as the completion of any kind of (explicit) declaration if
    supported by an implementation for that kind of declaration. If a
    completion is a pragma Import, then it shall appear in the same
    declarative_part, package_specification, task_definition or
    protected_definition as the declaration. For a library unit, it
    shall appear in the same compilation, before any subsequent
    compilation_units other than pragmas. If the local_name denotes
    more than one entity, then the pragma Import is the completion of
    all of them.

The last sentence of this paragraph means that the Import pragma
denotes *both* declarations of Proc, based on the rules in 13.1(5) and
8.1.  13.1(1) and 13.1(5) say that, for representation pragmas
(including Import), among other things, a local_name resolves to
denote the declaration or declarations that occur immediately within
the same declarative_region---and the declarative region here is
comprised of both the package specification and the body.  This means
that "Proc" denotes both declarations.  However, B.1(22) doesn't use
the "declarative_region" terminology, saying instead that the pragma
must occur in the same declarative_part or package_specification,
etc.

Is there any reason why the last sentence couldn't be changed to
something like:

"If the local_name denotes more than one entity, then the pragma
Import is the completion of all such entities whose declarations
appear in the same declarative_part, package_specification,
task_definition or protected_definition as the pragma; the pragma
shall be the completion of at least one entity."

or something like that?

This actually occurs in the GNAT runtime, or at least the latest
version we have.  The specification of GNAT.Expect contains two
declarations of a procedure Close; the body has a third declaration:

   procedure Close (Fd : File_Descriptor);
   pragma Import (C, Close);

Not that there's any rule that says that the GNAT runtime must be
written in Ada... :)  but I think it's purporting to be legal Ada, but
apparently it isn't, the way the rules are written.

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

From: Randy Brukardt
Sent: Thursday, October 11, 2007  5:56 PM

...
> Is there any reason why the last sentence couldn't be changed to
> something like:
>
> "If the local_name denotes more than one entity, then the pragma
> Import is the completion of all such entities whose declarations
> appear in the same declarative_part, package_specification,
> task_definition or protected_definition as the pragma; the pragma
> shall be the completion of at least one entity."
>
> or something like that?

I can't think of any reason, other than that any overloading of Import is
most likely a bug. I'd much prefer it just said:

"If the local_name denotes more than one entity, then the program is
illegal."

(which in fact is the rule that Janus/Ada implements, because otherwise we
get linking conflicts).

But that won't happen because of compatibility concerns.

(Why do I think it is most likely a bug? Consider something like:

    package Pak2 is
        procedure Proc (X : Integer; Y : String);
        procedure Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr);
        pragma Import (C, Proc, "proc");
    end Pak2;

Did the user really intent to bind both of these to the same external
procedure? Maybe, but its not likely; it's a lot more likely that the case
described in your example is intended. I would have preferred that the
pragma bind only to the previous subprogram declaration, but that too is
incompatible.)

Anyway, this is not particularly important, because it is easy to get the
right effect with renames without ever depending on overloaded Imports. For
instance, your example could be something like:

    package Pak1 is
        procedure Proc (X : Integer; Y : String);
    end Pak1;

    package body Pak1 is
        procedure C_Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr);
        pragma Import (C, C_Proc, "proc");

        procedure Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr)
            renames C_Proc;

        ...
    end Pak1;

...in which case there is no confusion as to where the Import applies. (This
is how Claw and all of the RRS bindings are structured.) As such, I think
even compilers that support a single import completing multiple declarations
should warn about it.

Net-net, I think changing this is just putting lipstick on a pig. I'd
support getting rid of the pig, but we've never had the will and/or reason
to do that in the past, and I doubt anything has changed on that front.

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

From: Tucker Taft
Sent: Thursday, October 11, 2007  6:17 PM

The one place where I have seen it handy to
allow an Import to complete multiple specifications
is when the "foreign" language supports variadic
parameter lists (e.g. C for functions like "printf").
You can then declare various overloadings of
a single function, all of which ultimately result
in a call on the same external function.

In any case, I don't support the change that Adam
is proposing to B.1, and I'm not sure it would "work"
anyway. It seems pretty clear from the general
rules for local_name (13.1(5,6)) that a pragma that applies
to an overloaded name is illegal if the pragma is
in the body and one or more of the overloadings
are declared in the spec.  Hence, even before we
start worrying about the rules in B.1 for the semantics
of pragma Import, the example program is already illegal
by 13.1(6), because it uses a local_name in a pragma
that denotes an entity that is outside the current
declarative part/package spec.

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

From: Adam Beneschan
Sent: Thursday, October 11, 2007  6:20 PM

> Did the user really intent to bind both of these to the same external
> procedure? Maybe, but its not likely; it's a lot more likely that the case
> described in your example is intended. I would have preferred that the
> pragma bind only to the previous subprogram declaration, but that too is
> incompatible.)

We've found multiple import to be convenient in a few cases.  Unix
ioctl(), for example, takes a variety of different parameter types for
the third parameter, based on the request in the second parameter, and
it can be convenient to declare various ioctl functions with different
record types (or access types) as the third parameter and import all
of them.  Of course, we declare them all together, so it's obvious
what's going on.  If the two declarations are relatively far apart,
with lots of other stuff in between, then it's indeed a lot more
likely that the Import wasn't intended to apply to both.  Anyway, the
workaround wouldn't be onerous if multiple import were disallowed.

I'm not sure how well this applies to my question, though, because in
my example, if the rule were written as I suggested, the Import would
apply only to one procedure, so the multiple import question wouldn't
come up.  It seems to be only a quirk of the language rules that the
Import applies to two procedures (three in the GNAT.Except case),
including one (or two) procedures that Import can't legally apply to.

But I agree that the workaround is easy enough that it's probably not
worth it to fix compilers that are currently obeying the rules as
written.  In fact, in this workaround:

>     package Pak1 is
>         procedure Proc (X : Integer; Y : String);
>     end Pak1;
>
>     package body Pak1 is
>         procedure C_Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr);
>         pragma Import (C, C_Proc, "proc");
>
>         procedure Proc (X : Interfaces.C.Int; Y : Interfaces.C.Chars_Ptr)
>             renames C_Proc;

it might be best to forget the rename and just write C_Proc everywhere
that you intend to call the C version of Proc---it might just make the
code more understandable!

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

Questions? Ask the ACAA Technical Agent