Hi,

I spent a portion of yesterday fighting the effect system in rustboot as it was ... "misclassifying" (to put it mildly) types by their effect and subsequently allocating and freeing them incorrectly. This is because at present the effect of a type determines its storage class (GC memory or non-GC) and things generally go sideways when you treat one class of memory as the other. I eventually gave up and disabled the GC entirely in the runtime (which is less serious than it sounds; it wasn't actually working anyways).

I would, however, like to solve this problem in a more systematic way, and I've been thinking in recent weeks how to address it. I have a two-part proposal I'd like to run by the audience here and see if anyone has opinions.

Part #1 of the proposal is to split the concept we're currently calling an effect in two pieces: the "effect" concept should have a concept I'm calling a "stratum"[1] split off and handled independently. The stratum of a type is the class of storage it belongs to: "immutable, shareable, pure" vs. "local, mutable, non-shareable". Where we currently say that "every type has an implied effect", the revised statement under this proposal would be "every function has an implied effect, every type has an implied stratum".

Effects would retain the qualifiers "io" and "unsafe". Users could continue to lie about the effect of a function using "auth" clauses in the crate file. Writing to a non-local mutable field would be considered an io action for the sake of effect calculation -- after all, mutating a PRNG is more or less observationally indistinguishable from "doing IO" -- but effects would strictly describe *code*, not data. If you make a function into a first class value, of course, that value will have a function type and that function type will have an effect. But the type as a whole won't have an effect; only a function has an effect, and the effect occurs when you *call* the function. You can pass the type around all you want without worrying about its effect. Effect checking would remain useful for helping users enforce purity and safety in their code, of course.

The "state" qualifier would be changed to denote the stratum of a type, and would no longer be subject to "auth" clauses; users would not be allowed to lie about the stratum of a type. This is important, because as it stands presently the ability to lie about state means that *everything* that touches a mis-classified stateful value is not only "likely unsafe" (in the "crash the system" sense) but also very difficult -- almost impossible -- to make safe: you would have to co-ordinate with every other reader and writer in manipulating refcounts and malloc/realloc/free actions, and ensure that everyone agreed on statefulness any time critical combinations of those actions occurred. This is not something likely achievable on a whole-program basis. IOW at the moment it's likely that if there's a single lie in your program about "state", the whole thing is liable to crash. We're not hitting this *much* at present only because we don't use state much and we've been lucky to be using it in not-as-crashy ways (mutable non-box slots and such). But in general this needs to stop happening; users have to be honest about which storage stratum a type belongs to.

That's part #1: effect => effect+stratum, and stratum is always enforced.

Part #2 of the proposal is a bit juicier: add a third stratum back in, "gc". We had this briefly a year or so ago, called "cyclic" back then, but I think "gc" is a pithier qualifier. We merged the concept into "state" when devising the current effect system, but I suspect that was a mistake.

Under proposal-part-#2, the state and gc strata would both still be task-local, but splitting state from gc would have three interesting implications:

  - State values could be frozen and thawed, reasonably, to and from
    stateless values. State means acyclic-and-mutable, after all, so
    in the singly-referenced case this would even wind up a no-op. This
    is the easy case of freeze/thaw, and it'd be formally denoted in
    the type system. This would provide a great utility for two idioms
    we currently have no good way to support: freeze-and-apply-predicate
    and freeze-and-transmit-over-channel.

  - State values could also have a our structural comparison definition
    extended to them. Again, acyclicality wins here.[2]

  - State objects could have destructors. Acyclicality, again. Yay!
    No more telling users to make artificial immutable sub-objects :)

So .. part #1 of the proposal seems necessary to get us back to "remotely safe", and part #2 has a lot of exciting prizes associated with it. Does anyone approve-of or object-to either part? I'm thinking of having a go at them shortly, as the current logic in rustboot around all this is quite a mess.

-Graydon

[1] Does anyone have a better term than "stratum" to describe the storage category of a type? I'm almost partial to "heap" or "pool", but that is both slightly misleading and also makes for sentences with strange grammar: "the heap of a type". Yuck.

[2] we could even extend comparison to gc types if we say "gc types are compared by address". It would be a little unpredictable though; I'm tempted to define a "std.util.addrcmp[gc T](&T a, &T b) -> order" that compares anything by address, along with a memcmp that compares by memory-content, and make the "built-in" operators <, =, etc. structural on acyclic types and errors when applied to a gc type. Safer, no?
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to