Skip to content

rust-analyzer thread infinite loops when opening file (most of the time) #21824

Description

@ds84182

rust-analyzer version: rust-analyzer version: 0.4.2826-standalone

rustc version: rustc 1.91.0-nightly (2e2642e64 2025-08-16)

editor or extension: VSCode, 0.4.2826

relevant settings: N/A

repository link (if public, optional): N/A

code snippet to reproduce:

This depends on the higher-kinded-types crate. The infinite loop happens when rdb.rs is opened, or when the server starts with rdb.rs opened. If rdb.rs isn't opened then the infinite loop does not happen. Rarely, opening rdb.rs does not trigger the infinite loop.

This requires two files (otherwise opening lib.rs will hit the infinite loop), along with the hkt dependency.

Cargo.toml

[package]
name = "soup_repro"
version = "0.1.0"
edition = "2024"

[dependencies]
higher-kinded-types = "0.3.0"

src/lib.rs

Details
use std::fmt::Debug;

pub(crate) mod rdb;

pub struct Arena;

pub trait Base {
  type Error: Debug;
  type PinnedBytes<'a>: AsRef<[u8]>;
}
pub trait Value<'a>: 'a {}

pub trait ComposableValue<'a> {}

pub trait KeyPrefix<'a, K>: Value<'a> {}

pub trait WriteBatch {
  fn size(&self) -> usize;
}

pub trait Cursor<'k, 'v, T: Table<'k, 'v, Error = Self::Error>>: Base + Sized {
  fn move_next(&mut self) -> Result<(), Self::Error>;
  fn move_prev(&mut self) -> Result<(), Self::Error>;

  fn seek_to_first(&mut self) -> Result<(), Self::Error>;
  fn seek<'a>(
    &mut self,
    key: &dyn KeyPrefix<'a, T::Key>,
    arena: &'a Arena,
  ) -> Result<(), Self::Error>;
  fn seek_before<'a>(
    &mut self,
    key: &dyn KeyPrefix<'a, T::Key>,
    arena: &'a Arena,
  ) -> Result<(), Self::Error>;
  fn seek_to_last(&mut self) -> Result<(), Self::Error>;

  fn refresh(&mut self) -> Result<(), Self::Error>;

  fn key(&'k self, arena: &'k Arena) -> Option<T::Key>;
  fn value(&'v self, arena: &'v Arena) -> Option<T::Value>;
  fn key_value<'a: 'k + 'v>(&'a self, arena: &'a Arena) -> Option<(T::Key, T::Value)>;
}

pub trait Table<'k, 'v>: Base + Sized + Clone {
  type Snapshot;
  type WriteBatch;
  type Cursor: Cursor<'k, 'v, Self, Error = Self::Error>;

  type Key: Value<'k>;
  type Value: Value<'v>;

  fn get<'this: 'v, 'a: 'k + 'v>(
    &'this self,
    snap: &Self::Snapshot,
    key: &'k Self::Key,
    arena: &'a Arena,
    pinned: &'v mut Option<Self::PinnedBytes<'this>>,
  ) -> Result<Option<Self::Value>, Self::Error>;

  fn put<'a: 'k + 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    key: &'k Self::Key,
    value: &'v Self::Value,
  ) -> Result<(), Self::Error>;

  fn delete<'a: 'k>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    key: &'k Self::Key,
  ) -> Result<(), Self::Error>;

  fn cursor(&self, snap: &Self::Snapshot) -> Self::Cursor;
}

pub trait TableMerge<'k, 'v>: Table<'k, 'v> {
  type Merge: Value<'v>;

  fn merge<'a: 'k + 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    key: &'k Self::Key,
    value: &'v Self::Merge,
  ) -> Result<(), Self::Error>;
}

pub trait Singleton<'v>: Base + Sized + Clone {
  type Snapshot;
  type WriteBatch;

  type Value: Value<'v>;

  fn get<'this: 'v, 'a: 'v>(
    &'this self,
    snap: &Self::Snapshot,
    arena: &'a Arena,
    pinned: &'v mut Option<Self::PinnedBytes<'this>>,
  ) -> Result<Option<Self::Value>, Self::Error>;

  fn put<'a: 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    value: &'v Self::Value,
  ) -> Result<(), Self::Error>;

  fn delete(&self, wb: &mut Self::WriteBatch, arena: &Arena) -> Result<(), Self::Error>;
}

pub trait SingletonMerge<'v>: Singleton<'v> {
  type Merge: Value<'v>;

  fn merge<'a: 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    value: &'v Self::Merge,
  ) -> Result<(), Self::Error>;
}

pub trait TableParams<D: Db> {
  fn params(&self) -> D::TableParams;
}

pub trait OpenTable<D: Db> {
  type Table: for<'k, 'v> Table<'k, 'v, Error = D::Error>;
  fn open(db: &D) -> Self::Table;
}

pub trait OpenSingleton<D: Db> {
  type Singleton: for<'v> Singleton<'v, Error = D::Error>;
  fn open(self, db: &D) -> Self::Singleton;
}

pub trait Db: Base + Clone {
  type Snapshot;
  type WriteBatch: WriteBatch;

  type Options;
  type TableParams;

  fn new(
    path: &std::path::Path,
    opts: &Self::Options,
    tables: &[&dyn TableParams<Self>],
  ) -> Result<Self, Self::Error>;

  fn new_read_only(
    path: &std::path::Path,
    opts: &Self::Options,
    tables: &[&dyn TableParams<Self>],
  ) -> Result<Self, Self::Error>;

  fn snapshot(&self) -> Self::Snapshot;

  fn new_write_batch(&self) -> Self::WriteBatch;
  fn commit(&self, wb: Self::WriteBatch) -> Result<(), Self::Error>;
  fn commit_no_wal(&self, wb: Self::WriteBatch) -> Result<(), Self::Error>;
}

src/rdb.rs

Details
use std::{marker::PhantomData, sync::Arc};

use higher_kinded_types as hkt;

use crate::*;

mod rocksdb {
  pub struct MultiThreaded;
  pub type DBWithThreadMode<T> = std::marker::PhantomData<T>;
  pub struct WriteBatch;
  pub type SnapshotWithThreadMode<'a, T> = std::marker::PhantomData<T>;
  pub type DBRawIteratorWithThreadMode<'a, T> = std::marker::PhantomData<T>;
  pub type Error = ();
  pub type DBPinnableSlice<'a> = &'a [u8];
  pub type BoundColumnFamily<'a> = ();
  pub type ReadOptions = ();
  pub type Options = ();
  pub type ColumnFamilyDescriptor = ();
}

// RocksDB Database Implementation:

#[derive(Clone)]
pub(crate) struct RocksDb(pub(crate) Arc<rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>>);

impl WriteBatch for rocksdb::WriteBatch {
  fn size(&self) -> usize {
    0
  }
}

pub(crate) struct RocksDbSnapshot(
  rocksdb::SnapshotWithThreadMode<'static, rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>>,
  RocksDb,
);

pub(crate) struct RocksDbCursor<T> {
  db: Arc<rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>>,
  pub(crate) iter: rocksdb::DBRawIteratorWithThreadMode<
    'static,
    rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>,
  >,
  table: PhantomData<T>,
}

impl<T> Base for RocksDbCursor<T> {
  type Error = rocksdb::Error;
  type PinnedBytes<'a> = rocksdb::DBPinnableSlice<'a>;
}

impl<'k, 'v, T: Table<'k, 'v, Error = Self::Error>> Cursor<'k, 'v, T> for RocksDbCursor<T> {
  fn move_next(&mut self) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn move_prev(&mut self) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn seek_to_first(&mut self) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn seek<'a>(
    &mut self,
    key: &dyn KeyPrefix<'a, T::Key>,
    arena: &'a Arena,
  ) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn seek_before<'a>(
    &mut self,
    key: &dyn KeyPrefix<'a, T::Key>,
    arena: &'a Arena,
  ) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn seek_to_last(&mut self) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn refresh(&mut self) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn key(&'k self, arena: &'k Arena) -> Option<T::Key> {
    unimplemented!()
  }

  fn value(&'v self, arena: &'v Arena) -> Option<T::Value> {
    unimplemented!()
  }

  fn key_value<'a: 'k + 'v>(&'a self, arena: &'a Arena) -> Option<(T::Key, T::Value)> {
    unimplemented!()
  }
}

pub(crate) struct RocksDbTable<M, K, V> {
  pub(crate) cf: Arc<rocksdb::BoundColumnFamily<'static>>,
  pub(crate) db: RocksDb,
  pub(crate) _pd: PhantomData<(M, K, V)>,
}

impl<M, K, V> Clone for RocksDbTable<M, K, V> {
  fn clone(&self) -> Self {
    Self {
      cf: self.cf.clone(),
      db: self.db.clone(),
      _pd: PhantomData,
    }
  }
}

