!standard 10.2.1 03-12-14 AI95-00366/02 !class amendment 03-12-14 !status work item 03-12-14 !status received 03-12-14 !priority Medium !difficulty Easy !subject More liberal rule for Pure units !summary Pure units allow access-to-subprogram types and access-to-object types, for which no storage pool is created. !problem In pure units access-to-subprogram types and access-to-object types, for which no storage pool is created, are presently prohibited. There is no technical reason for the restriction and many packages that should be pure can not be declared pure. Similarly, generic formal access types should be permitted (as the storage pool comes from outside of the unit). !proposal (see wording) !wording Replace 10.2.1(16) A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access type, except within a subprogram, generic subprogram, task unit, or protected unit. by: A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access-to-object type, for which the Storage_Size has not been specified to be 0, excepting declarations within a subprogram, generic subprogram, task unit, or protected unit. !discussion Pure implies that a unit has no state. Access-to-object types are associated with storage pools, which constitute state. However, access-to-subprogram types have no such implications. Hence they should not be excluded. A corresponding correction of 10.2.1(16) would read: A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access-to-object type, except within a subprogram, generic subprogram, task unit, or protected unit. Beyond access-to-subprogram types, some packages have a need for an access-to-object type, without a need for a storage pool for the type, however. Setting the Storage_size of the type to zero implies that no storage pool (and hence no state) needs to be created. The proposed !wording reflects these observations and removes the restriction on the presence of such access types. !example --!corrigendum !ACATS test An ACATS test checking that such types are allowed in Pure units should be constructed. !appendix !topic Generic formal access types in Pure packages !reference 10.2.1(16) !from Adam Beneschan 11-27-02 !discussion Is it the intent of 10.2.1(16) that a Pure package cannot declare a nested generic with a generic formal access type? Specifically, I believe the RM makes these two package declarations illegal: -- (1) package pak1 is pragma Pure; generic type T1 is private; type T2 is access all T1; package genpack is procedure proc (x : in T2); end genpack; end pak1; -- (2) package pak1 is pragma Pure; generic type T1 is private; type T2 is access all T1; procedure proc (x : in T2); end pak1; 10.2.1(16) says, "A pure library_item is a preelaborable library_item that does not contain the declaration of any variable or named access type, except within a subprogram, generic subprogram, task unit, or protected unit". Both of these packages contain declarations of named access types named T2. I can't find anything in the RM that would make declarations of generic formal types *not* fall under this rule; the declaration of a generic formal type is clearly a "declaration", and from everything I can tell, a generic formal type is a "type". (In case (2), although the type declaration is within a generic_subprogram_declaration, I don't think this is the same thing as being "within a ... generic subprogram".) However, my impression (which may be off base) is that the rule is intended to prevent Pure packages from declaring new access types, and a generic formal type doesn't fit this description. So should 10.2.1(16) be amended to exclude generic formal access types? By the way, GNAT accepts both of the above examples. **************************************************************** From: Christoph Grein Sent: Wednesday, November 27, 2002 11:48 PM Also Rational Apex 3.2.0b accepts them. **************************************************************** From: Pascal Leroy Sent: Thursday, November 28, 2002 3:12 AM I think that the two examples should be legal, and that the wording in 10.2.1(16) is lousy. It would probably be good to fix it, although I would see this as a low priority issue. **************************************************************** From: Randy Brukardt Sent: Tuesday, February 3, 2004 5:16 PM In the context of AI-362, I was thinking about how to get any sort of logging/debugging into a Preelaborated unit. The problem is that once a preelaborated unit has that pragma applied, you can no longer with any I/O. Without I/O, you can't have any logging for faults in a fielded system (if the system dies, it probably won't get to dump an in-memory log, making it useless for field debugging - you probably can't run a debugger on the target system - think about NASA's rovers...) I realized that something that my spam filter's code does provides a solution (assuming some up-front work): package Something is pragma Preelaborate (Something); type Logger_Access is access procedure (Text : in String); procedure Do_Something (....; Logger : Logger_Access := null); end Something; Then, a call to Do_Something can include a logger, even if the logger couldn't be withed by the Preelaborated unit: Do_Something (...., Logger => Ada.Text_IO.Put_Line'Access); If you do this consistently, with each preelaborated routine passing the logger on through any calls, you can pretty much log anything you need in a preelaborated unit. At the cost of an extra parameter to everything, of course. (You could use a global variable as well, but that's UGLY!) That's OK for preelaborated units, since that is mainly about library-level elaboration: which necessarily has been completed before any call can be made. However, AI-366 proposes to allow access-to-subprogram and access-to-object-with-empty-pool in pure units. This means that it would be trivial to pass an impure function to a Pure function, and thus make the results vary even if the parameters are the same (as per the permission of 10.2.1(18)): package Impure is pragma Pure (Impure); type Random_Access is function return Float; function Use_Random (Random : in Random_Access) return Natural; end Impure; A := Use_Random (Random'Access); B := Use_Random (Random'Access); Clearly, the calls to Use_Random have the same values for a by-copy parameter, but A and B are unlikely to have the same values. Yet the compiler can omit the second call. Indeed, this permission allows me to write a "Pure" random number generator: package Pure_Random is pragma Pure (Pure_Random); type Generator is private; type Generator_Access is access all Generator; for Generator_Access'Storage_Size use 0; function Random (Gen : in Generator_Access) return Float; end Pure_Random; Gen : aliased Pure_Random.Generator; A := Pure_Random.Random (Gen'Access); B := Pure_Random.Random (Gen'Access); Clearly, the permission of 10.2.1(18) applies to these calls, and yet it clearly must not! So it appears that AI-366 is incomplete, in that it should address this issue somehow. We'd really rather not allow such functions as Pure -- but that's the current state; we could vote this AI No Action, but that's hardly responsive to the problem. One possibility is to do as the GNAT Pure_Function pragma, which is to say that pragma Pure is an assertion that any functions always return the (logically) same results and have no significant side-effects (without checking those assertions). In which case, we can leave the permission alone. But others have suggested that the permission of 10.2.1(18) is flawed anywhy (that discussion was filed in AI-290 about pragma Pure_Function). Perhaps the permission can be 'patched up' by including the values of access-to-object parameters, and excluding the permission if there are any access-to-subprogram parameters. (That's probably a bit too strong, but I'd rather err on the side of correctness rather than optimization...) **************************************************************** From: Tucker Taft Sent: Tuesday, February 3, 2004 8:08 PM If we allow "pure" access types, then clearly the values of the variables accessible via an access value must be included in 10.2.1(18). The variables accessible via an access-to-subprogram value include any variables visible to the designated subprogram. The variables accessible via an access-to-object value include the designated object, and any variables that can be reached from there via following access values. **************************************************************** From: Pascal Leroy Sent: Wednesday, February 4, 2004 7:12 AM I don't see that there is anything new here. You can do the same currently if Random has an access parameter, because 10.2.1(16) only talks about named access types. I believe that the rules that 10.2.1(18) specifies for by-reference types should also apply to access types,. Actually I would be in favor of deleting 10.2.1(18) altogether, as it's one of these rules (like 11.6) which effectively says "forget the rest of this book, we don't really mean it". But I guess that's a different discussion. **************************************************************** From: Tucker Taft Sent: Wednesday, February 4, 2004 7:16 AM Yes, that's a very different discussion ;-). **************************************************************** From: Robert Dewar Sent: Wednesday, February 4, 2004 7:36 AM Remember that you can always do anything by doing raw import rather than WITH. It is really nothing to get worried about that this sort of thing can be done. Pure does NOT mean that a function is pure in the formal sense, just that the compiler can make assumptions about deleting identical calls, and deleting useless calls. **************************************************************** From: Robert Dewar Sent: Wednesday, February 4, 2004 7:41 AM Pascal Leroy wrote: > Actually I would be in favor of deleting 10.2.1(18) altogether, as it's > one of these rules (like 11.6) which effectively says "forget the rest > of this book, we don't really mean it". But I guess that's a different > discussion. This would be an unacceptable change! The optimziation of pure functions can be quite critical to efficiency in some applications, and certainly other languages have this capability. Of course removing it from the language would merely mean we had to invent a new pragma for an implementation (actually GNAT already does that with Pure_Function, which allows a function to be declared pure for optimization purposes without the whole package being Pure -- which carries so much irrelevant baggage along with it. Yes, you can worry from a theoretical point of view that 10.2.1(18) in conjunction with nasty stuff, e.g. calling unknown foreign language routines, can result in problems, but I don't think that's a major issue in practice. **************************************************************** From: Robert Dewar Sent: Wednesday, February 4, 2004 7:50 AM Randy Brukardt wrote: > In the context of AI-362, I was thinking about how to get any sort of > logging/debugging into a Preelaborated unit. > > The problem is that once a preelaborated unit has that pragma applied, you > can no longer with any I/O. Without I/O, you can't have any logging for > faults in a fielded system (if the system dies, it probably won't get to > dump an in-memory log, making it useless for field debugging - you probably > can't run a debugger on the target system - think about NASA's rovers...) Well of course you can do I/O, you just can't use the (somewhat dubious in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO which is a useful simple subset of Text_IO, and the documentation in g-io.ads says: -- A simple text I/O package that can be used for simple I/O functions -- in user programs as required. This package is also preelaborated, -- unlike Text_IO, and can thus be with'ed by preelaborated library -- units. The implementation is quite straightforward, using pragma Import on standard C library functions. **************************************************************** From: Robert A. Duff Sent: Wednesday, February 4, 2004 8:32 AM > The problem is that once a preelaborated unit has that pragma applied, you > can no longer with any I/O. I do debugging/tracing output in pragma-Pure and pragma-Preelab packages all the time. I have a debug package containing things like Put, and Put is declared ``pragma Import(Ada, Put, "Impure_Put");''. Impure_Put does the dirty work -- it is not with'ed by the debug package. The debug package has pragma Pure, so can be used anywhere. **************************************************************** From: Robert Dewar Sent: Wednesday, February 4, 2004 8:48 AM Pure seems wrong for that. If you have while not EOF loop ... end loop; then the compiler can (and most likely will) pull the test for EOF out of the loop. It is even OK in: put ("-----"); put ("-----"); to eliminate the second call. It seems conceptually wrong to have an I/O package be pure, though of course it can be programmed and may be useful in practice. It seems fine for an I/O package to be preelaborable. **************************************************************** From: Robert A. Duff Sent: Wednesday, February 4, 2004 9:22 AM > It seems conceptually wrong to have an I/O package be pure, ... Yes, it is. I do it anyway, because in some cases it's useful for debugging. Of course, I have to keep in mind that the compiler can transform the code in various ways that might affect the actual printouts. >...though > of course it can be programmed and may be useful in practice. Interestingly, my previous message just happened to be number 666 in my mailbox -- maybe that's a sign of how evil that package is. ;-) **************************************************************** From: Randy Brukardt Sent: Wednesday, February 4, 2004 9:49 PM Robert Dewar said: ... > Remember that you can always do anything by doing raw import > rather than WITH. Of course. But the design of Pure seems to imply that if you just write straight Ada code, the optimizer won't do anything bad to you. > It is really nothing to get worried about that this sort of > thing can be done. > > Pure does NOT mean that a function is pure in the formal > sense, just that the compiler can make assumptions about > deleting identical calls, and deleting useless calls. If this is the case, then there is no need for all of the restrictions on Pure units. We should just make the restrictions the same as Preelaborate, and say user-beware. (That doesn't seem very much in keeping with the Ada philosophy to me, but this is a weird case.) **************************************************************** From: Randy Brukardt Sent: Wednesday, February 4, 2004 10:01 PM Robert Dewar wrote: ... > Well of course you can do I/O, you just can't use the (somewhat dubious > in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO > which is a useful simple subset of Text_IO, and the documentation in > g-io.ads says: Well, I don't want to have to use implementation-defined stuff to make logging (which will remain in the application for its entire lifetime) work. That locks me into a specific compiler. If this is so easy to implement, then it seems that we should look at requiring some of the predefined IO to be preelaborated. Then there is no more problem. At least one implementer has objected to doing that, which is what led to the musings. Bob said: > I do debugging/tracing output in pragma-Pure and pragma-Preelab packages > all the time. I have a debug package containing things like Put, and > Put is declared ``pragma Import(Ada, Put, "Impure_Put");''. Impure_Put > does the dirty work -- it is not with'ed by the debug package. The > debug package has pragma Pure, so can be used anywhere. That's also not portable (although more so); most Janus/Ada compilers standard mode is "no Import/Export". That allows "one button" program creation (no bizarre system linkers needed); everything is in the Ada program. Of course, we support Import/Export in "clunky program creation mode", but I use that only when access to actual foreign code is needed. Pragma Import should be prohibited in Pure units anyway (I realize that isn't going to happen, it would break too many existing kludges). **************************************************************** From: Pascal Leroy Sent: Thursday, February 5, 2004 3:49 AM Randy replied to Robert: > > Remember that you can always do anything by doing raw import rather > > than WITH. > > Of course. But the design of Pure seems to imply that if you > just write straight Ada code, the optimizer won't do anything > bad to you. I agree with Randy. You can do all sorts of nasty things with low-level programming and interfacing with other languages, but the rules in 10.2.1 seem to be designed so that the na‹ve programmer is not unwittingly bitten by the permission of 10.2.1(18). It just happens that access types must be taken into account here. **************************************************************** From: Robert Dewar Sent: Thursday, February 5, 2004 4:14 AM Randy Brukardt wrote: > If this is so easy to implement, then it seems that we should look at > requiring some of the predefined IO to be preelaborated. Then there is no > more problem. At least one implementer has objected to doing that, which is > what led to the musings. How about a subset of Text_IO that removes a lot of the junk (e.g. page marks, line counting etc), handles just strings and characters (can always use 'Image) and is Preelaborable. **************************************************************** From: Robert I. Eachus Sent: Thursday, February 5, 2004 8:30 AM > How about a subset of Text_IO that removes a lot of the junk (e.g. page > marks, line counting etc), handles just strings and characters (can > always use 'Image) and is Preelaborable. Excellent idea. Call it Simple_IO or something, include put for Integer, Float, and possibly Duration, plus Long_ and Short_ variants if supported by the compiler. Hmmm. No. Feature creap. We could include the Short_ and Long_ types in the spec, and in Ada 0Y we should probably just bite the bullet and require that Short_Integer, Integer, and Long_Integer be supported along with Short_Float, Float, and Long_Float in standard. (They can be renames for compilers that only support two (or one) type) But that is a different AI. For Simple_IO. A better idea would be to have child generics for other integer, real, enumeration types, and non-Latin1 character types. Yes that will require instantiation inside subprograms if you want to use them in pre-elaboratable units. But I don't see that as a significant issue. **************************************************************** From: Robert Dewar Sent: Thursday, February 5, 2004 7:29 PM Why burden it with all this junk. It is fine to use 'Image for numeric stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or better I will give the spec here. This has proved very useful: -- A simple preelaborable subset of Text_IO capabilities -- A simple text I/O package that can be used for simple I/O functions in -- user programs as required. This package is also preelaborated, unlike -- Text_IO, and can thus be with'ed by preelaborated library units. -- Note that Data_Error is not raised by these subprograms for bad data. -- If such checks are needed then the regular Text_IO package must be used. package GNAT.IO is pragma Preelaborate (IO); type File_Type is limited private; -- Specifies file to be used (the only possibilities are Standard_Output -- and Standard_Error). There is no Create or Open facility that would -- allow more general use of file names. function Standard_Output return File_Type; function Standard_Error return File_Type; -- These functions are the only way to get File_Type values procedure Get (X : out Integer); procedure Get (C : out Character); procedure Get_Line (Item : out String; Last : out Natural); -- These routines always read from Standard_Input procedure Put (File : File_Type; X : Integer); procedure Put (X : Integer); -- Output integer to specified file, or to current output file, same -- output as if Ada.Text_IO.Integer_IO had been instantiated for Integer. procedure Put (File : File_Type; C : Character); procedure Put (C : Character); -- Output character to specified file, or to current output file procedure Put (File : File_Type; S : String); procedure Put (S : String); -- Output string to specified file, or to current output file procedure Put_Line (File : File_Type; S : String); procedure Put_Line (S : String); -- Output string followed by new line to specified file, or to -- current output file. procedure New_Line (File : File_Type; Spacing : Positive := 1); procedure New_Line (Spacing : Positive := 1); -- Output new line character to specified file, or to current output file procedure Set_Output (File : File_Type); -- Set current output file, default is Standard_Output if no call to -- Set_Output is made. private type File_Type is (Stdout, Stderr); -- Stdout = Standard_Output, Stderr = Standard_Error pragma Inline (Standard_Error); pragma Inline (Standard_Output); end GNAT.IO; **************************************************************** From: Randy Brukardt Sent: Thursday, February 5, 2004 6:29 PM Robert Dewar wrote, replying to me: > How about a subset of Text_IO that removes a lot of the junk (e.g. page > marks, line counting etc), handles just strings and characters (can > always use 'Image) and is Preelaborable. I have no objection to that, although it seems like a tough sell to me (why do we need yet another I/O library?). My preference would be to make Text_IO and Stream_IO preelaborated (because these have relatively simple dependence on the underlying system), leaving the rest alone. But I don't see that any library that allows file I/O (for logging) is going to easily go around the objections of existing implementers. All of our I/O is built on top of a single package (called System.Basic_IO), and if it is too hard to make that preelaborated, it seems unlikely that it would be much easier to implement a subset of it that is preelaborated. I believe that this is a common implementation (Pascal indicated that Rational's is organized the same way). And if you can make that preelaborated, making Stream_IO and Text_IO preelaborated is pretty simple. The only thing that causes trouble with making Text_IO preelaborated in Janus/Ada is handling Current/Standard_Input/Output. While we could dispense with redirection, we can hardly eliminate the concept of Standard_Output from Simple_IO. **************************************************************** From: Robert I. Eachus Sent: Friday, February 6, 2004 5:39 PM Robert Dewar wrote: > Why burden it with all this junk. It is fine to use 'Image for numeric > stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or > better I will give the spec here. This has proved very useful: I argee with Robert Dewar. That's what the "No. Feature Creep." in the message was saying. But the edited message as RBKD answered it is not how it looked to me. (It may just have been where the line breaks fell...) To be clear, I thought I was seconding Robert Dewar's suggestion, and noting that any support for other types in Standard could be pushed to child packages if a vendor wanted to include such support. I was also serious that in Ada 0Y we should at least require Long_Integer and Long_Float. Implementors who actually have only one base integer or floating point type could just put in a renames. There is little or no hardship for implementors there. But I am writing more and more code that uses Long_Float by default. I've never had to modify any of it to compile on a system that didn't support Long_Float, even though such a modification is also trivial. If everyone is supporting Long_Float, why don't we say so? **************************************************************** From: Randy Brukardt Sent: Friday, February 6, 2004 5:40 PM Robert Dewar said: > Why burden it with all this junk. It is fine to use 'Image for numeric > stuff, have Simple_IO just do string stuff (have a look at GNAT.IO), or > better I will give the spec here. This has proved very useful: I totally agree about the string-only stuff. I've been making do with a single logger procedure, after all. But the spec. you provided don't allow output to files. That won't do. Virtually all of the programs I work on these days either run unattended (web server, spam filter) or are GUI programs. In neither case, can you use Standard_Output (the unattended programs because there is no screen, and the logging is a permanent function used for debugging of production errors; the GUI programs because having a standard output window on the screen can interfere with the behavior that you are trying to debug -- or it is unreadable, because it is underneath the program's windows). So I think the package needs Open, Create, Close, and Delete. (But not necessarily Name or Form or Reset, as those tend to make the implementation harder). And I'd leave out Set_Output; it complicates things when you have files and you certainly can work around not having it. But the other operations are just about right. **************************************************************** From: Robert Dewar Sent: Friday, February 6, 2004 5:44 PM I agree that Long_Float should be unconditionally supported. **************************************************************** From: Robert A. Duff Sent: Thursday, February 5, 2004 8:24 AM Randy said: > Robert Dewar wrote: > ... > > Well of course you can do I/O, you just can't use the (somewhat dubious > > in any case) predefined Ada I/O packages. In GNAT, we provide GNAT.IO > > which is a useful simple subset of Text_IO, and the documentation in > > g-io.ads says: > > Well, I don't want to have to use implementation-defined stuff to make > logging (which will remain in the application for its entire lifetime) work. > That locks me into a specific compiler. Agreed. > If this is so easy to implement, then it seems that we should look at > requiring some of the predefined IO to be preelaborated. Then there is no > more problem. At least one implementer has objected to doing that, which is > what led to the musings. > > Bob said: > > > I do debugging/tracing output in pragma-Pure and pragma-Preelab packages > > all the time. I have a debug package containing things like Put, and > > Put is declared ``pragma Import(Ada, Put, "Impure_Put");''. Impure_Put > > does the dirty work -- it is not with'ed by the debug package. The > > debug package has pragma Pure, so can be used anywhere. > > That's also not portable (although more so); ... I think it *is* portable. Convention "Ada" and the relevant pragmas are not optional features of the language. You don't need any special linker options to get the impure part included -- just put a "with" of the impure version of the package somewhere in the program. By the way, although the semantics of my impure/pure cheat is somewhat dubious, as Robert Dewar pointed out, in practise it's no worse than what happens with an interactive debugger -- if the compiler has chosen to stir the code around, the debugger gets confused and sometimes lies. That's a pain, but getting a debugger to tell the truth about optimized code is a rather difficult trick. Pragma-Pure-related optimizations are but one example. Another standard trick is to use Import(Ada) to suppress default initialization. This is specified in RM-B.1(38), and mentioned in AARM-13.3(12.c) and B.1(38.a). >... most Janus/Ada compilers > standard mode is "no Import/Export". If you mean "pragma Import(Ada) is illegal in this mode, then you can make that the *default* mode if you like, but I don't think you can call it "standard". ;-) If you're talking about extra linker options, then fine -- but my ugly hack needs no extra linker options. >... That allows "one button" program > creation (no bizarre system linkers needed); everything is in the Ada > program. > > Of course, we support Import/Export in "clunky program creation mode", but I > use that only when access to actual foreign code is needed. > > Pragma Import should be prohibited in Pure units anyway (I realize that > isn't going to happen, it would break too many existing kludges). Over My Dead Body. It would break *my* kludge! (I'm sure I'm not the only one who's thought of it.) Anyway, it could be useful to import a pure C function into a pragma-Pure package -- nothing wrong with that. Of course, if the C function is impure, you might get a surprise, but that's no different than what happens if you get the parameter types and modes wrong. In general, import is a programmer-beware feature, and we should not place restrictions on it. I say the same about machine code -- if your machine has a "cosine" instruction, there's nothing wrong with using it in a pragma-Pure place. **************************************************************** From: Randy Brukardt Sent: Thursday, February 5, 2004 7:34 PM ... > > Bob said: > > > > > I do debugging/tracing output in pragma-Pure and pragma-Preelab packages > > > all the time. I have a debug package containing things like Put, and > > > Put is declared ``pragma Import(Ada, Put, "Impure_Put");''. Impure_Put > > > does the dirty work -- it is not with'ed by the debug package. The > > > debug package has pragma Pure, so can be used anywhere. > > > > That's also not portable (although more so); ... > > I think it *is* portable. Convention "Ada" and the relevant pragmas are > not optional features of the language. I agree with that. But... > You don't need any special > linker options to get the impure part included -- just put a "with" of > the impure version of the package somewhere in the program. I don't agree with this. Not the inclusion part, but the notion that it ought to work. You're declaring external code, and if your toolchain doesn't support external code, then it isn't going to work even if all of the code is actually in this program. Keep in mind that I'm only interested (for the purposes of this discussion) in what happens in an all-Ada program. Once you start bringing C and hardware into the equation, all bets are off anyway. ... > Another standard trick is to use Import(Ada) to suppress default > initialization. This is specified in RM-B.1(38), and mentioned in > AARM-13.3(12.c) and B.1(38.a). We support that, of course. I believe that there is special code to suppress the linker symbol in such a case - if there wasn't, the program could never link (because there is nothing that you are actually linking to). But note that in this case you're not really using Import at all, because the object still resides here, it's just not default initialized. (If you had the object reside somewhere else, you couldn't use the feature in an all-Ada program: how would you suppress default initialization at the location of the actual object? Well, you'd have to apply Import again, and you'd have an infinite regress.) > >... most Janus/Ada compilers > > standard mode is "no Import/Export". > > If you mean "pragma Import(Ada) is illegal in this mode, then you can > make that the *default* mode if you like, but I don't think you can call > it "standard". ;-) No. You just can't link the program; it contains external references, and the standard linker doesn't support external references. > If you're talking about extra linker options, then > fine -- but my ugly hack needs no extra linker options. No again; you have to use a different toolchain. That's the problem. Our compiler design is all-Ada top to bottom. The linker is of our own design, and creates a executable directly (no other programs run). This is the standard mode. That's not negotiable; we control the entire toolchain, and that's a requirement if you're staking your business on it. At a (much) later date, we added support for Import/Export. That was done using a special linker that outputs the entire Ada program as one giant system object file (complete with any Import/Export linker symbols), and then is given to the system linker. Now, the problem is you (Bob) are suggesting two uses for pragma Import: * To link to code somewhere else within the Ada program; and * To link to code external to the Ada program. The fact that the convention is "Ada" doesn't really change this; you still want to be to link to other, separate Ada (or assembler!) subsystems using the system linker. The problem is that there is no way to tell those apart; but they are using *different* linkers. The "solution" is simply to assume that you are linking to code external to the program (that works on internal code by accident); but that implies that you are using the "secondary" toolchain. And it also implies very substandard code (all imports call through thunks so that the actual external reference occurs in only one place). Even if, by some miracle, you could figure out which was which, it still would be impossible, because the Ada linker does not use link names. It simply uses address offsets, which of course change every time a change (or even recompile with different switches) occurs to a spec. You can't put them into "pragma Import" code because the only way to find out the right address is to with the appropriate package -- which defeats the purpose of the Import. The beauty of Ada is that you *don't* have to depend on unreliable code/tools from a whole bunch of unreliable vendors (just one unreliable Ada vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you need to access C, but (on Windows, at least) it's a lot rarer than people make out. (I believe that you can run any Claw GUI program and almost any Win32 program without any use of imported C code in the executable. Of course, you're using the C code that makes up the Windows kernel - AdaOS doesn't exist yet - but you need no Microsoft tools or code to create running programs.) We of course validated all of our compilers in the standard linker; we used the alternate chain only on C interface programs. (With the exception of Windows, for which the standard linker didn't exist when we validated.) I would be *very* upset if that did not remain true. **************************************************************** From: Robert Dewar Sent: Friday, February 6, 2004 4:12 PM Randy Brukardt wrote: > Our compiler design is all-Ada top to bottom. The linker is of our own > design, and creates a executable directly (no other programs run). This is > the standard mode. That's not negotiable; we control the entire toolchain, > and that's a requirement if you're staking your business on it. An exceedingly odd claim. Any real Ada compiler must depend on proprietary tool chains to be any use at all in my view. Anyway for sure this is off topic. I must say I agree with Bob Duff's view here that pragma Import Ada must work as he expects. > Now, the problem is you (Bob) are suggesting two uses for pragma Import: > > * To link to code somewhere else within the Ada program; and > * To link to code external to the Ada program. > > The fact that the convention is "Ada" doesn't really change this; you still > want to be to link to other, separate Ada (or assembler!) subsystems using > the system linker. Yes, both are reasonable, both should work > The problem is that there is no way to tell those apart; but they are using > *different* linkers. The "solution" is simply to assume that you are linking > to code external to the program (that works on internal code by accident); > but that implies that you are using the "secondary" toolchain. And it also > implies very substandard code (all imports call through thunks so that the > actual external reference occurs in only one place). That's an artifact of your peculiar linking approach. The Ada design clearly anticipates a conventional approach to linking. > Even if, by some miracle, you could figure out which was which, it still > would be impossible, because the Ada linker does not use link names. It > simply uses address offsets, which of course change every time a change (or > even recompile with different switches) occurs to a spec. You can't put them > into "pragma Import" code because the only way to find out the right address > is to with the appropriate package -- which defeats the purpose of the > Import. Well as I say, I think you have chosen a model that makes it very hard for you to do Import/Export correctly. > The beauty of Ada is that you *don't* have to depend on unreliable > code/tools from a whole bunch of unreliable vendors (just one unreliable Ada > vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you > need to access C, but (on Windows, at least) it's a lot rarer than people > make out. (I believe that you can run any Claw GUI program and almost any > Win32 program without any use of imported C code in the executable. Of > course, you're using the C code that makes up the Windows kernel - AdaOS > doesn't exist yet - but you need no Microsoft tools or code to create > running programs.) I don't buy this claim. I think your implementation (linking) approach is fundamentally flawed, and the difficulties you are having with Import/Export (Ada) are just a symptom of this. > We of course validated all of our compilers in the standard linker; we used > the alternate chain only on C interface programs. (With the exception of > Windows, for which the standard linker didn't exist when we validated.) I > would be *very* upset if that did not remain true. I don't see any basis for upset. I suspect that virtually all Ada 95 compilers do Import/Export Ada the way Bob expects it to work, and in fact it is a handy way of avoiding certain elaboration problems (very low level and junky, but sometimes you need that). Quite a few of the DEC tests use this capability, so ACT was actually contractually obligated to get this working (of course it worked out of the box, if you use a standard linking approach, it will). I think the approach Randy describes is suitable for Ada 83, but not for Ada 95. That is, I agree with him that it seems very hard to make this approach work properly. Perhaps we should indeed discuss this in the ARG, and decide what is and what is not supposed to work in this regard. **************************************************************** From: Randy Brukardt Sent: Friday, February 6, 2004 5:28 PM Robert Dewar wrote: > Randy Brukardt wrote: > > Our compiler design is all-Ada top to bottom. The linker is of our own > > design, and creates a executable directly (no other programs run). This is > > the standard mode. That's not negotiable; we control the entire toolchain, > > and that's a requirement if you're staking your business on it. > > An exceedingly odd claim. Any real Ada compiler must depend on > proprietary tool chains to be any use at all in my view. Why? It wasn't necessary for CP/M and MS-DOS, it isn't necessary for Windows (you can access all of Win32 and any DLL without using any Microsoft or other tool), and it certainly isn't necessary for bare machines. The only place that I'm familiar with that it *is* necessary is on Unix/Linux systems - and I consider that a design fault in those systems. (It is actually possible to build a full Ada compiler on Intel ABI machines without using any stuff from any Unix - our SCO compiler did exactly that - but I wouldn't do it again because too much stuff is implemented in the C libraries rathe in the Kernel.) Indeed, I don't see any Microsoft tools used in the Windows version of GNAT. You can use it out of the box to make programs for Windows without getting anything else. So what proprietary tools do you need? ... > > The beauty of Ada is that you *don't* have to depend on unreliable > > code/tools from a whole bunch of unreliable vendors (just one unreliable Ada > > vendor!) - you can do it *all* in Ada. Now, it obvious that sometimes you > > need to access C, but (on Windows, at least) it's a lot rarer than people > > make out. (I believe that you can run any Claw GUI program and almost any > > Win32 program without any use of imported C code in the executable. Of > > course, you're using the C code that makes up the Windows kernel - AdaOS > > doesn't exist yet - but you need no Microsoft tools or code to create > > running programs.) > > I don't buy this claim. I think your implementation (linking) approach > is fundamentally flawed, and the difficulties you are having with > Import/Export (Ada) are just a symptom of this. Fine. I want an all-Ada world as much as possible. I realize the current state of the world makes that impractical most of the time. But I don't believe for a minute that the standard insists on anything that would make it impossible to have an all-Ada world. If we cannot at least strive for a more perfect world > > We of course validated all of our compilers in the standard linker; we used > > the alternate chain only on C interface programs. (With the exception of > > Windows, for which the standard linker didn't exist when we validated.) I > > would be *very* upset if that did not remain true. > > I don't see any basis for upset. I suspect that virtually all Ada 95 > compilers do Import/Export Ada the way Bob expects it to work, and in > fact it is a handy way of avoiding certain elaboration problems (very > low level and junky, but sometimes you need that). It is exceedingly bad practice to have dependencies within an Ada program not documented in the context clause. I see absolutely no reason to encourage that - even if, as a practical matter, you'll need to once in a great while do something exceedingly bad. (In which case, you can use the alternative toolchain set up for that purpose. Just don't ask us to validate it - once you're depending on unknown external code, there is essentially nothing we can do to insure correctness.) Any rep. clause pointing inside of the Ada code itself is a terrible idea. [I know people do this as a workaround for "in out" function parameters, for example, but I find that horrific -- fix the root problem (using the wrong mode on the parameter), don't stand on your head. And lobby to get the language fixed...] > I think the approach Randy describes is suitable for Ada 83, but not > for Ada 95. That is, I agree with him that it seems very hard to make > this approach work properly. In general, approaches that worked for Ada 83 were supposed to continue working in Ada 95. We explicitly pointed out where we expected that *not* to be true. I would consider anything in the standard that make requirements to the contrary to fall under the Dewar rule "the standard never says anything silly". ... > Perhaps we should indeed discuss this in the ARG, and decide what is > and what is not supposed to work in this regard. I think that this would be a particularly bad use of the ARG's time. I can't imagine anything good that would come out of such a discussion - any conclusion is going to be considered very bad by someone, and I certainly don't hear users complaining about this issue. **************************************************************** From: Robert Dewar Sent: Friday, February 6, 2004 6:38 PM Randy Brukardt wrote: >>An exceedingly odd claim. Any real Ada compiler must depend on >>proprietary tool chains to be any use at all in my view. > > Why? It wasn't necessary for CP/M and MS-DOS, Most people would not consider those operating systems, and you were talking about Ada 83, not Ada 95. it isn't necessary for Windows You are arguing in a circular manner. You argue that the language allows your approach, which then validates the approach, which you then use to invalidate an important feature of the language (export import Ada). >>Perhaps we should indeed discuss this in the ARG, and decide what is >>and what is not supposed to work in this regard. > I think that this would be a particularly bad use of the ARG's time. I can't > imagine anything good that would come out of such a discussion - any > conclusion is going to be considered very bad by someone, and I certainly > don't hear users complaining about this issue. You might be right, I think the major complete language Ada 95 compilers do in fact fully implement pragma import/export for Ada anyway. **************************************************************** From: Randy Brukardt Sent: Friday, February 6, 2004 6:57 PM Robert Dewar: > it isn't necessary for Windows > > You are arguing in a circular manner. You argue that the language > allows your approach, which then validates the approach, which you > then use to invalidate an important feature of the language (export > import Ada). Well, clearly the problem is the "important feature of the language". I view Import/Export Ada to be something of no particular value that naturally fell out of the other definitions (sort of like non-binary modular types). It's for external interfacing to other Ada subsystems: a very rare event. Some people have found a way to abuse this capability in order to end-run Ada's protections. > >>Perhaps we should indeed discuss this in the ARG, and decide what is > >>and what is not supposed to work in this regard. > > > I think that this would be a particularly bad use of the ARG's time. I can't > > imagine anything good that would come out of such a discussion - any > > conclusion is going to be considered very bad by someone, and I certainly > > don't hear users complaining about this issue. > > You might be right, I think the major complete language Ada 95 compilers > do in fact fully implement pragma import/export for Ada anyway. Certainly Janus/Ada does: but only if you use the alternative toolchain. And we don't provide or support the external parts (i.e. the ones from Microsoft) needed for that. And that's not a problem for customers: if you're going to link C code, you need a C compiler anyway. But I don't want to have to make Ada-only customers have to purchase full-blown development systems from elsewhere, and I don't want our validations depending on tools that we don't provide or support. **************************************************************** From: Robert Dewar Sent: Friday, February 6, 2004 7:17 PM Randy Brukardt wrote: > Some people have found a way to abuse this capability in order to > end-run Ada's protections. Sounds like you are saying you don't like the feature. OK, everyone has that privilege, but it is part of the language, and its ability to bypass in a very deliberate way protections that are in the language is useful, just as address overlays are useful. In any case, the feature is there, it must be implemented whether you like it or not :-) > Certainly Janus/Ada does: but only if you use the alternative toolchain. And > we don't provide or support the external parts (i.e. the ones from > Microsoft) needed for that. OK that's fine. > And that's not a problem for customers: if > you're going to link C code, you need a C compiler anyway. But I don't want > to have to make Ada-only customers have to purchase full-blown development > systems from elsewhere, and I don't want our validations depending on tools > that we don't provide or support. Well to validate import/export, you need external compilers anyway, so I don't know what you are talking about here. The ACATS tests have non-Ada in them. It seems perfectly fine to tell your users that to use pragma Import/Export Ada they need the alternative toolchain. I do think the ACATS tests should test Import/Export Ada, since this is definitely a feature that is used, and I consider it an important feature. **************************************************************** From: Randy Brukardt Sent: Friday, February 6, 2004 7:55 PM Robert Dewar: > Sounds like you are saying you don't like the feature. OK, everyone > has that privilege, but it is part of the language, and its ability > to bypass in a very deliberate way protections that are in the language > is useful, just as address overlays are useful. Another mis-feature that ought to be restricted as much as possible. As with Imports, the danger is creating an overlay by accident. I'd prefer users that have to put "pragma Warranty_Void();" on any address overlays before they'd work. :-) ... > Well to validate import/export, you need external compilers anyway, > so I don't know what you are talking about here. The ACATS tests > have non-Ada in them. We used "special options" (which actually was a completely different set of programs) for the Interface C tests. The reason was the need to link foreign language code. That reason would not apply to Import/Export Ada. > It seems perfectly fine to tell your users that to use pragma > Import/Export Ada they need the alternative toolchain. > > I do think the ACATS tests should test Import/Export Ada, since > this is definitely a feature that is used, and I consider it an > important feature. Well, given the budget of the ACAA, and the feeling of vendors, I doubt that there is going to be many (if any) new ACATS tests on Ada other than those steming from the Corrigendum and (soon) the Amendment. There are lots of core rules (supposedly 30%, although I think the actual number of testable ones is more like 10%) that matter to users (read-only access to protected components from functions, for one example) that are untested - it's hard to imagine how to choose what ought to be tested. Vendors don't want hundreds of new tests. **************************************************************** From: Robert Dewar Sent: Friday, February 6, 2004 3:40 PM Making Text_IO preelaborated is far too big a change, it might result in a need for a radical reimplementation. There is simply nothing to justify such a huge change. **************************************************************** From: Pascal Leroy Sent: Tuesday, March 16, 2004 7:30 AM A quick note following the discussion of AI 366 at the last meeting: imported objects should be excluded from the restrictions in 10.2.1(9), as no initialization takes place for these objects (B.1(38)). This change would make it possible to write preelaborable units that reference objects exported from the runtime system. Not that any user would want to do that, but implementers might want to do it, for instance to implement some of the "Alan packages". **************************************************************** From: Robert Dewar Sent: Saturday, March 20, 2004 7:54 PM Well I am dubious about the idea of semantics that are useful only for implementors, but if this is the case, implementors can do anything they like anyway within the run time library, since it does not have to be in Ada at all. **************************************************************** From: Robert A. Duff Sent: Sunday, March 21, 2004 11:35 AM True, but why force implementors to have (another?) special case in their compilers, of the form "if we're compiling the run-time system then ... else ... end if;"? Anyway, I'm not sure I believe that the proposed rule is useful only for implementors. Pascal pointed out one usage (which is for implementors), but the feature in question is harmless and natural, and therefore might be useful for anybody. The wording of 10.1.2(9) talks about a "default-initialized object", so it seems pretty silly to forbid imported objects, since they are not, in fact, initialized by default. ****************************************************************