Closed
Description
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
// FamilyType (GAT workaround)
pub trait FamilyLt<'a> {
type Out;
}
struct RefMutFamily<T>(std::marker::PhantomData<T>, ());
impl<'a, T: 'a> FamilyLt<'a> for RefMutFamily<T> {
type Out = &'a mut T;
}
pub trait Execute {
type E: Inject;
fn execute(self, value: <<Self::E as Inject>::I as FamilyLt>::Out);
}
pub trait Inject
where
Self: Sized,
{
type I: for<'a> FamilyLt<'a>;
fn inject(_: &()) -> <Self::I as FamilyLt>::Out;
}
impl<T: 'static> Inject for RefMutFamily<T> {
type I = Self;
fn inject(_: &()) -> <Self::I as FamilyLt>::Out {
unimplemented!()
}
}
// This struct is only used to give a hint to the compiler about the type `Q`
struct Annotate<Q>(std::marker::PhantomData<Q>);
impl<Q> Annotate<Q> {
fn new() -> Self {
Self(std::marker::PhantomData)
}
}
// This function annotate a closure so it can have Higher-Rank Lifetime Bounds
//
// See 'annotate' workaround: https://github.com/rust-lang/rust/issues/58052
fn annotate<F, Q>(_q: Annotate<Q>, func: F) -> impl Execute + 'static
where
F: for<'r> FnOnce(<<Q as Inject>::I as FamilyLt<'r>>::Out) + 'static,
Q: Inject + 'static,
{
let wrapper: Wrapper<Q, F> = Wrapper(std::marker::PhantomData, func);
wrapper
}
struct Wrapper<Q, F>(std::marker::PhantomData<Q>, F);
impl<Q, F> Execute for Wrapper<Q, F>
where
Q: Inject,
F: for<'r> FnOnce(<<Q as Inject>::I as FamilyLt<'r>>::Out),
{
type E = Q;
fn execute(self, value: <<Self::E as Inject>::I as FamilyLt>::Out) {
(self.1)(value)
}
}
struct Task {
_processor: Box<dyn FnOnce()>,
}
// This function consume the closure
fn task<P>(processor: P) -> Task
where P: Execute + 'static {
Task {
_processor: Box::new(move || {
let q = P::E::inject(&());
processor.execute(q);
})
}
}
fn main() {
task(annotate(
Annotate::<RefMutFamily<usize>>::new(),
|value: &mut usize| {
*value = 2;
}
));
}
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:
- To store a closure that have Higher-Ranked Lifetimes in its arguments. (See workaround here Annotating higher-ranked lifetimes on closures is arduous #58052
- The arguments implements a
FamilyLt
trait (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 task
compiles (source code below).
This may be related to #29997, but I prefered to report this use case as well.
Meta
rustc --version --verbose
:
rustc 1.35.0-nightly (f22dca0a1 2019-03-05)
binary: rustc
commit-hash: f22dca0a1bef4141e75326caacc3cd59f3d5be8e
commit-date: 2019-03-05
host: x86_64-pc-windows-gnu
release: 1.35.0-nightly
LLVM version: 8.0
Backtrace:
error: internal compiler error: src/librustc/traits/codegen/mod.rs:58: Encountered error `OutputTypeParameterMismatch(Binder(<[closure@src/main.rs:147:9: 150:10] as std::ops::FnOnce<(<RefMutFamily<usize> as FamilyLt<'_>>::Out,)>>), Binder(<[closure@src/main.rs:147:9: 150:10] as std::ops::FnOnce<(RefMut<usize>,)>>), Sorts(ExpectedFound { expected: RefMut<usize>, found: <RefMutFamily<usize> as FamilyLt<'_>>::Out }))` selecting `Binder(<[closure@src/main.rs:147:9: 150:10] as std::ops::FnOnce<(RefMut<usize>,)>>)` during codegen
thread 'rustc' panicked at 'Box<Any>', src/librustc_errors/lib.rs:635:9
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
1: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:71
2: std::panicking::default_hook::{{closure}}
at src/libstd/sys_common/backtrace.rs:59
at src/libstd/panicking.rs:197
3: std::panicking::default_hook
at src/libstd/panicking.rs:211
4: rustc::util::common::panic_hook
5: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:478
6: std::panicking::begin_panic
7: rustc_errors::Handler::bug
8: rustc::util::bug::opt_span_bug_fmt::{{closure}}
9: rustc::ty::context::tls::with_opt::{{closure}}
10: rustc::ty::context::tls::with_context_opt
11: rustc::ty::context::tls::with_opt
12: rustc::util::bug::opt_span_bug_fmt
13: rustc::util::bug::bug_fmt
14: rustc::ty::context::GlobalCtxt::enter_local
15: rustc::traits::codegen::codegen_fulfill_obligation
16: rustc::ty::query::__query_compute::codegen_fulfill_obligation
17: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::codegen_fulfill_obligation>::compute
18: rustc::dep_graph::graph::DepGraph::with_task_impl
19: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
20: rustc::ty::instance::Instance::resolve
21: <rustc_mir::monomorphize::collector::MirNeighborCollector as rustc::mir::visit::Visitor>::visit_terminator_kind
22: rustc_mir::monomorphize::collector::collect_items_rec
23: rustc_mir::monomorphize::collector::collect_items_rec
24: rustc_mir::monomorphize::collector::collect_items_rec
25: rustc_mir::monomorphize::collector::collect_items_rec
26: rustc_mir::monomorphize::collector::collect_items_rec
27: rustc_mir::monomorphize::collector::collect_items_rec
28: rustc_mir::monomorphize::collector::collect_crate_mono_items::{{closure}}
29: rustc::util::common::time
30: rustc_mir::monomorphize::collector::collect_crate_mono_items
31: rustc::util::common::time
32: rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items
33: rustc::ty::query::__query_compute::collect_and_partition_mono_items
34: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors for rustc::ty::query::queries::collect_and_partition_mono_items>::compute
35: rustc::dep_graph::graph::DepGraph::with_task_impl
36: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt>::get_query
37: rustc_codegen_ssa::base::codegen_crate
38: <rustc_codegen_llvm::LlvmCodegenBackend as rustc_codegen_utils::codegen_backend::CodegenBackend>::codegen_crate
39: rustc::util::common::time
40: rustc_interface::passes::start_codegen
41: rustc::ty::context::tls::enter_global
42: rustc_interface::passes::BoxedGlobalCtxt::access::{{closure}}
43: rustc_interface::passes::create_global_ctxt::{{closure}}
44: rustc_interface::passes::BoxedGlobalCtxt::enter
45: rustc_interface::queries::Query<T>::compute
46: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::ongoing_codegen
47: rustc_interface::interface::run_compiler_in_existing_thread_pool
48: std::thread::local::LocalKey<T>::with
49: scoped_tls::ScopedKey<T>::set
50: syntax::with_globals
query stack during panic:
#0 [codegen_fulfill_obligation] checking if `std::ops::FnOnce` fulfills its obligations
#1 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack
error: aborting due to previous error
Source Code without `task`
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)
}
));
}