On Thu, 30 Apr 2009 07:04:59 -0400, Michel Fortin <[email protected]> wrote:
On 2009-04-29 22:54:20 -0400, "Robert Jacques" <[email protected]> said:

On Wed, 29 Apr 2009 21:25:32 -0400, Michel Fortin <[email protected]> wrote:

That's basically why I suggested adding scope constrains back then. To implement swap safely, you need to know that the scope of the pointer you are assigning to is always smaller or equal to the scope of the memory block you're feeding them with.
 Here's a new syntax for expressing contrains I've been thinking about:
        void swap(scope int* x, scope int* y)
scope(x = y && y = x) // caller enforces that y is assignable to x and x to y
        {
                scope(x = t && t = y) int* t;
                // y assignable to t and t to x; also imply that
                // x is assignable to y, which holds against previous constrains
                t = y;  // valid since scope(t = y)
                y = x;  // valid since scope(y = x)
                x = t;  // valid since scope(x = t)
        }
Perhaps with simple escape analysis, the compiler could infer the scope constrains of local variable t so you don't have to write it everywhere.
You know, the implementation of swap is really a bad example, since using a template works fine:
void swap(T)(ref T x, ref T y) {
     T t
     t = y;
     y = x;
     x = t;
}

You know, all functions could be made templates and it'd solve all our problems. Why aren't we doing that?

Seriously, templates can't be the answer to everything. What if you wanted swap as a member function of a class, and want to override it in a derived class? We need a system that works with non-templates too.

I agree, which is why using a function where people immediately think 'shouldn't that be a template' is a bad example.
By the way, using
scope(x = t && t = y) int* t;
implies to me that the function is templated.

Object a;
Object b;
shared Object c;
swap(a,b);   // Okay
swap(b,c); // Error, template instantiation swap(local object, shared object)

In the case of my function with constrains you'd get something alike:

        swap(b,c); // Error, 'swap' wants c (shared Object c)
        // to be assignable to b (local Object b) which isn't allowed.

Basically, you can't copy a scope variable to another scope variable inside a function (because they may not be of the same scope and/or ownership) unless the signature includes a constrain signaling the assignement, which is then evaluated at the call site.

Agreed

Here are some specific issues:
1) You seem to assume that different ownerships are interchangable. They are not. Even if the data layout and member signatures are the made to be the same, shared objects must maintain sequential consistency (i.e. memory fences). 1a) Limiting object signatures to being identical makes it hard for library writers to make a class that can be both allocated on both the shared and local heaps.

Where am I assuming that? How?

Sorry. I was making assumptions based on your previous proposal of scope constraints:

void swap(scope ref int* a, scope ref int* b)
    if ((*a).scope <= b.scope && (*b).scope <= a.scope)

The <= operator implies that it is possible that an object of one ownership might get assigned to an object of a different ownership.

I was also making the assumption that the scope function parameter implied templating on that ownership type, as implied by.
scope(x = t && t = y) int* t;

Speaking of limiting constraints to equality, how about:

void swap(scope[0] ref int* x, scope[0] ref int* y) {
    scope[0] t;
    t = x;
    x = y;
    y = t;
}

with scope meaning any owner and scope[id] meaning the same ownertype as all the other scope[id]'s. (id is a number or identifier)

Hmm... scope[>id] and scope[<id] also seem intuitive.

An advantage I see with this approach is that swap composes much more easily:

void bar(scope ref int* x, scope ref int* y)
scope(x = y && y = x) // Possible information loss, compiler knows satisfaction, not what generated satisfaction
{
swap(x,y); // Does the compiler know enough to allow this call?
}

void bar(scope[0] ref int* x, scope[0] ref int* y) {
    swap(x,y); // Yes, the compiler knows x and y have the same scope
}

Perhaps I'm not understanding something of your proposal, but I don't see how adding scope constrains breaks shared objects.

Adding any scope restraints other than equality, can be shared objects. See above.
Perhaps this will explain my thinking better:

shared class Foo {}

becomes

Interface __scope_Foo {}
class __local_foo:  __scope_Foo {}
class __shared_foo: __scope_Foo {}

under the hood. Even if scope is a super-type and not an interface, you can see how these two ownerships might produce incompatible classes. Of importance is that member variables are sequentially consistent on shared types vs non-shared types and that they may have different vtables, etc.

You say you want "scope" to be the super-type of all, which means that it should accept both local and shared variables, am I right? If that's the case I was right to write variable as being scope in my swap function, so it can accept everything.

No, I said that scope is the super-interface of all. Which would have made a difference if you were using objects instead of int*s.

2) You shouldn't rely on escape analysis to determine your function signature. It essentially forces you to do whole program static escape analysis, if you want to do it right, which is implausible. Consider recursive and member functions. What's the proper signature? And this isn't even considering the composability and forward referencing issues.

That I certainly agree with. (And I never suggested escape analysis to determine the scope of function arguments, only local variables inside the function.)

Opps, I misread that.


Reply via email to