impl<M, K, V> Base for RocksDbTable<M, K, V> {
  type Error = rocksdb::Error;
  type PinnedBytes<'a> = rocksdb::DBPinnableSlice<'a>;
}

impl<'k, 'v, M, K: for<'a> hkt::ForLt<Of<'a>: Value<'a>>, V: for<'a> hkt::ForLt<Of<'a>: Value<'a>>>
  Table<'k, 'v> for RocksDbTable<M, K, V>
{
  type Snapshot = RocksDbSnapshot;
  type WriteBatch = rocksdb::WriteBatch;
  type Cursor = RocksDbCursor<Self>;

  type Key = K::Of<'k>;
  type Value = V::Of<'v>;

  fn get<'this: 'v, 'a: 'k + 'v>(
    &'this self,
    snap: &Self::Snapshot,
    key: &'k Self::Key,
    arena: &'a Arena,
    pinned: &'v mut Option<Self::PinnedBytes<'this>>,
  ) -> Result<Option<Self::Value>, Self::Error> {
    unimplemented!()
  }

  fn put<'a: 'k + 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    key: &'k Self::Key,
    value: &'v Self::Value,
  ) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn delete<'a: 'k>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    key: &'k Self::Key,
  ) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn cursor(&self, snap: &Self::Snapshot) -> Self::Cursor {
    unimplemented!()
  }
}

pub(crate) struct RocksDbSingleton<M, V> {
  pub(crate) key: &'static [u8],
  pub(crate) table: RocksDbTable<(), hkt::ForLt!(&[u8]), V>,
  pub(crate) _pd: PhantomData<M>,
}

impl<M, V> Clone for RocksDbSingleton<M, V> {
  fn clone(&self) -> Self {
    let Self {
      key,
      ref table,
      _pd,
    } = *self;
    Self {
      key,
      table: table.clone(),
      _pd,
    }
  }
}

impl<M, V> Base for RocksDbSingleton<M, V> {
  type Error = rocksdb::Error;
  type PinnedBytes<'a> = rocksdb::DBPinnableSlice<'a>;
}

impl<'v, M, V: for<'a> hkt::ForLt<Of<'a>: Value<'a>>> Singleton<'v> for RocksDbSingleton<M, V> {
  type Snapshot = RocksDbSnapshot;
  type WriteBatch = rocksdb::WriteBatch;

  type Value = V::Of<'v>;

  fn get<'this: 'v, 'a: 'v>(
    &'this self,
    snap: &Self::Snapshot,
    arena: &'a Arena,
    pinned: &'v mut Option<Self::PinnedBytes<'this>>,
  ) -> Result<Option<Self::Value>, Self::Error> {
    unimplemented!()
  }

  fn put<'a: 'v>(
    &self,
    wb: &mut Self::WriteBatch,
    arena: &'a Arena,
    value: &'v Self::Value,
  ) -> Result<(), Self::Error> {
    unimplemented!()
  }

  fn delete(&self, wb: &mut Self::WriteBatch, arena: &Arena) -> Result<(), Self::Error> {
    unimplemented!()
  }
}

impl Base for RocksDb {
  type Error = rocksdb::Error;
  type PinnedBytes<'a> = rocksdb::DBPinnableSlice<'a>;
}

impl Db for RocksDb {
  type Snapshot = RocksDbSnapshot;
  type WriteBatch = rocksdb::WriteBatch;

  type Options = rocksdb::Options;
  type TableParams = rocksdb::ColumnFamilyDescriptor;

  fn new(
    path: &std::path::Path,
    opts: &Self::Options,
    tables: &[&dyn TableParams<Self>],
  ) -> Result<Self, Self::Error> {
    unimplemented!()
  }

  fn new_read_only(
    path: &std::path::Path,
    opts: &Self::Options,
    tables: &[&dyn TableParams<Self>],
  ) -> Result<Self, Self::Error> {
    unimplemented!()
  }

  fn snapshot(&self) -> Self::Snapshot {
    unimplemented!()
  }

  fn new_write_batch(&self) -> Self::WriteBatch {
    unimplemented!()
  }

  fn commit(&self, wb: Self::WriteBatch) -> Result<(), rocksdb::Error> {
    unimplemented!()
  }

  fn commit_no_wal(&self, wb: Self::WriteBatch) -> Result<(), rocksdb::Error> {
    unimplemented!()
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-macromacro expansionA-nameresname, path and module resolutionBroken WindowBugs / technical debt to be addressed immediatelyC-bugCategory: bugI-hangIssue: The analysis never terminates, due to infinite loops, deadlock, livelock, etc.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions