!standard B.1(38.1/3) 17-04-07 AI12-0219-1/01 !standard B.3(69/2) !standard B.3(70) !class binding interpretation 17-04-07 !status work item 17-04-07 !status received 16-12-22 !priority Low !difficulty Easy !qualifier Omission !subject Clarify C interfacing advice !summary An Ada "in" parameter most closely matches "const T*" in C code. !question Some Ada users have taken the advice in B.3(69-70) to mean that it is OK to modify an Ada "in" parameter in an imported routine. This clearly violates B.1(38.1/3). An Ada "in" parameter corresponds most closely with "const T*" rather than just "T*". Shouldn't the advice mention this? (Yes.) !recommendation (See Summary.) !wording Modify B.1(38.1/3): It is the programmer's responsibility to ensure that the use of interfacing aspects does not violate Ada semantics; otherwise, program execution is erroneous.{ For example, passing an object with mode in to imported code that modifies it causes erroneous execution. Similarly, calling an imported subprogram that does is not pure from a pure package causes erroneous execution.} Modify B.3(69/2): * An Ada parameter of a record type T, of any mode, other than an in parameter of a type of convention C_Pass_By_Copy, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T.{ In the case of an Ada parameter of mode in, use of the const modifier (that is, const t*) is recommended.} Modify B.3(70): * An Ada parameter of an array type with component type T, of any mode, is passed as a t* argument to a C function, where t is the C type corresponding to the Ada type T.{ In the case of an Ada parameter of mode in, use of the const modifier (that is, const t*) is recommended.} [Editor's note: I'd prefer breaking the above into 4 bullets, because these are defining "correspondences", not specifically how imported C code should be written. Most C code isn't under control of the Ada programmer anyway, so its unclear who that recommendation is for anyway. My suggestion: Replace B.3(69/2) with: * An Ada in parameter of a record type T, where T does not have convention C_Pass_By_Copy, is passed as a const t* argument to a C function, where t is the C struct corresponding to the Ada type T. This is equivalent to a t* argument if the C code does not modify the argument; otherwise execution may be erroneous (see B.1). * An Ada parameter of a record type T, other than a parameter of mod in, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T. Replace B.3(70) with: * An Ada in parameter of an array type with component type T is passed as a const t* argument to a C function, where t is the C type corresponding to the Ada type T. This is equivalent to a t* argument if the C code does not modify the argument; otherwise execution may be erroneous (see B.1). * An Ada parameter of an array type with component type T, of any mode other than mode in, is passed as a t* argument to a C function, where t is the C type corresponding to the Ada type T. End Editor's note.] !discussion We add some examples to B.1(38.1/3) to try to illustrate what it is talking about. This is a very important rule to implementers, as it constrains what an interfaced routine can do. Users need to be aware of the consequences. !ASIS No ASIS effect. !ACATS test No ACATS test needed, as the change is purely on the C side of the code. One could imagine changing the C code in some of the tests to follow this advice (to illustrate best practice), but it would make no operational change to the existing tests. (They're not erroneous as it is, and sometimes an IN parameter is the best match to an existing C interface.) !appendix From: Steve Baird Sent: Thursday, December 22, 2016 1:09 PM A user had the incorrect impression that it is ok to bind an imported (on the Ada side) convention-C subprogram with an in-mode parameter of an array type to a routine written in C which modifies that parameter (the actual parameter in all calls was a variable). This is clearly a mistake and is covered by RM B.1 (38.1/3): It is the programmer's responsibility to ensure that the use of interfacing aspects does not violate Ada semantics; otherwise, program execution is erroneous. Nonetheless, it was pointed out in the ensuing discussion that it might be helpful if B.3(69-70) recommended (or at least mentioned) using "const T*" instead of "T*" in the case of an in-mode Ada parameter of an array or record type. What would folks think of appending the following sentence (displayed with appropriate fonts) at the end of both B.3(69/2) and B.3(70): In the case of an Ada parameter of mode in, use of the const modifier (i.e., const t*) is recommended. ? Is there a better way to express this that doesn't involve two copies of the same sentence? We could also do something similar in the case of an access-to-constant parameter type, but that's not the topic of this message. P.S. I hope I got the "const T*" vs. "T* const" distinction right in the above. **************************************************************** From: Tucker Taft Sent: Thursday, December 22, 2016 3:05 PM > ... What would folks think of appending the following sentence > (displayed with appropriate fonts) at the end of both > B.3(69/2) and B.3(70): > > In the case of an Ada parameter of mode in, > use of the const modifier (i.e., const t*) is recommended. > > ? Is there a better way to express this that doesn't involve two > copies of the same sentence? I would just start the sentence in the second occurrence: "As above, in the case ..." since it is in the immediate following paragraph. I would also suggest we add somewhere a note saying that if an imported routine modifies a parameter declared with mode IN, it can lead to erroneous execution, with a reference to the more general rule. > We could also do something similar in the case of an access-to-constant > parameter type, but that's not the topic of this message. > > P.S. I hope I got the "const T*" vs. "T* const" distinction > right in the above. Yes, you got it right. In this case, "const t* const" would also make sense, but the more important "const" is the first one. **************************************************************** From: Steve Baird Sent: Thursday, December 22, 2016 3:34 PM > I would just start the sentence in the second occurrence: > > "As above, in the case ..." > > since it is in the immediate following paragraph. > Sounds good. > I would also suggest we add somewhere a note saying that if an > imported routine modifies a parameter declared with mode IN, it can > lead to erroneous execution, with a reference to the more general rule. Also sounds good. Perhaps "somewhere" could be at the point of the more general rule (i.e. the Erroneous Execution section of B.1). We could (in redundant text) give a couple of examples to illustrate the consequences of this rule. **************************************************************** From: Tucker Taft Sent: Thursday, December 22, 2016 3:57 PM >> ... I would also suggest we add somewhere a note saying that if an >> imported routine modifies a parameter declared with mode IN, it can >> lead to erroneous execution, with a reference to the more general rule. > > Also sounds good. > > Perhaps "somewhere" could be at the point of the more general rule > (i.e. the Erroneous Execution section of B.1). > We could (in redundant text) give a couple of examples to illustrate > the consequences of this rule. Yes, that might be helpful in making this one-sentence paragraph a bit clearer. **************************************************************** From: Randy Brukardt Sent: Friday, April 7, 2016 7:38 PM ... >What would folks think of appending the following sentence (displayed >with appropriate fonts) at the end of both B.3(69/2) and B.3(70): > > In the case of an Ada parameter of mode in, > use of the const modifier (i.e., const t*) is recommended. I'm writing this thread up, and it strikes me that the above wording is not a good fit for the style of the rest of the implementation advice. In particular, this section is written to specify correspondences between Ada and C, without making any inference as to which side comes first. The above is written as if the C code is being written second (which is the unusual case in my experience). Usually the Ada programmer is matching some existing C code. With that in mind, I'd probably write the above reversed, something like: Care should be taken in selecting the mode of the parameter in this case; in particular, mode *in* should be used only if the C code uses the const modifier (that is, const t*) or if the C code does not modify the parameter; otherwise execution may be erroneous (see B.1). [Aside: We never use "e.g." or "i.e." in Standard wording. Please use "for example" or "that is"... End lecture.] This is better because it matches the way this section is typically used, and it manages to work in a cross-reference to a rule that most Ada programmers never have seen (and is critical). This is lousy because it's a run-on sentence, and because it still isn't in the style of the actual bullets. And both of these solutions seem to be only worrying about imported C routines; what about Ada routines exported to C?? (To be far, much of the existing wording already has this problem. This is supposed to be about "correspondences", direction of passing shouldn't enter into it.) The best solution would be to properly split the bullet to always use the modifier for the in parameter case (and add a sentence suggesting that t* can be used instead with care): * An Ada in parameter of a record type T, where T does not have convention C_Pass_By_Copy, is passed as a const t* argument to a C function, where t is the C struct corresponding to the Ada type T. This is equivalent to a t* argument if the C code does not modify the argument; otherwise execution may be erroneous (see B.1). * An Ada parameter of a record type T, other than a parameter of mod in, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T. And likewise for the array case: * An Ada in parameter of an array type with component type T is passed as a const t* argument to a C function, where t is the C type corresponding to the Ada type T. This is equivalent to a t* argument if the C code does not modify the argument; otherwise execution may be erroneous (see B.1). * An Ada parameter of an array type with component type T, of any mode other than mode in, is passed as a t* argument to a C function, where t is the C type corresponding to the Ada type T. In all of these, I don't like the phrase "...is passed as...to a C function", because for export it is going the other way but we're using the same correspondences (at least I hope so!), and for access-to-subprogram convention it could be going either way. But that's probably not worth fixing 23 years in (this section dates back 1994 drafts of Ada 9x). Should we go all the way here (as in my last suggestion), or is there some better wording than I've been able to come up with?? ... > > Perhaps "somewhere" could be at the point of the more general rule > > (i.e. the Erroneous Execution section of B.1). > > We could (in redundant text) give a couple of examples to illustrate > > the consequences of this rule. > > Yes, that might be helpful in making this one-sentence paragraph a bit > clearer. Unfortunately, no one suggested what other example(s) might be relevant. I recall that this rule has come up repeatedly in discussion of other AIs, but I'm not going to go looking for those. The Duff technique of defeating Pure restrictions came to mind after much griping, so I used that, but perhaps someone has a better example. It is the programmer's responsibility to ensure that the use of interfacing aspects does not violate Ada semantics; otherwise, program execution is erroneous.{ For example, passing an object with mode in to imported code that modifies it causes erroneous execution. Similarly, calling an imported subprogram that does is not pure from a pure package causes erroneous execution.} Better examples/wording welcome. ****************************************************************