Version 1.1 of acs/ac-00061.txt

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

!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).

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

Questions? Ask the ACAA Technical Agent