ISO/ IEC JTC1/SC22/WG14 N766

N766                    Inlining Issues                       N766
J11/97-130              ---------------                 J11/97-130

                       23 September 1997
                         Tom MacDonald
                         [email protected]

Introduction
------------

Function inlining was added to the C9X Draft at the London meeting.
Some changes were made to the base proposal.  This proposal identifies
some of those changes and explores some alternatives that might provide
a better inlining feature.


Issue #1


The constraint below:


   6.5.4  Function-specifiers

       [#4] An inline definition (see below)  of  a  function  with
       external  linkage  shall  not  define  an  object  of static
       storage  duration  or  refer  to  an  object  with  internal
       linkage.


Is needlessly strict.  There are two cases where the translator must
handle static data:  string literals and __func__.  Since the translator
must "correctly" handle some objects with static storage duration that are
not modifiable, there appears to be no additional burden on the
implementor if all non-modifiable objects with static storage duration are
allowed inside inline definitions.

Seems like the "refer to an object with internal linkage" is not
strict enough.  The same problem exists for functions with internal
linkage.

The following modification is proposed:


------------------------------------------------------------------------
|                                                                      |
| An inline definition (see below) of a function with external linkage |
| shall not contain a definition of an object with static storage      |
| duration that can be modified, and shall not contain a reference     |
| to an identifier with internal linkage.                              |
|                                                                      |
------------------------------------------------------------------------

These words allow:

   inline double circum(float radius) {
      static const double pi = 3.14159;  // OK - pi is not modifiable
      return 2.0 * pi * radius;
   }

and forbid:

   static int funny(void) { return __LINE__; }

   inline int bad(void) { return funny(); }  // Error - funny has
                                             // internal linkage

--------------------------------------------------------------------

Issue #2

The constraint below:


   6.5.4  Function-specifiers

       [#5] A file scope declaration without inline for a function
       with external linkage shall not follow a definition with
       inline of that function.

contains the most significant change to the original inline proposal
and was caused by a concern for one pass compilers.

The first problem is that the new words seem to disallow the following:

        extern int add(int x, int y);
        inline int add(int x, int y) {return x + y;}
        extern int add(int x, int y);  // Error?

Also, the following is disallowed:

        int add(int x, int y) { return x + y; }
        inline int add(int, int);
        extern int add(int x, int y);  // Error?

because a file scope declaration without inline follows a definition with
inline.  Doesn't seem like this was intended.

The following words make it an error only if it's an inline definition
up to that point.

    -----------------------------------------------------------------------
    |                                                                     |
    |  [#5] If the definition of a function with external linkage has the |
    |  inline specifier and is not preceded by a file scope declaration   |
    |  of that function without the inline specifier, then it shall not   |
    |  be followed by such a declaration.                                 |
    |                                                                     |
    -----------------------------------------------------------------------


Forbidding:

        inline int add(int x, int y) {return x + y;}
        extern int add(int x, int y);

--------------------------------------------------------------------

Final Issue:


The belief seems to be that single pass compilers are burdened and forced
to keep a copy of the body of any inline function around.  Thus the
programmer must write:

        extern int add(int x, int y);
        inline int add(int x, int y) {return x + y;}

instead.  This turns out to be quite an onerous burden to place on the
programmer.  If we can show that single pass implementations are not
burdened by the original rules, then perhaps this can be relaxed.

It's a burden on programmers because they now bump into situations where
they have to write:

       extern int add(int x, int y);
       #include "common_defs.h"

When enhancing existing programs it is far more natural to write:

       #include "common_defs.h"
       extern int add(int x, int y);

(Granted there are other work-arounds but I think most can agree that
if this restriction is unnecessary, then inline is easier to use).

The belief seems to be that a single pass implementation would have to
needlessly keep a copy of the inline function around.  If an implementation
performs inlining, then a copy is kept around anyway in case an inlining
opportunity arises.

If the implementation performs no inlining, then the fear is that an
inline definition is carried around internally until the end of the
translation unit just in case an external definition appears, and the
implementation must materialize a definition.  One easy scenario is to
write the internal representations of the inline candidates to a file.  If
an external definition is required, the compiler retrieves the internal
representation from the file and generates code.

The question becomes, is this too big of a burden to place on an
implementation when compared to the burden placed on the programmer?

--------------------------------------------------------------------

Nits:

The Draft talks about:

  "The declaration of an identifier for a function" and not about
  "a function declaration" (which seems to include pointers to
  functions, etc.).  Therefore, the following tweaks are recommened.


  [#3] Function-specifiers shall be used only in function declarations.
                                              ^^^^^^^^^^^^^^^^^^^^^^^^
                                              with function declarators


or possibly:


  [#3] Function-specifiers shall be used only in function declarations.
                                              ^^^^^^^^^^^^^^^^^^^^^^^^
                     in the declaration of an identifier for a function


Better specification words:


  [#6] The inline function specifier shall not appear in a declaration of main.
                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                      be used in a declaration of the identifier /main/ if the
                      identifier has external linkage.


The following seems better specified:

  [#7] A function declaration with an inline function
                  ^^^^^^^^^^^
                  declared

       specifier declares an inline function.
                 ^^^^^^^^
                 is


Typo and a tweak:

       [#8] Any function with internal linkage  can  be  an  inline
       function.    For  a  function  wtih  external  linkage, the
                                      ^^^^
                                      with

       following restrictions apply.  If a function is declared
       with an inline function specifier, then it shall also be
       defined in the same translation unit.  If all of the file
                                                ^
                                                the definition and

       scope  declarations  for  a  function  in a translation unit
       include the inline function specifier, then  the  definition
       in that translation unit is an inline definition.  An inline
       ........

Typo:

       [#9] The declaration of an inline  function  can  result  in
       either an external definition, or a definition available for
       use  only  within  the  translation  unit.   A  file   scope
       declaration  without  inline creates an external definition.
       The following example shows an entier translation unit.
                                      ^^^^^^
                                      entire


Rewrite of para. 10:

From:  [#10] Note that the declaration of inline function fahr
       results in the creation of an external definition, but the
       inline definition of cels requires an external definition in
       another translation unit.

To:    [#10] Note that the definition of fahr is an external
       definition because fahr is originally declared without the
       inline keyword, but the definition of cels is an inline
       definition.  Because there is a call to cels, an external
       definition of cels in another translation unit is still
       required by 6.7.