Version 1.8 of ai12s/ai12-0028-1.txt

Unformatted version of ai12s/ai12-0028-1.txt version 1.8
Other versions for file ai12s/ai12-0028-1.txt

!standard B.3(1/3)          13-01-02 AI12-0028-1/03
!standard B.3(60.15/3)
!standard B.3(75)
!class binding interpretation 12-06-04
!status Corrigendum 2015 13-01-02
!status WG9 Approved 13-06-14
!status ARG Approved 9-0-2 12-12-08
!status work item 12-06-04
!status received 12-04-13
!priority Medium
!difficulty Medium
!subject Import of variadic C functions
!summary
A new convention, C_Variadic_N is introduced to handle C functions with variable numbers of parameters.
!question
B.3(75) contains the following statement: "10 A C function that takes a variable number of arguments can correspond to several Ada subprograms, taking various specific numbers and types of parameters.".
This is only true for ABIs where the regular C calling convention is the same as the calling convention used for variadic C functions. On systems using the System V ABI AMD64 (used e.g. on 64bit Linux, see http://www.x86-64.org/documentation/abi.pdf) the calling conventions are different, which leads to unpredictable results when importing variadic C functions.
Should this note be rewritten or deleted? (Yes.)
Should Ada have some standard way of interfacing to variadic C functions? (Yes.)
!recommendation
(See summary.)
!wording
In B.3(1/3) replace
... and support for specifying the Convention aspect with convention identifiers C and C_Pass_By_Copy.
with
... and support for specifying the Convention aspect with convention identifiers C, C_Pass_By_Copy, and any of the C_Variadic_n conventions described below.
Append after B.3(60.15/3)
The identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and so on are convention_identifiers. These conventions are said to be C_Variadic. The convention C_Variadic_n is the calling convention for a variadic C function taking n fixed parameters and then a variable number of additional parameters. The C_Variadic_n convention shall only be specified as the convention aspect for a subprogram, or for an access-to-subprogram type, having at least n parameters. A type is compatible with a C_Variadic convention if and only if the type is C-compatible.
AARM To Be Honest: It is implementation defined what the largest n in C_Variadic_n is supported. We don't say this because it complicates the wording and it is true for almost any convention_identifier (only Ada is required to be supported by the language, all others need to be documented in order for programmers to know that they are available).
In B.3(75), replace
"A C function that takes a variable number of arguments"
with
"A variadic C function"
!discussion
This AI is classified as a Binding Interpretation, applying to Ada 2012. Note that adding a language name is something that any implementation can do, so Ada 95 and Ada 2005 compilers also can implement the solution. As such the classification does not matter that much, but it seems better to encourage adoption in Ada 2005 compilers (else it is impossible to write portable interfaces without resorting to writing C code, which should never be required lest programmers wonder why they're using Ada at all).
!corrigendum B.3(1/3)
Replace the paragraph:
The facilities relevant to interfacing with the C language and the corresponding subset of the C++ language are the package Interfaces.C and its children, and support for specifying the Convention aspect with convention_identifiers C and C_Pass_By_Copy.
by:
The facilities relevant to interfacing with the C language and the corresponding subset of the C++ language are the package Interfaces.C and its children, and support for specifying the Convention aspect with convention_identifiers C, C_Pass_By_Copy, and any of the C_Variadic_n conventions described below.
!corrigendum B.3(60.15/3)
Insert after the paragraph:
If a type is C_Pass_By_Copy-compatible, then it is also C-compatible.
the new paragraph:
The identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and so on are convention_identifiers. These conventions are said to be C_Variadic. The convention C_Variadic_n is the calling convention for a variadic C function taking n fixed parameters and then a variable number of additional parameters. The C_Variadic_n convention shall only be specified as the convention aspect for a subprogram, or for an access-to-subprogram type, having at least n parameters. A type is compatible with a C_Variadic convention if and only if the type is C-compatible.
!corrigendum B.3(75)
Replace the paragraph:
A C function that takes a variable number of arguments can correspond to several Ada subprograms, taking various specific numbers and types of parameters.
by:
A variadic C function can correspond to several Ada subprograms, taking various specific numbers and types of parameters.
!ACATS test
An ACATS C-Test could be created to interface to a variadic C function (provided by the test suite) using these identifiers. It's not clear that such a test would be of much value.
!appendix

From: Schoepfin, Markus
Sent: Friday, April 13, 2012  3:41 AM

!topic Problematic wording regarding the import of variadic C functions
!reference Ada 2005 RM B.3(75)
!from Markus Sch”pflin 12-04-13
!keywords varargs
!discussion

B.3(75) contains the following statement: "10  A C function that takes a
variable number of arguments can correspond to several Ada subprograms, taking
various specific numbers and types of parameters. ".

Currently this is only true for ABIs where the regular C calling convention is
the same as the calling convention used for variadic C functions. On systems
using the System V ABI AMD64 (used e.g. on 64bit Linux, see
http://www.x86-64.org/documentation/abi.pdf) the calling conventions are
different, which leads to unpredictable results when importing variadic C
functions.

Possible solution 1: Amend B.3(75) with a warning that this only works on ABIs
where the calling convention for variadic C functions is the same as for regular
C functions.

Possible solution 2: As an implementation advice in B.3, add a new import
convention (e.g. `C_Varargs' or `C_Variadic') which allows to explicitly specify
a different calling convention for variadic C functions. This new calling
convention of course should map to `C' on ABIs where `C_Varargs' and `C' are
identical.

The discussion on comp.lang.ada which stipulated this mail can be found here:
http://groups.google.com/group/comp.lang.ada/browse_thread/thread/235855e3822c83d1#

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

From: Tucker Taft
Sent: Friday, April 13, 2012  2:51 PM

Thanks for bringing this to our attention.

I would agree that a special "convention" might be the best way to handle this.
However, in some cases just using, for example, C_Varargs as the convention,
might not provide enough information, as perhaps it is necessary to know where
the varargs part starts.

However, it sounds like the AMD64 ABI for varargs doesn't need anything more
than a count provided in the %al register.  Here is a quote from the ABI
document:

     ... %al does not need to match exactly the number of
     vector registers, but must be an upper bound on
     the number of vector registers used and is in the
     range 0-8 inclusive.

So setting it to 8 will always work, apparently, through presumably incur a
heavier overhead, so even a simple-minded compiler could get something that
worked, seeing only a calling convention of, say, C_Varargs.

For now, the recommendation in the GNAT user manual seems like the correct
approach.  That is, create a wrapper function in C that takes a fixed number of
arguments, and then have that call the variadic function.

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

From: Schoepfin, Markus
Sent: Monday, April 16, 2012  4:42 AM

> So setting it to 8 will always work, apparently, through presumably incur a
> heavier overhead, so even a simple-minded compiler could get something that
> worked, seeing only a calling convention of, say, C_Varargs.

As long as the compiler doesn't use the vector registers (which I think is the
case for GNAT), just setting %al to 0 also works without resulting in a possible
performance degradation.

But that wasn't really the point of my mail. I think it is problematic that the
Ada standard seems to imply by B.3(75) that variadic C functions can be called
by using various Ada overloads and a simple import pragma. And this should be
fixed some way or another.

Providing an official way to import variadic C function of course would be the
best solution, but even replacing B.3(75) by a statement that explicitly warns
that variadic C functions in general cannot be imported in this way (or perhaps
use a wording similar to the one in the GNAT user guide) would be an improvement
over the current wording, in my opinion.

On the other hand, B.3(75) has been unmodified since the original Ada 83
standard, and perhaps it's more a problem with my reading of the standard than a
problem with the standard itself.

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

From: Tucker Taft
Sent: Monday, April 16, 2012  6:53 AM

Actually, there was no B.3(75) before Ada 95.

In any case, it is only relatively recently that this became a problem.
In old versions of C, function prototypes were completely optional, so even the
C compiler didn't know it was calling a variadic function.  C++ always required
prototypes, but the compilers still attempted to use calling conventions that
didn't take advantage of this for many years (actually, for decades).
Apparently when they designed the ABI for the X86-64 architecture, they finally
decided to require prototypes, even for C.

So I think this was correct as written until relatively recently, and
unfortunately, no one pointed out this problem during the Ada 2012 revision
process.  We can only fix problems we know about!  This is certainly a good
candidate for a fix to the standard, but it will be a while before there is
another "official" Ada standard, so hopefully we can get the word out that
variadic functions may require special handling.

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

From: Schoepfin, Markus
Sent: Monday, April 16, 2012  8:15 AM

Yes, you are right of course, B.3(75) was added for Ada 95. Sorry for the
thinko.

Is there anything else I need to do that this issue stays on the radar of the
ARG?

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

From: Tucker Taft
Sent: Monday, April 16, 2012  7:50 PM

No, Randy Brukardt is very diligent about creating official "AI"s if the issue
is substantive, and this one seems to be so.  Official AIs are tracked very
carefully and pop up next time we have an ARG meeting.

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

From: Florian Weimer
Sent: Tuesday, April 17, 2012  2:26 AM

> In any case, it is only relatively recently that this became a
> problem.  In old versions of C, function prototypes were completely
> optional, so even the C compiler didn't know it was calling a variadic
> function.  C++ always required prototypes, but the compilers still
> attempted to use calling conventions that didn't take advantage of
> this for many years (actually, for decades).

C compilers use different calling conventions for functions with and without
prototypes.  Historically, a prototype-less declaration had to be matched with
an old-style function definition.  The default argument promotions (such as
conversion from float to double) are automatically applied when you call a
function without a prototype. For variadic functions with prototypes, default
argument promotions are applied only for the optional arguments.  This can be
compensated for on the Ada by using promoted types there.  But prototypes had an
ABI impact right from the beginning.

With the x86_64 psABI (and apparently, the Windows amd64 ABI), it is
insufficient to apply the default argument promotions manually.  This is indeed
a difference.  But a knowledge when to apply default argument promotions would
still help Ada compilers to detect incorrect interface declarations.  Knowing
the number of fixed arguments is needed for that, too.

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

From: Tucker Taft
Sent: Monday, July 23, 2012  9:33 AM

> Append after B.3(60.15/3)
>
> For some implementation-dependent integer M greater than or equal to
> 16,

I find this a weird way of specifying the requirement that you support at least
_0 up to _16.  And where in the world did "16" come from? If we have such a
requirement, I would put it in a separate sentence in the implementation
requirements section.  E.g.:

    Implementations shall support at least up to C_Variadic_16.

> ... the identifiers C_Variadic_0, C_Variadic_1, C_Variadic_2, and on
> up to C_Variadic_*M* are convention identifiers.

If we drop "M" then this becomes simply:

   ..., C_Variadic_2, and so on, are convention identifiers.

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

From: Steve Baird
Sent: Monday, July 23, 2012  10:58 AM

> I find this a weird way of specifying the requirement that you support
> at least _0 up to _16.  And where in the world did "16" come from?

I see two choices here:
    1) Don't require any support for this feature. That means
       code that is intended to be portable can't use this feature.
    2) Pick a number. I picked one - 16 just seemed like a reasonable
       value. No more justification than that.

I agree that the wording changes you suggested are improvements.

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

From: Tucker Taft
Sent: Monday, July 23, 2012  11:12 AM

> I see two choices here:
> 1) Don't require any support for this feature. That means code that is
> intended to be portable can't use this feature.
> 2) Pick a number. I picked one - 16 just seemed like a reasonable
> value. No more justification than that.

