This site contains older material on Eiffel. For the main Eiffel page, see http://www.eiffel.com.

NICE Language Committee: ms-creation

From Michael Schweitzer
Date: Sat, 16 Apr 94 15:11:55 +0200
To: bertrand@eiffel.com
Subject: MS-creation
Cc: tynor@atlanta.twr.com
Content-Length: 11545
Status: RO
X-Lines: 334

-----------------------------------------------------------
Key   : MS-creation
Title : How to determine the exact creation type
Message from: Michael Schweitzer (April 1994)
-----------------------------------------------------------

This is probably the most complicated question that I've
posed so far. A few weeks ago I already had a discussion
about this with Bertrand. In this special case I would
like to present my understanding of the semantics in an
(almost) algorithmic fashion, that is, as a step by step
procedure. You may find that the examples are really
weird - but either we make some of them impossible or
we have to deal with them - there is no middle ground.
-----------------------------------------------------------

The general problem:

In a creation instruction of the form

        !!e     -- (1)

or
        !T!e    -- (2)

how can we determine the exact type which must be used
as creation type? Note that I'm omitting any creation
procedures, since they don't alter the problem.

Let me start with a concrete example which shows that
this is not a trivial question and that a detailed
analysis is absolutely necessary. For the moment, I'll
discuss only the case of creating attributes without
an explicitly given creation type.

Example 1:

Suppose that in a class C we find the following declarations:

    attr : like f

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

    f (arg : ANY) : ARRAY [like arg] is

        do
            !!attr  -- (1)
        end
-----------------------------------------------------------

    g is

        do
            !!attr  -- (2)
        end
-----------------------------------------------------------

For simplicity, let's assume that this class has no descendants
in the system so that redefinition must not be taken into account.

Question: What is the exact type of 'attr' after execution
          of instruction (1) and after execution of (2)?

Let's start with (2):

The type of 'attr' is 'like f', so we may start by replacing
'like f' by the declared type of 'f'. This gives

        attr : ARRAY [like arg]

But wait! In the context of routine 'g', the identifier 'arg'
is unknown. Therefore we _must_ take the following steps:

    A) Make the signature of 'f' free of "local" anchors, that is,
       anchors to formal arguments. This yields

            f (arg : ANY) : ARRAY [ANY]

    B) Replace the "global" anchor 'f' in the type of 'attr' by
       it's type computed in step A. This yields

            attr : ARRAY [ANY]

Therefore the answer for case (2) is : ARRAY [ANY].

However, in case (1) there are several possibilities. Obviously,
we can proceed as in case (2). However, if we first replace
the anchor 'like f' by it's declared type (which yields
attr : ARRAY [like arg]) there is now an interpretation for
'arg', since we're in the body of 'f'. We could therefore
use the actual dynamic type of 'arg' or the declared type of 'arg'
at the call site:

        actual : A
        b      : B

        actual := b   -- Assuming B conforms to A.

        f (actual)

So here the type of 'attr' after execution of (1) could be

        ARRAY [A]   -- If we use the declared type of 'actual'.
        ARRAY [B]   -- If we use the dynamic type of 'actual'.

Personally I believe that the only policy which keeps things
reasonably simple is to apply steps A) and B) in _both_ cases.
That is, the answer to my question should be "ARRAY [ANY]"
in both cases.

Rationale:

If the type of one component of the signature of a feature is
indirectly anchored to a formal argument of another feature
then the resolution of anchors should be static. One could
of course also prohibit this. Just to illustrate it, let me
give another extreme example (don't try this at home!) :

        f (f_arg : ARRAY [like g]) : TABLE [like f_arg]

        g (g_arg : ANY) : LIST [like g_arg]

        attr : like f

Here, 'f_arg' is indirectly anchored to 'g_arg' and 'attr' is
indirectly anchored to both 'f_arg' and 'g_arg'. It's weird,
but it is allowed.

To demonstrate the process, which will be defined in a more
formal way below, let's carry out steps A) and B):

    A) Replace local anchors:

       f (f_arg : ARRAY [like g]) : TABLE [ARRAY [like g]]
       g (g_arg : ANY) : LIST [ANY]
       attr : like f

    B) Replace global anchors:

       f (f_arg : ARRAY [LIST [ANY]]) : TABLE [ARRAY [LIST [ANY]]]
       g (g_arg : ANY) : LIST [ANY]
       attr : TABLE [ARRAY [LIST [ANY]]]

Therefore we find that both instructions ((1) and (2)) will create
a 'TABLE [ARRAY [LIST [ANY]]]'.

Another justification for applying A) and B) in both cases is that
if you find the same creation instruction for an attribute at two
different places in a class, you expect that the type created will
be the same in both cases (assuming the same dynamic type of Current).
-----------------------------------------------------------
-- Definition 1 (anchor_free version of a type in a class)
-----------------------------------------------------------

Let C be a class, CT it's type with all formal generic
constraints (if any) removed and with the keyword 'expanded'
removed (if C was declared as expanded).

First of all, perform the following steps (NOTE: 'routine'
and 'feature' refer to _all_ routines and features, intro-
duced in C or inherited):

A) For every routine 'r' of 'C' and every type 'S' occuring
   in the signature of 'r' do:

   If 'S' contains an anchored type of the form 'like anchor' then
      if 'anchor' is the name of a formal argument of 'r' then
            replace 'like anchor' by the declared type of 'anchor'.
      elseif 'anchor' is 'Current' then
            replace 'like anchor' by CT.
      end
   end

Repeat A) until no type 'S' occuring in the signature of any routine
'r' contains anchors to formal arguments or 'Current'.

At this point, all local anchors and anchors to 'Current' have
been replaced. The remaining anchors are anchors to features
of the class.

B) For every feature 'f' of 'C' and every type 'S' occuring
   in the signature of 'f' do:

   If 'S' contains an anchored type of the form 'like anchor' then
        replace 'like anchor' by the type of the feature 'anchor'
   end

Repeat B) until no type 'S' occuring in the signature of any feature
'f' contains any anchors.

After this process, we've computed a signature for every feature 'f'
which does not involve any anchors.

Now, if 'T' is a type occuring in the original signature of a feature
'f' of class C, then the anchor_free version of 'T' in 'C' is the
corresponding type in the anchor_free signature resulting from
applying the above steps.
-----------------------------------------------------------

We can now speak of the "anchor_free type of feature 'f' in class 'C'".

Comment:

As you may have noted, we need a language rule which disallows
any cycles in the declaration of anchored types. This is currently only
partially covered (i.e. x : ARRAY [like y]; y : ARRAY [like x]
must be excluded by a validity rule).
-----------------------------------------------------------
-- Creation types, Case 1
-----------------------------------------------------------

We're now in the position to define the semantics of attribute
creation in case that the creation type is not given explicitly:

In an instruction of the form

            !!attr

where 'attr' is an attribute of the enclosing class, the
creation type is determined as follows:

Let 'D' be the base class of the dynamic type of 'Current',
'b' the version of 'attr' in 'D'. Let 'T' be the anchor_free
type of 'b' in 'D'. Replace every formal generic appearing
in 'T' by the corresponding actual generic of 'Current'. The
resulting type is the creation type.
-----------------------------------------------------------
For the remaining cases it is convenient to introduce the
notion of 'call signature'.
-----------------------------------------------------------
-- Definition 2: Call signature
-----------------------------------------------------------

In the body of a routine 'r' the call signature of 'r' consists
of
    a) The dynamic type of the entity 'Current'.
    b) A type for every formal argument of 'r' (if any).

One may denote this by

    >
-----------------------------------------------------------
Note that I have deliberatly left open the question _how_
one obtains the call signature. For the moment it is sufficient
that we assume the existence of a call signature in the body
of a routine.
-----------------------------------------------------------
-- Creation types, Case 2
-----------------------------------------------------------

Assume that, in the body of a routine 'r', we have an
instruction of the form

        !!e     -- (1)

or
        !T!e    -- (2)

where, in case (1), 'e' is a local entity and in case (2),
'e' is a local entity or an attribute of the enclosing
class. Then the creation type is determined as follows:

In case (1), let 'S' be the declared type of 'e', in case
(2), let 'S' be 'T'.

Step 1: If 'S' contains an anchored type 'like anchor' then
            if 'anchor' is the formal argument name Arg_i then
                replace 'like anchor' by the type of Arg_i in
                the call signature of 'r'.
            elseif 'anchor' is the name of a feature of the class then
                replace 'like anchor' by the anchor_free type
                of the version of 'anchor' in the base class of
                Current's dynamic type.
            else -- 'anchor' must be 'Current'
                replace 'like anchor' by the dynamic type of Current.
            end
        end

Repeat Step 1 until 'S' contains no more anchored types.

Step 2: Replace any formal generic parameter in 'S' by the
        corresponding actual generic type in the dynamic type
        of 'Current'.

Now 'S' does not contain any anchors or formal generics. This
type is used as the creation type for (1) and (2).
-----------------------------------------------------------
Note that Steps 1 and 2 are also necessary in case (2), since
the explicit creation type may involve anchors as in

            !LIST [like arg]!e
-----------------------------------------------------------

The only remaining problem is: How can we determine the call
signature? There are three possible answers:

1) Use the declared types of the _formal_ arguments as call
   types.

2) Use the declared types of the _actual_ arguments at the
   call site as call types.

3) Use the dynamic types of the _actual_ arguments as call
   types.

Discussion:

1) is the simplest solution but it would require that
   anchoring to formal arguments would not be allowed
   any more (otherwise one could not guarantee that
   the return value of a function has a type which
   conforms to the type of some actual argument).

2) is the most difficult solution from an implementer's
   point of view, since in this case every call must
   somehow pass the declared types of the actual arguments
   too.

3) is certainly easy to implement, since every object carries
   information about it's dynamic type anyway. This does not
   require much extra effort.
-----------------------------------------------------------

Comment:

Our new implementation uses the procedures outlined above to
determine the creation type. We use version 3) of the call
signature. The compiler detects cycles in the declaration
of anchored types and flags them as errors.
-----------------------------------------------------------

Best regards,
    Michael