Skip to content
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

Inline Storage - Size / Alignment compile-time assertions. #1

Open
liarokapisv opened this issue Feb 14, 2021 · 2 comments
Open

Inline Storage - Size / Alignment compile-time assertions. #1

liarokapisv opened this issue Feb 14, 2021 · 2 comments

Comments

@liarokapisv
Copy link

liarokapisv commented Feb 14, 2021

One worthy goal would be to be able to perform compile-time size/alignment assertions for inline storage without changing the final api, and retaining the storage-agnostic goal of the final container objects. Heap storage should not do any assertions.

My approach is to use generic associated types as assertion objects and have the final create function perform the check using the assertion object directly. This way each associated assertion is a policy for the final create function. For all storages the assertion is performed as a trait requirement on create, the trick is to make the assertion always pass for Heap storages but possibly-fail for Inline storages.

Here is a POC of this.

Unfortunately having the check being part of the signature means that all call sites will need to propagate the constraint even if we know that eg on HeapStorage it will always pass. The compiler first checks the trait bounds and refuses to evaluate the bound itself. This means that if we attempt to make an alias with HeapStorage the constraint will have to leak to user code as well.

Eg a user can't write an external create function:

fn create<T>(val : T) -> RawBox<T, HeapStorage> {
    RawBox::create(val)
}

even though the trait is always valid. Instead they have to write the following:

fn create<T>(val : T) -> RawBox<T, HeapStorage> where [u8 ; size_assert::<HeapStorage, S>()] : Sized {
    RawBox::create(val)
}

it would be nice if the compiler attempted to evaluate the trait first. The above should be able to reduce to:

[u8 ; {HeapStorage::Asserter<T>::VALUE as usize} - 1] : Sized =>
// At this point AlwaysTrueAsserter is parameterized but we can avoid it.
[u8 ; {AlwaysTrueAsserter::VALUE as usize} - 1] : Sized =>
[u8 ; {true as usize} - 1] : Sized =>
[u8 ; 0] : Sized

So the fact that I have to specify the bound seems a bit unneeded to me.

@matthieu-m
Copy link
Owner

Well done! GATs really are awesome.

I realized there's another fundamental difference between the inline storage and the heap storage. With the inline storage, it's known at compile-time whether the allocation will succeed, whether with the heap storage it's not known until one tries.

Hence, if we wanted to go all the way, we should also make it possible to specialize the error type in create. That is:

fn create<T: Pointee>(&mut self, value: T) -> Result<Self::Handle<T>, T>;

Should be:

fn create<T: Pointee>(&mut self, value: T) -> Result<Self::Handle<T>, Self::AllocError<T>>;

And the AllocError<T> should expand to either T if failure is possible or ! if it is not.


I must admit that I'm not ready to use the clunky [u8; X] : Sized work-around.

If at some point the compiler were to support value-based where clauses, such as where S::is_allocation_possible::<T>(), it would be one thing; but the goal here is to end up with a design fitting the std library, and I have some doubts that where [u8; X] : Sized will pass muster.

I do like exploring how far compile-time assertions can be pushed; I just want to be clear that I am not likely to act on any such exploration.

@liarokapisv
Copy link
Author

I understand this, I also wouldn't attempt to standarize this as is. Even if we had a better syntax for the assertion, I would expect the HeapStorage to completely remove the assertion for user code otherwise it would just be confusing. Currently trying to see if this is in the scope of the const_generics RFC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants