salsa-rs / salsa

A generic framework for on-demand, incrementalized computation. Inspired by adapton, glimmer, and rustc's query system.
https://salsa-rs.netlify.app/
Apache License 2.0
2.09k stars 142 forks source link

Add DiplayWithDb #477

Open gavrilikhin-d opened 5 months ago

gavrilikhin-d commented 5 months ago

Add trait to Display items with database

xiyuzhai commented 5 months ago

Haha, I already add this trait in my personal customized version of salsa

gavrilikhin-d commented 5 months ago

@xiyuzhai how did you do it? I have troubles with references lifetimes

xiyuzhai commented 5 months ago

https://github.com/xiyuzhai-husky-lang/husky/blob/main/crates/abstractions/salsa/src/display.rs

I imitated DebugWithDb's implementation. However, my implementation differs from salsa in that I remove the generic parameter Db, and use a single Db type for the whole workspace to save compilation time significantly.


pub trait DisplayWithDb {
    fn display<'me, 'db>(&'me self, db: &'me Db) -> DisplayWith<'me>
    where
        Self: Sized + 'me,
    {
        DisplayWith {
            value: BoxRef::Ref(self),
            db,
        }
    }

    fn display_with<'me, 'db>(&'me self, db: &'me Db) -> DisplayWith<'me>
    where
        Self: Sized + 'me,
    {
        DisplayWith {
            value: BoxRef::Ref(self),
            db,
        }
    }

    /// if `level` is `false` only identity fields should be read, which means:
    ///     - for [#\[salsa::input\]](salsa_macros::input) no fields
    ///     - for [#\[salsa::tracked\]](salsa_macros::tracked) only fields with `#[id]` attribute
    ///     - for [#\[salsa::interned\]](salsa_macros::interned) any field
    fn display_fmt_with_db(&self, f: &mut fmt::Formatter<'_>, db: &Db) -> fmt::Result;
}
gavrilikhin-d commented 5 months ago

I came up with something like this at the end:

use std::fmt::{self, Display, Formatter, FormatterFn};

pub trait DisplayWithDb<'me, DB: ?Sized + 'me> {
    fn fmt_with(&self, db: &DB, f: &mut Formatter<'_>) -> fmt::Result;

    fn display_with(&self, db: &DB) -> FormatterFn<impl Fn(&mut Formatter<'_>) -> fmt::Result> {
        FormatterFn(|f| self.fmt_with(db, f)) // Unstable rust. Write a struct wrapper, if you want to use stable rust
    }

    fn to_string_with(&self, db: &DB) -> String {
        self.display_with(db).to_string()
    }
}

impl<'me, DB: ?Sized + 'me, D: Display> DisplayWithDb<'me, DB> for D {
    fn fmt_with(&self, _db: &DB, f: &mut Formatter<'_>) -> fmt::Result {
        Display::fmt(&self, f)
    }
}

Implement it like this:

#[salsa::interned]
pub struct Typename {
    #[return_ref]
    pub text: String,
}

impl<'me> DisplayWithDb<'me, dyn Db + 'me> for Typename {
    fn fmt_with(&self, db: &dyn Db, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.text(db))
    }
}

There is something tricky with dyn Db lifetimes that forces me to specify them explicitly