rust-lang / rust

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

async code fails to compile with `-Znext-solver` #129865

Open 0xdeafbeef opened 1 month ago

0xdeafbeef commented 1 month ago

I tried this code:

cd $(mktemp -d)
git clone https://github.com/wvwwvwwv/scalable-concurrent-containers.git
cd scalable-concurrent-containers/
git checkout c014a2c7ce98a13237b842bd03b80206ff8bb66e

compiling with normal rustc succeeds:

cargo +nightly check --locked
    Checking sdd v3.0.2
    Checking scc v2.1.16 (/tmp/tmp.RjDtqDRisT/scalable-concurrent-containers)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.72s

using next-solver fails

RUSTFLAGS='-Znext-solver'  cargo +nightly check
    Checking sdd v3.0.2
    Checking scc v2.1.16 (/tmp/tmp.RjDtqDRisT/scalable-concurrent-containers)
error: concrete type differs from previous defining opaque type use
    --> src/hash_map.rs:1439:5
     |
1439 |     async fn cleanse_old_array_async(&self, current_array: &BucketArray<K, V, (), SEQUENTIAL>) {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{async fn body of hash_map::HashMap<K, V, H>::cleanse_old_array_async()}`, got `{async fn body of hash_map::HashMap<K, V, H>::cleanse_old_array_async()}`
     |
     = note: previous use here

error: concrete type differs from previous defining opaque type use
    --> src/hash_index.rs:1090:5
     |
1090 |     async fn cleanse_old_array_async(&self, current_array: &BucketArray<K, V, (), OPTIMISTIC>) {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{async fn body of HashIndex<K, V, H>::cleanse_old_array_async()}`, got `{async fn body of HashIndex<K, V, H>::cleanse_old_array_async()}`
     |
     = note: previous use here

error: concrete type differs from previous defining opaque type use
    --> src/hash_cache.rs:1064:5
     |
1064 | /     async fn cleanse_old_array_async(
1065 | |         &self,
1066 | |         current_array: &BucketArray<K, V, DoublyLinkedList, CACHE>,
1067 | |     ) {
     | |_____^ expected `{async fn body of HashCache<K, V, H>::cleanse_old_array_async()}`, got `{async fn body of HashCache<K, V, H>::cleanse_old_array_async()}`
     |
     = note: previous use here

error: could not compile `scc` (lib) due to 3 previous errors

Meta

rustc --version --verbose:

cargo 1.82.0-nightly (8f40fc59f 2024-08-21)
release: 1.82.0-nightly
commit-hash: 8f40fc59fb0c8df91c97405785197f3c630304ea
commit-date: 2024-08-21
host: x86_64-unknown-linux-gnu
libgit2: 1.8.1 (sys:0.19.0 vendored)
libcurl: 8.9.0-DEV (sys:0.4.74+curl-8.9.0 vendored ssl:OpenSSL/1.1.1w)
ssl: OpenSSL 1.1.1w  11 Sep 2023
os: Fedora 40.0.0 [64-bit]

I'm not sure even if it is an issue, but here's report anyway :)

traviscross commented 1 week ago

@0xdeafbeef: Might you be able to try to minimize this to a self-contained example? That would be helpful to us in trying to better understand this.

0xdeafbeef commented 1 week ago

@0xdeafbeef: Might you be able to try to minimize this to a self-contained example? That would be helpful to us in trying to better understand this.

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

pub struct AtomicShared<T>(PhantomData<T>);
pub struct BucketArray<K, V, L, const TYPE: char>(PhantomData<(K, V, L)>);
pub struct AsyncWait;
pub struct Guard;
pub struct DoublyLinkedList;

trait HashTable<K, V, H, L, const TYPE: char> {
    fn cleanse_old_array_async<'a>(
        &'a self,
        current_array: &'a BucketArray<K, V, L, TYPE>,
    ) -> impl Future<Output = ()> + 'a;
}

struct HashMap<K, V, H> {
    _marker: PhantomData<(K, V, H)>,
}

struct HashIndex<K, V, H> {
    _marker: PhantomData<(K, V, H)>,
}

