Topic: Nested functions


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 25 Apr 1993 21:04:39 GMT
Raw View
Any comments?

      NESTED FUNCTIONS
      -----------------

This is part 1 of a 2 part proposal which allows nested functions.
In this part, the effect of taking the address of an auto nested
function is not considered.

The second part is intended to consider the creation
of closures of nested functions by taking their address,
as well as the corresponding notion of object closures
in which a member function can be bound to an object.

Syntax
------

The rules for nested functions are simple. A function
can be declared or defined within another function,
in which case it may be termed a member of the function,
with the following restrictions:

1) The keyword auto or static may be specified, auto is the default.

2) There is no default return type.

3) Both declarations and definitions are allowed.

4) The keyword inline may optionally be specified.

5) If a declaration is given, a subsequent definition must
   be given in the same region (scope).

6) A goto in a nested function may not jump out of the function.

7) Static nested functions may not refer to automatic variables or functions
   of their surrounding regions (scope).

8) The address of static nested function may be taken: and yields
   an ordinary function pointer.

9) An function nested directly or indirectly in an class member or friend
   function is a private member or friend of the class (respectively)
  with the same priviledges as the member or friend.

10) No accessible variables may be initialised between a function declaration
    and its definition, in the same region.

11) Nested functions have internal linkage.

12) Functions act like namespaces with respect to overloading,
   that is, a name of a nested function hides a name in an outer scope,
   and the name of a function may be used *inside* the function
   as a namespace name in qualified access.


Explanation and examples.
-------------------------

(1,7,8) The reason for restricting static functions to static data access
is to allow their addresses to be taken and passed outside
the function, without any implication that a dynamic closure need be
formed.

typedef void proc(void);
typedef proc *pproc;

pproc func() {
  int x;
  static void f(){}
  return f;
}

(2) The reason for not alllowing a default return type is
to deprecate this confusing practice.

(3) Allowing declarations and definitions is essential for forward
references in mutually recursive function calls.

(4) Nested functions can be inlined by the compiler whether
inline is specified or not. Allowing inline, however, allows
a clear distinction to be made between variable and function
declarations.

(5) The requirement for a definition in the same scope as a
declaration ensures missing definitions can be detected,
and prevents ambiguity about which definition a declaration
is bound to.

(6) The reason for disallowing nonlocal gotos is basically
to simplify this proposal, and in preparation for
introduction of closures. Non-local gotos could easily
be allowed if closures are not. However, with closures,
non-local gotos seem problematical.

Naturally, exceptions can be thrown by nested functions.

(9) An auto function nested directly in an non-static member function
is also a non-static member function, recursively:
the 'this' pointer is an ordinary part of the context
inherited by the nested function.

Similarly, a static function nested directly in a
member function is a static member function, recursively.

An auto function nested in a static member is a
non-non-static member <grin>. I suppose one could
say it is a static member, except that it is not a static
function.

For example:

class X {
  static int s;
  int i;
  static void f()
  {
    int j;
    void g() {
      s; // ok: g is a static member of X
      j; // ok: j is auto 'member' of f
      i; // bad: g is not a non-static member of X
    }
  }
};

Such a member has the same access priviledges as its
containing member.

(10) The reason for restricting variable initialisations between declarations
and definitions is to prevent access to uninitialised variables,
similar to the rule about jumping past an initialisation:

void f() {
  auto void after();
  after();
  int x=1;
  void after(){ x;}
}

(11) The reason for specifying that nested functions have
internal linkage is obvious. It is allowed to write:

int f(){
  extern int g();
};

but g is not a nested function.

(12) The following is legal:

  static f() {
  static  g() {
  static   h() {
  static    g() {
             f::g();
             ::f::g();
         }}}}

Of course, it is not legal to subsequently write

  f::g();

The consideration of a function name as a namespace name
also suggests that it is legal to write

void f() {
  int i;
  void g() {
    int i;
    void h() {
      using f::i;
}}}

As for classes,

void f() {
  int i;
  void g() {
    int i;
    void h() {
      using namespace f;
}}}

is not allowed: it would achieve nothing since the namespace
would be already accessible at the same level before and
after the directive.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA