Open fakeshadow opened 1 year ago
this is a regression happens in 1.72.0-nightly (5bd28f5ea 2023-06-28)
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());
}
I got the same ICE, but on rustc 1.72.0 stable, which persists on the latest nightly.
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 {}
}
}
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
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
```
0: _rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::assert_failed_inner
3: core::panicking::assert_failed::
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 {}
}
}
WG-prioritization assigning priority (Zulip discussion).
@rustbot label -I-prioritize +P-critical +E-needs-bisection
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?
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 {}
}
}
Yeah, @ouz-a can you add the test above?
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
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!)
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.
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.
Is there any update on this? @wesleywiser
I tried this code:
I expected to see this happen: code compile.
Instead, this happened: internal compiler error: unexpected initial operand type
Meta
rustc --version --verbose
: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.
```