rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.82k stars 12.77k forks source link

ICE: unexpected initial operand type. #114858

Open fakeshadow opened 1 year ago

fakeshadow commented 1 year ago

I tried this code:

#![feature(impl_trait_in_assoc_type)]

use std::{future::Future, convert::Infallible};

trait AsyncFn<Arg> {
    type Output;
    type Future<'f>: Future<Output = Self::Output>
    where
        Arg: 'f,
        Self: 'f;

    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's;
}

trait AsyncFn2<Arg> {
    type Output;
    type Future: Future<Output = Self::Output>;

    fn call(&self, arg: Arg) -> Self::Future;
}

impl<F, A, B, Fut> AsyncFn2<(A, B)> for F
where
    F: Fn(A, B) -> Fut,
    Fut: Future,
{
    type Output = Fut::Output;
    type Future = Fut;

    fn call(&self, (a, b): (A, B)) -> Self::Future {
        self(a, b)
    }
}

struct Func<F>(F);

impl<Arg, F, Fut> AsyncFn<Arg> for Func<F>
where
    F: Fn(Arg) -> Fut,
    Fut: Future,
{
    type Output = Fut::Output;
    type Future<'f> = impl Future<Output = Self::Output> + 'f where Arg: 'f, Self: 'f;

    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's,
    {
        async move { (self.0)(arg).await }
    }
}

struct EnclosedFn<T1, T2> {
    this: T1,
    other: T2,
}

impl<Arg, T1, T2, O> AsyncFn<Arg> for EnclosedFn<T1, T2>
where
    T1: AsyncFn<Arg>,
    T2: for<'s> AsyncFn2<(&'s T1, Arg), Output = O>,
{
    type Output = O;
    type Future<'f> = impl Future<Output = Self::Output> + 'f where Arg: 'f, Self: 'f;

    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's,
    {
        self.other.call((&self.this, arg))
    }
}

trait Enclosed<Arg>: AsyncFn<Arg> {
    fn enclosed<T>(self, other: T) -> EnclosedFn<Self, T>
    where
        T: for<'s> AsyncFn2<(&'s Self, Arg)>,
        Self: Sized,
    {
        EnclosedFn { this: self, other }
    }
}

impl<Arg, F> Enclosed<Arg> for F where F: AsyncFn<Arg> {}

fn main() {
    async fn middelware<S, Arg>(s: &S, arg: Arg) -> S::Output
    where
        S: AsyncFn<Arg>,
    {
        s.call(arg).await
    }

    let f = Func(|arg: String| async move { Ok::<_, Infallible>(arg) })
        .enclosed(middelware)
        .enclosed(middelware);

    futures::executor::block_on(f.call(String::new())).unwrap();
}

I expected to see this happen: code compile.

Instead, this happened: internal compiler error: unexpected initial operand type

Meta

rustc --version --verbose:

rustc 1.73.0-nightly (1b198b3a1 2023-08-13)
binary: rustc
commit-hash: 1b198b3a196442e14fb06978166ab46a4618d131
commit-date: 2023-08-13
host: x86_64-unknown-linux-gnu
release: 1.73.0-nightly
LLVM version: 17.0.0
Backtrace

``` thread 'rustc' panicked at /rustc/1b198b3a196442e14fb06978166ab46a4618d131/compiler/rustc_codegen_ssa/src/mir/locals.rs:46:21: assertion failed: `(left == right)` left: `*mut [async fn body@src/main.rs:333:5: 335:6]`, right: `*mut [async fn body@src/main.rs:333:5: 335:6]`: unexpected initial operand type stack backtrace: 0: rust_begin_unwind at /rustc/1b198b3a196442e14fb06978166ab46a4618d131/library/std/src/panicking.rs:617:5 1: core::panicking::panic_fmt at /rustc/1b198b3a196442e14fb06978166ab46a4618d131/library/core/src/panicking.rs:67:14 2: core::panicking::assert_failed_inner 3: core::panicking::assert_failed:: 4: rustc_codegen_ssa::mir::codegen_mir:: 5: rustc_codegen_llvm::base::compile_codegen_unit::module_codegen 6: ::with_deps::<>::with_task>::{closure#0}::{closure#0}, rustc_codegen_ssa::ModuleCodegen> 7: rustc_codegen_llvm::base::compile_codegen_unit 8: rustc_codegen_ssa::base::codegen_crate:: 9: ::codegen_crate 10: ::time::, rustc_interface::passes::start_codegen::{closure#0}> 11: rustc_interface::passes::start_codegen 12: ::enter::<::ongoing_codegen::{closure#0}, core::result::Result, rustc_span::ErrorGuaranteed>> 13: rustc_span::set_source_map::, rustc_interface::interface::run_compiler, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}::{closure#0}> note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ```

fakeshadow commented 1 year ago

this is a regression happens in 1.72.0-nightly (5bd28f5ea 2023-06-28)

albertlarsan68 commented 1 year ago

Minimized using cargo-minimize and Perses, so I was able to remove dependency on futures:

#![feature(impl_trait_in_assoc_type)]
use std::future::Future;
trait AsyncFn<Arg> {
    type Output;
    type Future<'f>: Future<Output = Self::Output>
    where
        Arg: 'f,
        Self: 'f;
    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's;
}
trait AsyncFn2<Arg> {
    type Output;
    type Future: Future<Output = Self::Output>;
    fn call(&self, arg: Arg) -> Self::Future;
}
impl<F, A, B, Fut> AsyncFn2<(A, B)> for F
where
    F: Fn(A, B) -> Fut,
    Fut: Future
{
    type Output = Fut::Output;
    type Future = Fut;
    fn call(&self, _a: (A, B)) -> Self::Future {
        loop {}
    }
}
struct Func<F>(F);
impl<Arg, F, Fut> AsyncFn<Arg> for Func<F>
where
    F: Fn(Arg) -> Fut,
    Fut: Future
{
    type Output = Fut::Output;
    type Future<'f>
     = impl Future<Output = Self::Output> + 'f where Arg: 'f, Self: 'f;
    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's
    {
        async move { (self.0)(arg).await }
    }
}
struct EnclosedFn<T1, T2> {
    this: T1,
    other: T2
}
impl<Arg, T1, T2, O> AsyncFn<Arg> for EnclosedFn<T1, T2>
where
    T2: for<'s> AsyncFn2<(&'s T1, Arg), Output = O>
{
    type Output = O;
    type Future<'f>
     = impl Future<Output = Self::Output> + 'f where Arg: 'f, Self: 'f;
    fn call<'s>(&'s self, arg: Arg) -> Self::Future<'s>
    where
        Arg: 's
    {
        self.other.call((&self.this, arg))
    }
}
trait Enclosed<Arg> {
    fn enclosed<T>(self, _other: T) -> EnclosedFn<Self, T>
    where
        Self: Sized
    {
        loop {}
    }
}
impl<Arg, F> Enclosed<Arg> for F where F: AsyncFn<Arg> {}
fn main() {
    async fn middelware<S, Arg>(s: &S, arg: Arg) -> S::Output
    where
        S: AsyncFn<Arg>
    {
        s.call(arg).await
    }
    let f = Func(|arg| async move {})
        .enclosed(middelware)
        .enclosed(middelware);
    f.call(String::new());
}
PhotonQuantum commented 1 year ago

I got the same ICE, but on rustc 1.72.0 stable, which persists on the latest nightly.

Code

I apologize I can't shorten the code at the moment as I'm in a rush. The code is taken from a project using actix-web and sqlx, and I have extracted all the relevant traits and types. It only requires futures and tokio to build, and works in the playground.

use std::future::Future;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;

use futures::future::BoxFuture;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    wrapper_call(handler, ()).await; // must call through `wrapper_call`, or no ICE
}

pub async fn wrapper_call<F, Args>(handler: F, args: Args) -> F::Output
where
    F: Handler<Args>,
    Args: FromRequest + 'static,
    F::Output: Responder + 'static,
{
    std::hint::black_box(handler.call(args).await)
}

async fn handler() -> impl Responder {
    let mut conn: PoolConnection<Postgres> = loop {};
    let _a = f(&mut *conn).await; // must pass to a function accepting `impl Acquire`, and must await
}

async fn f<'a>(db: impl Acquire<'a, Database = Postgres>) {
    let _a = db.acquire().await; // must await
}

// === Taken from actix-web ===

pub trait Responder {}

impl Responder for () {}

pub trait Handler<Args>: Clone + 'static {
    type Output;
    type Future: Future<Output = Self::Output>;

    fn call(&self, args: Args) -> Self::Future;
}

impl<Func, Fut> Handler<()> for Func
where
    Func: Fn() -> Fut + Clone + 'static,
    Fut: Future,
{
    type Output = Fut::Output;
    type Future = Fut;

    #[inline]
    fn call(&self, _: ()) -> Self::Future {
        loop {}
    }
}

pub trait FromRequest: Sized {}

impl FromRequest for () {}

pub struct Data<T: ?Sized>(Arc<T>);

impl<T: ?Sized> Deref for Data<T> {
    type Target = Arc<T>;

    fn deref(&self) -> &Arc<T> {
        loop {}
    }
}

impl<T: ?Sized + 'static> FromRequest for Data<T> {}

// === Taken from sqlx ===

pub trait Acquire<'c> {
    type Database: Database;

    type Connection: Deref<Target = <Self::Database as Database>::Connection> + DerefMut + Send;

    fn acquire(self) -> BoxFuture<'c, Self::Connection>;
}

pub trait Database: 'static + Sized + Send {
    type Connection: Connection<Database = Self>;
}

pub trait Connection: Send {
    type Database: Database;
}

pub struct PoolConnection<DB: Database>(PhantomData<DB>);

impl<DB: Database> Deref for PoolConnection<DB> {
    type Target = DB::Connection;

    fn deref(&self) -> &Self::Target {
        loop {}
    }
}

impl<DB: Database> DerefMut for PoolConnection<DB> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        loop {}
    }
}

impl<'c, DB: Database> Acquire<'c> for &'c mut PoolConnection<DB> {
    type Database = DB;

    type Connection = &'c mut <DB as Database>::Connection;

    #[inline]
    fn acquire(self) -> BoxFuture<'c, Self::Connection> {
        loop {}
    }
}

pub struct Postgres;

impl Database for Postgres {
    type Connection = PgConnection;
}

pub struct PgConnection;

impl Connection for PgConnection {
    type Database = Postgres;
}

impl<'c> Acquire<'c> for &'c mut PgConnection {
    type Database = Postgres;

    type Connection = &'c mut <Postgres as Database>::Connection;

    #[inline]
    fn acquire(self) -> BoxFuture<'c, Self::Connection> {
        loop {}
    }
}

https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=f78b9542bf0a24ece742bba90a2b8247

Meta

rustc 1.72.0 (5680fa18f 2023-08-23)
binary: rustc
commit-hash: 5680fa18feaa87f3ff04063800aec256c3d4b4be
commit-date: 2023-08-23
host: aarch64-apple-darwin
release: 1.72.0
LLVM version: 16.0.5

Error output

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `*mut [async fn body@src/main.rs:22:38: 25:2]`,
 right: `*mut [async fn body@src/main.rs:22:38: 25:2]`: unexpected initial operand type', /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/compiler/rustc_codegen_ssa/src/mir/locals.rs:46:21
Backtrace

``` 0: _rust_begin_unwind 1: core::panicking::panic_fmt 2: core::panicking::assert_failed_inner 3: core::panicking::assert_failed:: 4: rustc_codegen_ssa::mir::codegen_mir:: 5: rustc_codegen_ssa::base::codegen_instance:: 6: rustc_codegen_llvm::base::compile_codegen_unit::module_codegen 7: ::with_deps::<>::with_task>::{closure#0}::{closure#0}, rustc_codegen_ssa::ModuleCodegen> 8: rustc_codegen_llvm::base::compile_codegen_unit 9: rustc_codegen_ssa::base::codegen_crate:: 10: ::codegen_crate 11: ::time::, rustc_interface::passes::start_codegen::{closure#0}> 12: rustc_interface::passes::start_codegen 13: ::enter::<::ongoing_codegen::{closure#0}, core::result::Result, rustc_span::ErrorGuaranteed>> 14: ::ongoing_codegen 15: ::enter::, rustc_span::ErrorGuaranteed>> 16: rustc_span::set_source_map::, rustc_interface::interface::run_compiler, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}::{closure#0}> note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ```

