const debacle

Janice Caron caron800 at googlemail.com
Wed Mar 26 07:56:26 PDT 2008


I think I've got it.

Imagine, if you will, a third flavor of constancy, called inout(T),
with the following rules:

(1) T will NOT implicitly cast to inout(T)
(2) const(T) will NOT implicitly cast to inout(T)
(3) invariant(T) will NOT implicitly cast to inout(T)

and
(4) inout(T) will implicitly cast to const(T)

When compiling a function body, the compiler will act as though
inout(T) means "I promise that I will not modify T, but someone else
might". (i.e. the same as const(T)).

When /calling/ a function which contains at least one inout(T) in its
signature, then the call shall be compiled /as if/ there had been
three separate declarations - one for mutable, one for const, and one
for invariant.This is probably better explained with an example - so
let's start with strstr. Here's the function definition:

    inout(char)[] strstr(inout(char)[] s, const(char)[] pattern)
    {
        int n = s.find(pattern);
        return n == -1 ? null : s[n..$];
    }

The compiler generates exactly one function from this body - this is
not a template. It compiles, because rules (1) to (4) above are
adhered to, and because, within the function body, the inout()
contract is adhered to.

Now let's see how things look from the call site:

    string s;
    string pattern;
    auto r = strstr(s,pattern);

In order to resolve this, the compiler looks at the function
signature, and sees:

    inout(char)[] strstr(inout(char)[] s, const(char)[] pattern)

So (and here's the clever part) it substitutes "inout" with all three
possibilities, and then tries to find the best match from among the
three. In other words, it acts /as if/ there had been three separate
function declarations:

    char[] strstr(char[] s, const(char)[] pattern);
    const(char)[] strstr(const(char)[] s, const(char)[] pattern);
    invariant(char)[] strstr(invariant(char)[] s, const(char)[] pattern);

It tries to find the best match from among those three declarations,
using the normal matching rules. It finds that the best match is:

    invariant(char)[] strstr(invariant(char)[] s, const(char)[] pattern);

and so that's the one it calls. (Even though there's only one
function, the caller behaves as though there were three). And since r
was declared with auto, r gets a type of invariant(char)[].

Let's try another example:

    inout(T) min(T)(inout(T) x, inout(T) y)
    {
    	return x < y ? x : y;
    }

Again, we use rules (1) to (4) to compile this into a function, this
time templatised on T. Now let's have a more interesting call:

    // C is a class
    C x;
    const(C) y;
    auto z = min(x,y);

What type is z?

The procedure outlined above makes this very easy to figure out.
Again, the compiler acts /as if/ it has seen three separate
definitions, these being:

    T min(T)(T x, T y);
    const(T) min(T)(const(T) x, const(T) y);
    invariant(T) min(T)(invariant(T) x, invariant(T) y);

It finds the best match, using the normal matching rules. It finds
that the best match is with:

    const(T) min(T)(const(T) x, const(T) y);

and so that's the one it calls. The template paramter T matches the
class C, and so the return type must be const(C). Therefore z has a
type of const(C).

So far as I can, this solves all problems, and just works.

It does, however, require one additional caveat. For member functions,
it must be possible to declare

    class C
    {
        inout f(...);
    }

which, again, generates exactly one function body. By similar rules,
at the point of the function call, the compiler must act as though it
had seen three separate declarations:

    class C
    {
        f(...);
        const f(...);
        invariant f(...);
    }

and match the right one, using the normal rules.

I honestly think this might be problem solved. But please, please,
please chime in if I've got anything wrong.



More information about the Digitalmars-d mailing list