I don't see any need for a limit here at all.  We don't specify elsewhere a
separate requirement for number of parameters allowed in a subprogram.
Implementations of course have some capacity limits, but pulling this number
"16" out of a hat seems a bit bizarre at this stage.  I would presume this is
just a pattern match thing, where if the convention name matches
"C_Variadic_<integer>" and <integer> is less than or equal to the number of
formal parameters, it is acceptable.

I suppose one could argue for a separate aspect, something like First_Varying =>
N+1, which would perhaps be easier to swallow. This would make it independent of
the "language" specified by "Convention," which might be nice for interfacing to
other languages that have similar capabilities.

> I agree that the wording changes you suggested are improvements.

Glad they were useful.

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

From: Steve Baird
Sent: Monday, July 23, 2012  12:04 PM

> I don't see any need for a limit here at all.  We don't specify
> elsewhere a separate requirement for number of parameters allowed in a
> subprogram.

Ok, you've convinced me.

This felt like a different situation because it affects whether a given
identifier is a recognized convention name, but I suppose this difference isn't
really significant.

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

From: Robert Dewar
Sent: Tuesday, July 24, 2012  6:09 PM

> I would presume
> this is just a pattern match thing, where if the convention name
> matches "C_Variadic_<integer>" and <integer> is less than or equal to
> the number of formal parameters, it is acceptable.

Well that's a totally new mechanism, makes it MUCH harder to implement IMO.

> I suppose one could argue for a separate aspect, something like
> First_Varying => N+1, which would perhaps be easier to swallow.
> This would make it independent of the "language" specified by
> "Convention," which might be nice for interfacing to other languages
> that have similar capabilities.

Better

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

From: Steve Baird
Sent: Tuesday, July 24, 2012  6:35 PM

I think this would imply that the "calling convention" part of determining
whether two profiles are subtype conformant (6.3.1(17/3)) would need to look at
more than the Conventions of the two profiles.

Consider:

     procedure Proc (X, Y, Z : Integer) with Convention => C,
                                             First_Varying => 2);

     type Ref is access procedure (Xx, Yy, Zz : Integer)
        with Convention => C;

     Ptr : Ref := Proc'Access; -- legal?

This example must be illegal (right?) and yet the profiles for Ref and Proc have
the same Convention (i.e., C). So just looking at the convention isn't enough.

We could certainly do this, but it seems to run counter to the idea that an Ada
source-level Convention for a profile corresponds to an implementation level
calling convention.

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

Questions? Ask the ACAA Technical Agent