Skip to content

Commit

Permalink
fix(core): replace ~const Drop with ~const Destruct
Browse files Browse the repository at this point in the history
This change was driven by [rust-lang/rust#94901][1]. It's actually more
than just renaming - it fixes the irregular meaning of `~const Drop`
(“the type can be destructed in a const context”, instead of the regular
meaning of “`<T as Drop>::drop is `const fn`”), which the compiler was
unable to reconcile in `Drop` bounds and thus necessitated the work-
around explained in `[ref:drop_const_bounds]`. This is no longer an
issue because `~const Destruct` has a regular meaning.

[1]: rust-lang/rust#94901
  • Loading branch information
yvt committed Mar 29, 2022
1 parent 597282b commit ff7e987
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 117 deletions.
74 changes: 0 additions & 74 deletions doc/toolchain_limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,80 +224,6 @@ impl<C> Cfg<C> {
```


### `[tag:drop_const_bounds]` It's non-trivial for a `Drop` implementation to depend on `TypeParam: ~const Drop`

The following code doesn't compile (which is okay) because `T` might not be `T: const Drop`.

```rust,compile_fail,E0493
#![feature(const_trait_impl)]
#![feature(const_mut_refs)]
#![feature(const_option)]
struct Type<T>(Option<T>);
impl<T> const Drop for Type<T> {
fn drop(&mut self) {
// error[E0493]: destructors cannot be evaluated at compile-time
let _ = self.0.take().unwrap();
}
}
```

The obvious solution is to add `T: ~const Drop` to the `Drop` implementation as well as to the type definition. However, this doesn't work because `~const` is not allowed to appear in the type definition.

```rust,compile_fail,E0367
#![feature(const_trait_impl)]
#![feature(const_mut_refs)]
#![feature(const_option)]
// error: `~const` is not allowed here
struct Type<T: ~const Drop>(Option<T>);
// error[E0367]: `Drop` impl requires `T: ~const Drop` but the struct it is
// implemented for does not
impl<T: ~const Drop> const Drop for Type<T> {
fn drop(&mut self) {
let _ = self.0.take().unwrap();
}
}
```

According to [rust-lang/rust#93028](https://github.com/rust-lang/rust/pull/93028), we can actually remove `~const` from this type definition, and the compiler permits the `Drop` implementation to have an extra `~const`. Unfortunately, this leaves a `Drop` trait bound on the type, which actually cover different types than `~const Drop` does. That's because `T: ~const Drop` means that `T` can be dropped in a constant context (n.b. this is [a special case for `Drop`](https://internals.rust-lang.org/t/pre-rfc-revamped-const-trait-impl-aka-rfc-2632/15192#const-drop-in-generic-code-6) and doesn't apply to other traits), while `T: Drop` means that `T` has a user-defined `Drop` implementation.

```rust,compile_fail,E0277
#![feature(const_trait_impl)]
#![feature(const_mut_refs)]
#![feature(const_option)]
struct Type<T: Drop>(Option<T>);
impl<T: ~const Drop> const Drop for Type<T> {
fn drop(&mut self) {
let _ = self.0.take().unwrap();
}
}
// error[E0277]: the trait bound `(): Drop` is not satisfied
let _ = Type(Some(()));
```

A work-around is to enclose `T` in a container that unconditionally implements `const Drop`.

```rust
#![feature(const_trait_impl)]
#![feature(const_mut_refs)]
#![feature(const_option)]
struct UserDrop<T>(T);
impl<T> const Drop for UserDrop<T> {
fn drop(&mut self) {}
}
struct Type<T>(Option<T>) where UserDrop<T>: Drop;
impl<T> const Drop for Type<T>
where
UserDrop<T>: ~const Drop,
{
fn drop(&mut self) {
let _ = UserDrop(self.0.take().unwrap());
}
}
let _ = Type(Some(()));
const _: () = { let _ = Type(Some(())); };
```


### `[tag:const_closures]` Closures can't be `impl const Fn`

```rust
Expand Down
13 changes: 8 additions & 5 deletions src/r3_core/src/bind/sorter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@
//!
// FIXME: We might be able to improve the interface when `ComptimeVec` is not
// restricted to "comptime" anymore
use core::ops::{Deref, DerefMut, Index, IndexMut};
use core::{
marker::Destruct,
ops::{Deref, DerefMut, Index, IndexMut},
};

use super::{BindBorrowType, BindUsage};
use crate::utils::{
Expand Down Expand Up @@ -538,12 +541,12 @@ trait MyIterator {
}

trait GraphAccess<VertexRef> {
type VertexIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Drop + 'a
type VertexIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Destruct + 'a
where
Self: 'a;
fn vertices(&self) -> Self::VertexIter<'_>;

type SuccessorIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Drop + 'a
type SuccessorIter<'a>: ~const MyIterator<Item = VertexRef> + ~const Destruct + 'a
where
Self: 'a;
fn successors(&self, v: &VertexRef) -> Self::SuccessorIter<'_>;
Expand Down Expand Up @@ -597,9 +600,9 @@ const fn topological_sort<
where
Graph: ~const GraphAccess<VertexRef>,
// [ref:const_trait_not_implied] necessitates `: ~const MyIterator`
Graph::VertexIter<'a>: ~const MyIterator + ~const Drop,
Graph::VertexIter<'a>: ~const MyIterator + ~const Destruct,
// [ref:const_trait_not_implied] necessitates `: ~const MyIterator`
Graph::SuccessorIter<'a>: ~const MyIterator + ~const Drop,
Graph::SuccessorIter<'a>: ~const MyIterator + ~const Destruct,
VertexRef: Copy,
VertexRefLessThan: ~const FnMut(&VertexRef, &VertexRef) -> bool,
// `~const Deref[Mut]` isn't implied because of
Expand Down
24 changes: 13 additions & 11 deletions src/r3_core/src/utils/binary_heap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//!
//! The implementation is mostly based on the Rust standard library's
//! `BinaryHeap`.
use core::marker::Destruct;

mod helpers;
#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -31,36 +33,36 @@ pub trait BinaryHeap: VecLike {
/// Remove the least item from the heap and return it.
fn heap_pop<Ctx>(&mut self, ctx: Ctx) -> Option<Self::Element>
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop;
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct;

/// Remove the item at the specified position and return it.
fn heap_remove<Ctx>(&mut self, i: usize, ctx: Ctx) -> Option<Self::Element>
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop;
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct;

/// Push an item onto the heap and return its position.
fn heap_push<Ctx>(&mut self, item: Self::Element, ctx: Ctx) -> usize
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop;
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct;
}

impl<T> const BinaryHeap for T
where
// `~const Deref` isn't implied because of
// [ref:veclike_const_supertrait]
T: ~const VecLike + ~const core::ops::Deref + ~const core::ops::DerefMut,
T::Element: ~const Drop,
T::Element: ~const Destruct,
{
fn heap_pop<Ctx>(&mut self, ctx: Ctx) -> Option<Self::Element>
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop,
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct,
{
self.heap_remove(0, ctx)
}

fn heap_remove<Ctx>(&mut self, i: usize, mut ctx: Ctx) -> Option<Self::Element>
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop,
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct,
{
if i >= self.len() {
return None;
Expand Down Expand Up @@ -94,7 +96,7 @@ where

fn heap_push<Ctx>(&mut self, item: Self::Element, ctx: Ctx) -> usize
where
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Drop,
Ctx: ~const BinaryHeapCtx<Self::Element> + ~const Destruct,
{
let i = self.len();
self.push(item);
Expand Down Expand Up @@ -127,8 +129,8 @@ const unsafe fn sift_up<Element, Ctx>(
mut ctx: Ctx,
) -> usize
where
Ctx: ~const BinaryHeapCtx<Element> + ~const Drop,
Element: ~const Drop,
Ctx: ~const BinaryHeapCtx<Element> + ~const Destruct,
Element: ~const Destruct,
{
unsafe {
// Take out the value at `pos` and create a hole.
Expand Down Expand Up @@ -163,8 +165,8 @@ where
/// `pos` must point to an element within `this`.
const unsafe fn sift_down<Element, Ctx>(this: &mut [Element], pos: usize, mut ctx: Ctx)
where
Ctx: ~const BinaryHeapCtx<Element> + ~const Drop,
Element: ~const Drop,
Ctx: ~const BinaryHeapCtx<Element> + ~const Destruct,
Element: ~const Destruct,
{
let end = this.len();
unsafe {
Expand Down
38 changes: 11 additions & 27 deletions src/r3_core/src/utils/vec.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
use core::{alloc::Layout, ops, ptr::NonNull};
use core::{alloc::Layout, marker::Destruct, ops, ptr::NonNull};

use super::{AllocError, Allocator, ConstAllocator};

#[doc(hidden)]
pub struct UserDrop<T>(T);

impl<T> const Drop for UserDrop<T> {
#[inline]
fn drop(&mut self) {}
}

/// `Vec` that can only be used in a constant context.
#[doc(hidden)]
#[allow(drop_bounds)] // Due to the work-around for [ref:drop_const_bounds]
pub struct ComptimeVec<T>
where
// Work-around for [ref:drop_const_bounds]
UserDrop<T>: Drop,
{
pub struct ComptimeVec<T: Destruct> {
ptr: NonNull<T>,
len: usize,
capacity: usize,
allocator: ConstAllocator,
}

impl<T: ~const Clone + ~const Drop> const Clone for ComptimeVec<T> {
impl<T: ~const Clone + ~const Destruct> const Clone for ComptimeVec<T> {
fn clone(&self) -> Self {
// FIXME: Work-around for a mysterious error saying "the trait bound
// `for<'r> fn(&'r T) -> T {<T as Clone>::clone}: ~const FnMut<(&T,)>`
Expand All @@ -37,16 +24,10 @@ impl<T: ~const Clone + ~const Drop> const Clone for ComptimeVec<T> {
}
}

impl<T> const Drop for ComptimeVec<T>
where
// Work-around for [ref:drop_const_bounds]
UserDrop<T>: ~const Drop,
{
impl<T: ~const Destruct> const Drop for ComptimeVec<T> {
fn drop(&mut self) {
if core::mem::needs_drop::<T>() {
// Wrap `T` with `UserDrop` before dropping to work around
// [ref:drop_const_bounds]
while self.pop().map(UserDrop).is_some() {}
while self.pop().is_some() {}
}

// Safety: The referent is a valid heap allocation from `self.allocator`,
Expand Down Expand Up @@ -133,7 +114,10 @@ impl<T> ComptimeVec<T> {

/// Return a `ComptimeVec` of the same `len` as `self` with function `f`
/// applied to each element in order.
pub const fn map<F: ~const FnMut(&T) -> U + ~const Drop, U>(&self, mut f: F) -> ComptimeVec<U> {
pub const fn map<F: ~const FnMut(&T) -> U + ~const Destruct, U>(
&self,
mut f: F,
) -> ComptimeVec<U> {
let mut out = ComptimeVec::with_capacity_in(self.len, self.allocator.clone());
let mut i = 0;
while i < self.len() {
Expand All @@ -146,7 +130,7 @@ impl<T> ComptimeVec<T> {
/// Remove all elements.
pub const fn clear(&mut self)
where
T: ~const Drop,
T: ~const Destruct,
{
if core::mem::needs_drop::<T>() {
while self.pop().is_some() {}
Expand Down Expand Up @@ -217,7 +201,7 @@ macro_rules! vec_position {
}

/// Unwrap `Result<T, AllocError>`.
const fn unwrap_alloc_error<T: ~const Drop>(x: Result<T, AllocError>) -> T {
const fn unwrap_alloc_error<T: ~const Destruct>(x: Result<T, AllocError>) -> T {
match x {
Ok(x) => x,
Err(AllocError) => panic!("compile-time heap allocation failed"),
Expand Down

0 comments on commit ff7e987

Please sign in to comment.