-
Notifications
You must be signed in to change notification settings - Fork 235
[Work in Progress] Enriching Steel's memory model
We would like to add support in Steel for:
-
Pointers, including
- pointers to struct fields 1 2 3 4
- pointers to subarrays 1
- fractional permissions
- custom PCMs (e.g. resource handles, channels)
- pointer arithmetic on arrays (lower priority, if at all)
- hard to correctly and usefully model permissions on subtracting a pointer
and excluding:
- reading one field of a struct through a pointer to another field
- pointer arithmetic on non-arrays in general
- pointers to continguous substructs of a struct
-
Unions, without the ability to write in one field and read out of another
- if you have a union of two types S and T holding a value of type S,
can switch to T by writing to one of T's fields; in that case,
cannot read any of T's other fields
- in this situation, should it be possible to take write-only pointers to uninitialized fields of T?
- if you have a union of two types S and T holding a value of type S,
can switch to T by writing to one of T's fields; in that case,
cannot read any of T's other fields
A summary of this morning's discussion.
To design a Steel memory model compatible with C extraction, we are trying to strike a balance between:
- Expressiveness: The memory model should not restrict the expressiveness of the Steel separation logic. For instance, considering arrays, a user should be able to define/use arrays with custom PCMs, monotonicity, frozen prefixes, ...
- Genericity: We do not want to multiply libraries and duplicate code. For instance, considering pointers, we should have a unique user-facing abstraction that encapsulates both standard C pointers and field pointers.
- Simplicity of the extraction: The memory model must be compatible with KreMLin's pattern-matching based extraction. To get there, a metaprogramming preprocessing phase (ideally, through normalization) is possible. Furthermore, the memory model must remain close enough to C to make a meta-argument about the soundness of the extraction.
- Proposed plan of action:
For now, we want to focus on expressiveness, and gaining experience modeling different C idioms in Steel. In particular, try to model different versions of arrays, structs, and unions. We restrict ourselves to structured programming, in particular, preventing pointer arithmetic on struct fields/unions. While experimenting, John will draft a wiki page with the pros and cons of different approaches. Once we have models we are happy with, we will think about genericity, possibly building upon Denis' preliminary work using a pointer typeclass.
In addition to standard C idioms, it might also be useful to generalize examples/steel/Selectors.PtrLList to lists of arbitrary vprops. It could help with understanding how to do arrays of structs, structs of arrays, ...
- Additional comments on the current proposal for Steel.Array:
Tahina's proposed design relies on a sequence of references. This gives us a basis for C-like arrays, but could lead to several issues. First, it is unclear how this model would be compatible with user-defined PCMs on the full array (needed for monotonic/frozen-prefix arrays for instance). Second, this model is a bit further from the C standard, which leads to a more hand-wavy extraction soundness argument. An alternative previously proposed by Jonathan and Denis (in the context of structs, but it should be applicable to arrays) was to instead use a reference to a sequence, and to rely on fictional separation logic to model subarray permissions.
In addition to the aforementioned issues, specifying adjacency in the current library is problematic. Low* allowed to take a sub-buffer of a live buffer. In Steel's separation logic, doing this soundly relies on a split into two different arrays. To later merge subarrays into the original array, we need to keep track of the compatibility/adjacency of the subarrays. Currently, our model either allows to:
- Have split return two new arrays a1, a2 and a pure predicate relating them to the original array (Steel.Array library). This is not C-idiomatic, as a1 corresponds to the same pointer as the original array; we'd only like to return the new array a2 by performing pointer arithmetic.
- Return only the pointer to the subarray, with a stateful predicate about adjacency operating on the array selectors (Steel.ArrayPtr library). Relying on a stateful predicate seems overkill, and would induce extra reasoning. Combining the best of both worlds currently is an open question.
So far we have the following approaches:
- Nik: define a PCM for pointers to different fields of a struct
- Denis: in addition to this PCM, define typeclasses of "pointer-like" objects to allow users to manipulate references without having to know what kinds of references they are
- Tahina: model a pointer-to-array or pointer-to-struct not as a reference to a sequence, but a sequence of references
- Structs: https://github.com/FStarLang/FStar/blob/steel/examples/steel/CQueue.Cell.fsti
- Arrays: https://github.com/FStarLang/FStar/pull/2309
- Modelling arrays as sequences of references has some potential issues, described by Aymeric's notes above
- Jonathan & Denis: model arrays as reference to sequence, using fictional separation logic for subarray permission
Questions:
- How invasive of a change is it to switch to fictional separation logic?
- What are the pros and cons of keeping track of adjacency with a extra pure predicate vs. representing array slices by their delimiting indices? Some discussion at https://github.com/FStarLang/FStar/pull/2309 - If using indices is not so bad for the user, then fictional separation logic may not be necessary
- Are there applications that require both a PCM on an entire array and the ability to split that array into subarrays?
- And, is one of the above approaches better than the others in this regard?
- The arraystruct examples are all for a struct with two fields {x, y}. If there are three fields {x, y, z}, is it possible split {x, y, z} into {x, _, z} and {_, y, _}? - Perhaps unnecessary and instead can just explose explode/recombine operations
To still allow them to write code generic over the various kinds of references they might come up with, we have to ensure that:
- the "pointer-like" typeclasses is general enough, and
- our model of structs, arrays, and unions should allow fields/elements with custom PCMs
Denis defines two such typeclasses in Steel.ArrayStruct:
rw_pointer
for read-write pointers and r_pointer
for read-only pointers. If we would like to support uninitialized fields
in unions, then it may be useful also to have a typeclass w_pointer
of write-only pointers.
Examples of custom PCMs inside arrays, structs, and unions, which we would like to support:
- arrays/structs containing channels (e.g. in state machines)
- monotonic arrays where PCM is over the whole array (nice-to-have)
- ...