Note: I fail to make a proper second link to playground, so copy paste the code here
#![feature(fnbox)]
#![feature(never_type)]
use std::ops::{Deref, DerefMut};
use std::boxed::FnBox;
use std::sync::Arc;
//
// Context
//
struct Context {}
//
// FamilyType (GAT workaround)
//
pub trait FamilyLt<'a> {
type Out;
}
struct RefMut<'a, T: 'a> {
value: &'a mut T
}
impl<'a, T: 'a> Deref for RefMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<'a, T: 'a> DerefMut for RefMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
struct RefMutFamily<T>(std::marker::PhantomData<T>, !);
impl<'a, T: 'a> FamilyLt<'a> for RefMutFamily<T> {
type Out = RefMut<'a, T>;
}
//
// Execute trait
//
pub trait Execute<Ctx, R, E> {
type Injected: TryInject<Ctx>;
fn execute(
self,
value: <<Self::Injected as TryInject<Ctx>>::Injected as FamilyLt>::Out,
) -> Result<R, E>;
}
//
// Injection trait
//
pub trait TryInject<C>
where
Self: Sized,
{
type Error;
type Injected: for<'a> FamilyLt<'a>;
fn try_inject(context: &C) -> Result<<Self::Injected as FamilyLt>::Out, Self::Error>;
}
impl<T: 'static> TryInject<Context> for RefMutFamily<T> {
type Error = ();
type Injected = Self;
fn try_inject(context: &Context) -> Result<<Self::Injected as FamilyLt>::Out, Self::Error> {
unimplemented!()
}
}
//
// Helper func
//
struct Annotate<Q>(std::marker::PhantomData<Q>);
impl<Q> Annotate<Q> {
fn new() -> Self {
Self(std::marker::PhantomData)
}
}
fn annotate<F, Q, R, E, Ctx>(_q: Annotate<Q>, func: F) -> impl Execute<Ctx, R, E>
where
F: for<'r> FnOnce(<<Q as TryInject<Ctx>>::Injected as FamilyLt<'r>>::Out) -> Result<R, E>,
Q: TryInject<Ctx>,
{
struct Wrapper<Q, F>(std::marker::PhantomData<Q>, F);
impl<Ctx, Q, R, E, F> Execute<Ctx, R, E> for Wrapper<Q, F>
where
Q: TryInject<Ctx>,
F: for<'r> FnOnce(<<Q as TryInject<Ctx>>::Injected as FamilyLt<'r>>::Out) -> Result<R, E>,
{
type Injected = Q;
fn execute(
self,
value: <<Self::Injected as TryInject<Ctx>>::Injected as FamilyLt>::Out,
) -> Result<R, E> {
(self.1)(value)
}
}
let wrapper: Wrapper<Q, F> = Wrapper(std::marker::PhantomData, func);
wrapper
}
struct schedule {
processor: Box<FnBox(Arc<Context>) -> Result<(), ()>>,
}
fn task<Ctx, R, E, P>(processor: P)
where P: Execute<Ctx, R, E> {
}
//
// Usage
//
fn main() {
task(annotate(
Annotate::<RefMutFamily<usize>>::new(),
|mut value: RefMut<usize>| -> Result<usize, ()> {
*value = 2;
Ok(1)
}
));
}
Update by pnkfelix: Here is a version of the code that does not use any features, so you can see the bug on stable/beta/nightly (play):
Click to expand the code, in case playpen link above is broken
Original report follows.
Hi, I ran into the ICE while compiling this code: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c4d5196a6bdaf9a0bd5d346ef2bb20e1
The basic idea with this code is:
FamilyLttrait (workaround for GAT, see 'Workaround B' http://lukaskalbertodt.github.io/2018/08/03/solving-the-generalized-streaming-iterator-problem-without-gats.html)Note:
The ICE appeared when I actually use the closure to store it in the
FnBox.See, this one without the implementation of
taskcompiles (source code below).This may be related to #29997, but I prefered to report this use case as well.
Meta
rustc --version --verbose:Backtrace:
Source Code without `task`
Note: I fail to make a proper second link to playground, so copy paste the code here