struct HashCache<K, V, H> {
    _marker: PhantomData<(K, V, H)>,
}

impl<K, V, H> HashTable<K, V, H, (), 'S'> for HashMap<K, V, H> {
    fn cleanse_old_array_async<'a>(
        &'a self,
        current_array: &'a BucketArray<K, V, (), 'S'>,
    ) -> impl Future<Output = ()> + 'a {
        async move {
            while current_array.has_old_array() {
                let mut async_wait = AsyncWait;
                let mut async_wait_pinned = Pin::new(&mut async_wait);
                if self.incremental_rehash(current_array, &mut async_wait_pinned, &Guard::new()) {
                    break;
                }
                std::future::pending::<()>().await;
            }
        }
    }
}

impl<K, V, H> HashTable<K, V, H, (), 'O'> for HashIndex<K, V, H> {
    fn cleanse_old_array_async<'a>(
        &'a self,
        current_array: &'a BucketArray<K, V, (), 'O'>,
    ) -> impl Future<Output = ()> + 'a {
        async move {
            while current_array.has_old_array() {
                let mut async_wait = AsyncWait;
                let mut async_wait_pinned = Pin::new(&mut async_wait);
                if self.incremental_rehash(current_array, &mut async_wait_pinned, &Guard::new()) {
                    break;
                }
                std::future::pending::<()>().await;
            }
        }
    }
}

impl<K, V, H> HashTable<K, V, H, DoublyLinkedList, 'C'> for HashCache<K, V, H> {
    fn cleanse_old_array_async<'a>(
        &'a self,
        current_array: &'a BucketArray<K, V, DoublyLinkedList, 'C'>,
    ) -> impl Future<Output = ()> + 'a {
        async move {
            while current_array.has_old_array() {
                let mut async_wait = AsyncWait;
                let mut async_wait_pinned = Pin::new(&mut async_wait);
                if self.incremental_rehash(current_array, &mut async_wait_pinned, &Guard::new()) {
                    break;
                }
                std::future::pending::<()>().await;
            }
        }
    }
}

trait HashTableImpl<K, V, H, L, const TYPE: char> {
    fn incremental_rehash(
        &self,
        current_array: &BucketArray<K, V, L, TYPE>,
        async_wait: &mut Pin<&mut AsyncWait>,
        guard: &Guard,
    ) -> bool;
}

impl<K, V, H, L, const TYPE: char> HashTableImpl<K, V, H, L, TYPE> for HashMap<K, V, H> {
    fn incremental_rehash(
        &self,
        _: &BucketArray<K, V, L, TYPE>,
        _: &mut Pin<&mut AsyncWait>,
        _: &Guard,
    ) -> bool {
        true
    }
}

impl<K, V, H, L, const TYPE: char> HashTableImpl<K, V, H, L, TYPE> for HashIndex<K, V, H> {
    fn incremental_rehash(
        &self,
        _: &BucketArray<K, V, L, TYPE>,
        _: &mut Pin<&mut AsyncWait>,
        _: &Guard,
    ) -> bool {
        true
    }
}

impl<K, V, H, L, const TYPE: char> HashTableImpl<K, V, H, L, TYPE> for HashCache<K, V, H> {
    fn incremental_rehash(
        &self,
        _: &BucketArray<K, V, L, TYPE>,
        _: &mut Pin<&mut AsyncWait>,
        _: &Guard,
    ) -> bool {
        true
    }
}

impl<K, V, L, const TYPE: char> BucketArray<K, V, L, TYPE> {
    fn has_old_array(&self) -> bool {
        false
    }
}

impl Guard {
    fn new() -> Self {
        Guard
    }
}

I reduced to this, but it doesn't reproduce :(

lqd commented 1 week ago

Image

pub async fn cleanse_old_array_async(_: &[u8; BUCKET_LEN]) {}
pub const BUCKET_LEN: usize = 0;

Link to godbolt due to -Znext-solver.

lcnr commented 1 week ago

nice minimization :3 cc @compiler-errors

compiler-errors commented 1 week ago

eagerly normalizing consts :'(