albertlarsan68 commented 1 year ago

Minimized using cargo-minimize and Perses, and removed all the dependencies:

use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T>>>;
fn main() {
    wrapper_call(handler, ());
}
async fn wrapper_call<F, Args>(handler: F, args: Args) -> F::Output
where
    F: Handler<Args>,
{
    handler.call(args).await
}
async fn handler() {
    let conn: PoolConnection<Postgres> = loop {};
    f(&mut *conn).await;
}
async fn f<'a>(db: impl Acquire<'a>) {
    db.acquire().await;
}
trait Handler<Args> {
    type Output;
    type Future: Future<Output = Self::Output>;
    fn call(&self, args: Args) -> Self::Future;
}
impl<Func, Fut> Handler<()> for Func
where
    Func: Fn() -> Fut,
    Fut: Future,
{
    type Output = Fut::Output;
    type Future = Fut;
    fn call(&self, _: ()) -> Self::Future {
        loop {}
    }
}
trait Acquire<'c> {
    type Database;
    type Connection;
    fn acquire(self) -> BoxFuture<'c, Self::Connection>;
}
trait Database {
    type Connection;
}
struct PoolConnection<DB>(DB);
impl<DB: Database> Deref for PoolConnection<DB> {
    type Target = DB::Connection;
    fn deref(&self) -> &Self::Target {
        loop {}
    }
}
struct Postgres;
impl Database for Postgres {
    type Connection = PgConnection;
}
struct PgConnection;
impl<'c> Acquire<'c> for &'c mut PgConnection {
    type Database = Postgres;
    type Connection = &'c Postgres;
    fn acquire(self) -> BoxFuture<'c, Self::Connection> {
        loop {}
    }
}
apiraino commented 1 year ago

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-critical +E-needs-bisection

lqd commented 1 year ago

Bisected to #113108.

https://github.com/rust-lang/rust/issues/114858#issuecomment-1694401072 could likely be reduced even more, but it seems small enough to get a test in #115215 to fix the issue, wdyt @compiler-errors?

Luk-ESC commented 1 year ago

Reduced https://github.com/rust-lang/rust/issues/114858#issuecomment-1694401072 some more:

use std::future::Future;
use std::pin::Pin;

type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;

fn main() {
    _ = wrapper_call(handler);
}

async fn wrapper_call(handler: impl Handler) {
    handler.call().await;
}
async fn handler() {
    f(&()).await;
}
async fn f<'a>(db: impl Acquire<'a>) {
    db.acquire().await;
}

trait Handler {
    type Future: Future;
    fn call(self) -> Self::Future;
}

impl<Fut, F> Handler for F
where
    F: Fn() -> Fut,
    Fut: Future,
{
    type Future = Fut;
    fn call(self) -> Self::Future {
        loop {}
    }
}

trait Acquire<'a> {
    type Connection;
    fn acquire(self) -> BoxFuture<Self::Connection>;
}
impl<'a> Acquire<'a> for &'a () {
    type Connection = Self;
    fn acquire(self) -> BoxFuture<Self> {
        loop {}
    }
}
compiler-errors commented 1 year ago

Yeah, @ouz-a can you add the test above?

ouz-a commented 1 year ago

We should keep this issue open even after https://github.com/rust-lang/rust/pull/115215 is merged, since there seems to be something else going on see https://github.com/rust-lang/rust/pull/115215#issuecomment-1710401747

fasterthanlime commented 1 year ago

As of nightly-2023-09-13 (which includes #115215), my test suite passes.

That warning does appear thrice during compilation, I believe that's work left for later:

WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type. See the issues/114858

(My work is unblocked though, thanks very much!)

ouz-a commented 1 year ago

As of nightly-2023-09-13 (which includes #115215), my test suite passes.

The same warning does appear thrice during compilation, I believe that's work left for later:

WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type. See the issues/114858

Hopefully that shouldn't affect your workflow, we will investigate this issue further in the future.

wesleywiser commented 1 year ago

Visited during the weekly compiler triage meeting. We think the severity of this issue can now be downgraded to P-high as the compiler no longer ICEs and work is continuing to remove the root cause of the current warning which is in place.

tsatke commented 6 months ago

Is there any update on this? @wesleywiser