!standard 7.1(01) 03-07-30 AC95-00061/01 !class amendment 03-07-30 !status received no action 03-07-30 !status received 02-10-26 !subject Bodyless packages !summary !appendix !topic Bodyless packages !reference RM95-7.1 !from Chris Miller 02-10-26 !keywords closure packages !discussion !summary It is proposed that a new pragma be added that prevents a package from having a body. As a result of this, any declaration in the package specification that would normally require a completion in the body would be illegal. This would reduce the set of valid declarations in a package spec to be a subset of those normally available. In simple terms, it would prevent the inclusion of any subprogram, task, package or controlled type in the package specification. This provides designers greater control over program closure and in particular provides support for "types" packages. The pragma could be called "Pragma No_Body(package_name)", though the actual name and format to be used is open to discussion. !problem There is currently no way for a system designer to specify and enforce program closure (or "needed by", ARM 10.2(2)) for a set of packages. There is a need for a pragma that provides support for what is often referred to as a "types" package. A "types" package (or packages) is typically used on a large project to collect together a set of common types that are to be used widely elsewhere. The name is included in quotes since "types" packages also typically include constants, they may also include exceptions and they might even include variables (should variables be desired in the spec). However they still seem to be called "types" packages. A problem can arise if (when!) someone subsequently adds a procedure to a "types" package or changes a constant to be a function. A body is then required, which often "withs" other units. The closure set of the types package then suddenly expands to include a lot more code. This can cause problems when the types packages are used in several executable images, possibly across several projects or companies. Examples of these are given below. It should be possible to identify a package as a "types" package, and have the system enforce this. !proposal A new pragma should be added that prevents a package from having a body. This in turn restricts the set of declarations permitted in the specification to be a subset of those normally available. The pragma could be called "Pragma No_Body(package_name)", though the actual name and format to be used is open to discussion. (Strictly speaking, as per ARM 7.2(5), there is still a body but it is restricted to having a single null statement.) In Ada 83 there were essentially no restrictions as to what could be placed into packages. Ada 95 introduced restrictions to support better elaboration and distributed systems (e.g pragmas Pure, Preelaborate, Elaborate_Body, Shared_Passive, Remote_Types and Remote_Call_Interface). This proposal extends this concept further by providing a new pragma to provide additional control over program closure for packages. !example The following demonstrate (cut down) examples of the problems that occur and how this proposal would address them. Example 1. A set of messages pass between Ada code written by two separate companies, Company A and Company B. Company A (who wrote their code first) develops a set of package specs containing the definitions of the messages. At a subsequent meeting Company B agrees that, to save time and reduce errors, they should use the packages written by Company A for their code. At some later stage a developer at company A adds some procedures / functions to the message packages. A package body is then required, let's assume that the body also "withs" some error handling code. Because this is only a small change - and during the development process lots of code is being changed - no one really notices or cares that an additional package body is in the image. Also the error handling code was already being linked in anyway so again, no one at Company A notices the difference. At the next formal release the updated message packages are supplied from Company A to Company B. A developer at Company B then compiles and links the new code and finds that they suddenly have all of Company A's error handling code. They weren't expecting it, and didn't want it. Various scenarios then follow :- (a) Company B stubs the massage packages to remove the unwanted procedures. These are then loaded into the configuration management system as a branch. Two (only slightly different) sets of packages must then be maintained in the future. (b) Company B refuses to accept Company A's code since it has had an unexpected impact on them. Problems arise ... (c) Company B just accepts the code and the unwanted functionality gets included into the image. It remains there for ever and the developers curse "why doesn't someone fix it", but nothing ever happens. (Particularly annoying if the additional error handling code has tasks the do some set-up at elaboration time...) In addition, because of the extra dependencies, Company B finds that they need to change their previously stable subsystem imports (Apex), ADA_INCLUDE_PATH (GNAT) or adapath (Verdix), before the new code will compile and link. Over time changes such as this result in the subsystem / library dependencies becoming untidy. Had it been possible to identify a set of packages as "types" packages, and to be able to enforce this by a suitable pragma, then this situation could have been avoided. Example 2. A program logs certain data / events and writes these to a log file. A much smaller separate program then reads the log file and allows an off line analysis of the data to be performed. It is developed in a later build. The two programs need to share the record types that describe the format of the log file but, beyond that, do not wish to share executable code. However because the type definitions and executable code are all mixed up this is not possible. The second group of developers decide that it is too hard / not possible to change the already tested existing code and so define their own set of record types ... Over time the duplicate types get out of sync and the log analysis program subsequently fails. Again, being able to separate out the definitions from the executable code that operates on them, and being able to document and enforce this by a pragma, would have avoided this problem. !discussion Implications of this proposal are discussed below :- 1. To see exactly what could and could not be included in a package with this pragma it is possible to expand the "basic_declarative_item" syntax rule found in a package spec, and look at all the alternatives. basic_declarative_item :== --representation_clause - permitted --use_clause - permitted --basic_declaration ----subtype_declaration - permitted ----object_declaration - permitted ----number_declaration - permitted ----subprogram_declaration - not permitted ----abstract_subprogram_declaration - not permitted ----package_declaration - not permitted (see note 6) ----renaming_declaration - object & exception renaming permitted. Package, subprogram and generic renaming not permitted. (see note 6) ----exception_declaration - permitted ----generic_declaration - not permitted (see note 6) ----generic_instantiation - not permitted.(see note 6) ----type_declaration ------full_type_declaration. Task types and protected types not permitted. Others (enumeration, integer, real, array, record, access & derived) OK. ------incomplete_type_declaration - permitted ------private_type_declaration - permitted, though the full view would still have to satisfy these rules. ------private_extension_declaration - permitted 2. The pragma Elaborate_Body would not be permitted since it requires a body. These two pragmas are therefore mutually exclusive. 3. Pragma Import would not be able to import any item not allowed by the above rules. So cannot import a procedure, but could import a variable or constant. 4. Not allowing procedures / functions in a spec places obvious restrictions on the use of tagged types. It would still be possible to define tagged types, but no extra primitive operations could be defined. Desperate people could probably find a way around this - such as using a derived type in a different package. Remember this pragma is not intended to apply to every package, rather if we choose to use it we accept the restrictions applied (just like we do when using pragma pure for example). It's optional !. 5. An incomplete access type (ARM 3.10.1) would not be able to have its full_type_declaration in the package body, since there is not going to be a body. 6. For simplicity, the above rules do not allow a package to have nested packages, even though these nested packages may not have bodies. So, for example the following is not permitted :- package A is pragma No_Body; type T1 is . . .; package B is type T2 is . . .; end B; end A; If this is desired then it could be added subject to the requirement that the nested package (B) must also have a pragma No_Body. Likewise you could also then have generic declarations, generic instantiations and renames of these. However this would make the above rules more complex and so may not be worthwhile. Complexity vs. flexibility. There is though the issue of generic library level No_Body packages. Do you allow one but not the other ?. 7. Although the main intent of this pragma would be for packages that are library units, it would still be able to be applied to a package at any level. 8. Applying pragma No_Body to a parent package has no effect on any child package(s). In particular, child packages could still have bodies. 9. An alternative to this proposal could be to enhance pragma restrictions, though it is a configuration pragma. 10. Being a new pragma it would not affect any existing code and it also should be easy to implement. 11. Although not the intent of this, the pragma also could simplify elaboration order issues. Given that there is no package body it is not possible to get program_error by calling a subprogram that has not yet been elaborated. Also there can be no elaboration code executed after the "begin" in the package body, since it doesn't exist. 12. Providing support for "types" packages may be of some use for interfacing other languages to Ada. 13. The pragma could be used to enforce the style that a parent package only acts as a placeholder for a subsystem hierarchy. It contains global types, constants and exceptions, but all other code is to appear in child packages. e.g. package Ada is pragma Pure(Ada); pragma No_Body(Ada); end Ada; Package Ada.Numerics is pragma Pure(Numerics); pragma No_Body (Numerics); Argument_Error : exception; Pi : constant :=3.14159...; e : constant := ...; end Ada.Numerics; The balance of the Ada.Numerics subsystem is then placed into child packages, such as Ada.Numerics.Float_Random, which of course do have bodies. 14. A package with pragma No_Body can still "with" other library units that do have bodies. In practice this could be prevented by placing all the pragma No_Body package(s) in a separate library / subsystem and limiting imports or paths. Requiring pragma No_Body packages to depend semantically on only other such packages (or the predefined Ada environment) is an interesting optional extension. Probably too hard in the short term though. **************************************************************** From: Robert A. Duff Sent: Saturday, October 26, 2002 9:51 AM IMHO, there is insufficient justification for the "pragma No_Body" proposal. If you don't want bodies on your "types" packages, then don't put bodies. If two companies are involved, there are a million things one might do that damages the other -- adding an unwanted body is just one of them. And the best solution involves agreed-upon interfaces, and proper configuration management, not additional pragmas. Note also that if you want to enforce coding conventions of this nature, an ASIS tool might be appropriate. The ARG really needs to be expending its energy on fixing the real flaws in the language: The fact that you can't separately compiled but mutually recursive type declarations. Lack of multiple interface inheritance. and perhaps a few others like: aggregates are illegal for limited types **************************************************************** From: Robert A. Duff Sent: Sunday, October 27, 2002 7:31 PM I agree with Bob, this is an unnecessary frill. The standard should not be in the business of enforcing coding conventions. This is the job of tool sets (compiler options, asis tools, style checkers etc). ****************************************************************