-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unused argument elimination discards required field #183
Comments
Hi Landon, Unfortunately I don't think you can achieve this as-is. Let me try to explain a little bit. After processing all of the inline_for_extraction's (let's ignore refinements which are irrelevant and get erased anyhow), you're getting this: type slice t a = {
len: u32;
bytes: (match t with CONST -> const_buffer a);
}
let example (s: slice CONST u8) = s.bytes This normalization is roughly the first step that happens in the extraction pipeline. The next step is erasing types that would prevent the program from type-checking with, roughly, ML-like polymorphism (this is a Letouzey-style extraction). This means in particular that the Then, KreMLin thinks that this field is a unit and eliminates it, causing problems further down the pipeline. I don't know much about your use-case, so it's hard to speculate, but let me try to offer a few suggestions. General solutionThere is a workaround that I established recently and documented here: https://github.com/project-everest/hacl-star/blob/master/code/streaming/Hacl.Streaming.Functor.fst#L29 -- well, sort of documented. (I had the same problem as you just a few months ago.) Let me explain: you can introduce a spurious type parameter whose sole purpose is to superficially make your data type definition look like it's parameterized over a type. Here's your example, fixed, along with a demo of how to write buftype-polymorphic functions. module Test
module CB = LowStar.ConstBuffer
module U8 = FStar.UInt8
module U32 = FStar.UInt32
inline_for_extraction
type buftype =
| CONST
inline_for_extraction
let buffer_t (ty:buftype) (a:Type0) =
match ty with
| CONST -> CB.const_buffer a
inline_for_extraction
let length (#t:buftype) (#a:Type0) (b:buffer_t t a): GTot nat =
match t with
| CONST -> CB.length (b <: buffer_t CONST a)
// This extracts in ML as:
// type 'b slice_t = { len: U32.t; bytes: 'b }
// which then triggers whole-program monomorphization over type parameter 'b.
inline_for_extraction
noeq type slice_t (t:Ghost.erased buftype) (a:Type0) (b: Type0 { b == buffer_t t a }) = {
len: U32.t;
bytes: (buf:b { length (buf <: buffer_t t a) == U32.v len });
}
// An abbreviation for convenience.
inline_for_extraction noextract
let slice_t' (t: Ghost.erased buftype) (a: Type0) =
slice_t t a (buffer_t t a)
// No particular precautions are required to use slice_t' provided the t is a
// constant value that can trigger reduction (you get a monomorphic function).
let example (s:slice_t' CONST U8.t) = s.bytes
// If you want to write a slice-polymorphic function, you need to enforce
// ML-style prenex polymorphism via a type parameter that appears *first*.
let length' (#t: Ghost.erased buftype) (#a:Type0) (#b: Type0 { b == buffer_t t a }) (s: slice_t t a b) =
s.len
module B = LowStar.Buffer
let test (): FStar.HyperStack.ST.St UInt32.t =
let b = B.malloc FStar.HyperStack.root 0uy 1ul in
let b = CB.of_buffer b in
let s: slice_t' CONST UInt8.t = { bytes = b; len = 1ul } in
length' s Note that you will get a monomorphization of the type // Specialized definition of slice, for b == CB.const_buffer UInt8.t, obtained via KreMLin whole-program monomorphization
typedef struct Test_slice_t__const_uint8_t__s
{
uint32_t len;
const uint8_t *bytes;
}
Test_slice_t__const_uint8_t_;
// Specialized definition of example, obtained via F* reduction since the arguments to the type are constant
const uint8_t *Test_example(Test_slice_t__const_uint8_t_ s);
// No polymorphism in C, so the polymorphic function length is not compiled, but for each use-site, a monomorphic instance is generated, in this case, from the use of `length'` in test (). Also note how the judicious use of `erased` in the F* code makes sure the signature of Test_length' is clean and doesn't mention any buftype parameter. (It would without the erased, which would be a shame.)
uint32_t Test_length_(uint32_t s); Ad-hoc solutionYou can probably replace the record type with a pair to achieve the same effect. If this helps, don't hesitate to continue the discussion on zulip (https://fstar.zulipchat.com/#narrow/stream/184683-questions-and.20help) so that others can benefit from it, or feel free to submit a PR to the Low* tutorial to include this trick in the tips & tricks section. Cheers, Jonathan |
The following fails with an assert in
krml
(see below)Fails with:
The assert is in
remove_unit_fields
:Not a Contribution.
The text was updated successfully, but these errors were encountered: