This appears to work:
//////////////
fun borrow[T] (p: &<(uniq T)):T => p*.unbox;
proc test() {
var s = ucstr "hello";
println$ s&.borrow;
s = set (s, 0, char "e");
println$ borrow (&s);
delete s;
}
test();
////////////
Note the two borrows are the same, &. is just a convenience infix operator.
The Felix uniqueness system ignores taking the address of a variable
of unique type. Of course this is unsafe, but tracking the pointer is hard.
The alternative would be to ban it.
So the function then cheats, by dereferencing the pointer, and casting
away the uniqueness. Note, it takes a read only pointer, not that this
matter here. It may be quite sane to allow writing to a borrowed pointer!
In fact, you can write to it anyhow.
So the advantage of the borrow function above is merely a convenience,
and, in particular, documenting that we’re borrowing.
Note, in Felix, no claim is made for guarranteed correctness of unique handling.
In fact, the methods working at the primitive level with a boxed value cannot
do anything without unboxing it. The idea is simply to hide these methods
in a class and abstract the type, so the abstraction prevents writing further
unsafe methods.
The ucstr type is not abstract. The _ucstr type is abstract, and ucstr
is just a boxed version of it:
// abstract representation
private type _ucstr = new +char;
// make it uniq
typedef ucstr = uniq _ucstr;
The user cannot get at the underlying char pointer. However the type _ucstr,
despite being private escapes. Private types cannot be named, but they
can escape and be used. Otherwise “borrow” wouldn’t work.
=====
NOW the idea is .. can we make a safe borrow? By safe I mean, the compiler
barfs if you borrow a dead value. This would be a special kind of pointer,
a borrow pointer. When a borrow address is taken, the uniqueness checker
now *does* take notice. Taking a borrow address, like an ordinary address,
does NOT “consume” the variable. But unlike an ordinary address, it DOES
check for liveness.
========
What about writing? Well, really we should have read only, write only, and read write
borrows.
Write only borrows are interesting. The condition seems to be that the variable
being address must be dead. Since we’re borrowing and can only write,
we must write, and then we can make the variable live again. This only
makes sense if the pointer is passed to a procedure.
Now, just consier, assignment to a dead variables livens it.
And assignment is a procedure so
a = b;
actually means (in theory):
storeat(&>a, b);
Although storeat is in the library the prinitive _storeat it maps to is a compiler
intrinsic. Now at present the uniquness checker recognises
* assignment
* the storeat operator, when the first argument is the address of a variable
and the LHS of the assignment variable of the variable in the special form
has a unique type. In that case, the variable must be dead to start and
ends up live afterwards.
If we had a write only borrow pointer type, it could process it similarly.
Now we can defer operating by saving the pointer and writing later BUT
the variable must be dead when the address is taken and becomes
live afterwards. The onus is on the programmer to use it correctly,
that is, to do the actual write because the variable is used.
just to be clear the difference is this: at present, if you do:
var x : uniq thing; // dead ..
var px : &x; // x is still dead
px <- something; // x is still dead
The system cannot track which variable px points at. So the address
taking is ignored. But compare:
var x : uniq thing; // dead
var px = writeborrow x; // x is now live
px <- something; // x is still live, system doesn’t know px points at x
=========
Read write borrows? Well one has to assume the read is first, then the wriet.
So the start condition is live, and the final condition is live too.
=========
Subtyping. A distint pointer type? Or can we just examine of the pointers
point at a uniq thing???
The latter has the same problem we have already with type variables
that LINEAR kind was introduced to fix. If the base type variable
has kind TYPE, it cannot be uniq, and so a pointer to uniq T,
where T is a type, ensure decoding. If T is LINEAR, we don’t know.
So in this case we have to ban operation. In other words you can
only borrow something explicitly uniq. [But you can still take
an ordinary address .. and pay later .. :]
—
John Skaller
ska...@internode.on.net