Skip to content

ICE: OutputTypeParameterMismatch when combining closures and higher-rank lifetime bounds #60070

Closed
@fredericvauchelles

Description

@fredericvauchelles

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:

  1. To store a closure that have Higher-Ranked Lifetimes in its arguments. (See workaround here Annotating higher-ranked lifetimes on closures is arduous #58052
  2. 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)
        }
    ));
}

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️P-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions