Skip to content

Instantly share code, notes, and snippets.

@lcnr
Last active March 5, 2024 20:41
Show Gist options
  • Save lcnr/7c1c652f30567048ea240554a36ed95c to your computer and use it in GitHub Desktop.
Save lcnr/7c1c652f30567048ea240554a36ed95c to your computer and use it in GitHub Desktop.
Impact on existing crates

rust-lang/rust#119820 is a breaking change. A crater run found 17 regressed crates.

calling Ramhorns::get https://docs.rs/ramhorns/0.14.0/ramhorns/struct.Ramhorns.html#method.get relies on inference guidance by dropping a candidate due to the leak check

This can be minimized to

trait Trait<T> {}
impl<T> Trait<T> for T {}
impl<T> Trait<T> for &T {}

fn get<S>(_: &S)
where
    for<'a> &'a u32: Trait<S>,
{}

fn render(template: &Box<u32>) {
    get(template);
    // `for<'a> &'a u32: Trait<?S>` previously discarded the first impl
    // as it causes a placeholder error. This inferred `?S` to `u32`,
    // causing coercion to add a necessary auto-deref step here.
}

FIXED IN maciejhirsz/ramhorns#69. NICE

Caused by multiple impls with one getting dropped due to the leak check.

struct SmtParser<R> {
    input: R,
}

pub trait IdentParser<Ident, Type, Input>: Copy {
    // ..
}

impl<'a, Br> IdentParser<&'a str, &'a str, &'a mut SmtParser<Br>> for () {
    // ..
}

impl<'a, Br> IdentParser<String, String, &'a mut SmtParser<Br>> for () {
    // ..
}

impl<R> SmtParser<R> {
    fn get_model<Ident, Type, Parser>(&mut self, parser: Parser)
    where
        Parser: for<'a> IdentParser<Ident, Type, &'a mut Self>,
    {}
}

fn foo<R>(mut p: SmtParser<R>) {
    p.get_model(()); // impl for `&'a str` causes a leak check failure
}

FIXED IN kino-mc/rsmt2#43

https://github.com/launchbadge/sqlx some derives of sqlx::Type

Higher ranked lifetime error when deriving sqlx::Type, e.g.

use sqlx::Type;

#[derive(Type)]
struct Position {
    id: i64,
}

This expands into

struct Position {
    id: i64,
}

impl Encode<'_, Postgres> for Position
where
    i64: for<'q> Encode<'q, Postgres>,
    i64: Type<Postgres>,
{
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
        let mut encoder = PgRecordEncoder::new(buf);
        encoder.encode(&self.id);
        encoder.finish();
        IsNull::No
    }
    fn size_hint(&self) -> usize {
        1usize * (4 + 4) + <i64 as Encode<Postgres>>::size_hint(&self.id)
    }
}
impl<'r> Decode<'r, Postgres> for Position
where
    i64: Decode<'r, Postgres>,
    i64: Type<Postgres>,
{
    fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn Error + 'static + Send + Sync>> {
        let mut decoder = PgRecordDecoder::new(value)?;
        let id = decoder.try_decode::<i64>()?;
        // ERROR: this requires `for<'r> i64: Decode<'r, Postgres>`
        Ok(Position { id })
    }
}

impl Type<Postgres> for Position {
    fn type_info() -> PgTypeInfo {
        PgTypeInfo::with_name("Position")
    }
}

Replacing the higher ranked bound in https://github.com/launchbadge/sqlx/blob/84d576004c93a32133688426eacb50434bb5c5f0/sqlx-postgres/src/types/record.rs#L97 should fix this and compiles. Similarly, why does Encode require i64: for<'q> Encode<'q, Postgres> instead of i64: Encode<'param, Postgres>?

ALREADY FIXED ON MASTER

// from the `tracing-subscriber` crate
trait MakeWriter<'a> {}
impl<'a, F: Fn() -> W> MakeWriter<'a> for F {}

fn new_formatting_layer<F: for<'a> MakeWriter<'a>>(_: F) {}

fn get_subsriber<'a, F: MakeWriter<'a> + Fn() -> W>(f: F) {
    new_formatting_layer(f)
    // Non-HR bound, HR goal.
} 

FIXED IN renato145/docker_queue#1

We get a universe error when using the param_env candidate but are able to successfully use the impl candidate. Without the leak check both candidates may apply and we prefer the param_env candidate in winnowing.

trait Trait {}

impl<T: Trait> Trait for &T {}
impl Trait for u32 {}


fn hr_bound<T>() 
where
    for<'a> &'a T: Trait,
{}

fn foo<T>()
where
    T: Trait,
    for<'a> &'a &'a T: Trait,
{
    hr_bound::<&T>();
    // We get a universe error when using the `param_env` candidate
    // but are able to successfully use the impl candidate. Without
    // the leak check both candidates may apply and we prefer the
    // `param_env` candidate in winnowing.
}

fn main() {}

FIXED IN lycantropos/rene#5

Generated code by a derive, something like the following:

use serde::{de::DeserializeOwned, Deserialize, Deserializer};

struct NeedsOwned(bool);
impl<'de> Deserialize<'de> for NeedsOwned
where
    bool: DeserializeOwned,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(NeedsOwned(<bool as Deserialize>::deserialize(deserializer)?))
    }
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    bool: Deserialize<'de>,
    D: Deserializer<'de>,
{
    // requires `bool: DeserializeOwned` which requires `for<'de> bool: Deserialize<'de>`
    match <NeedsOwned as ::serde::Deserialize>::deserialize(deserializer) {
        Ok(value) => Ok(value.0),
        Err(e) => Err(e),
    }
}

FIXED IN jmeggitt/serde_flat_path#1

Failure in a branch of rocket https://github.com/jebrosen/Rocket/tree/tracing-rebase-202103

A trivial where-bound uses the same bound variable in both the self-type and as a trait argument, while impl uses separate lifetimes.

This means that trying to apply the where-bound for F: for<'a> FormatFields<'a> results in a higher ranked region error while applying the impl succeeds.

trait FormatFields<'a> {}
struct FmtContext<'a, S> {
    whatever: &'a S,
}
impl<'a, 'b, S> FormatFields<'a> for FmtContext<'b, S> {}

fn requires_hr<F: for<'a> FormatFields<'a>>(_: &F) {}

fn format_event<S>(x: &FmtContext<'_, S>)
where
    for<'a> FmtContext<'a, S>: FormatFields<'a>,
{
    requires_hr(x)
}

fn main() {}

WON'T FIX, REQUIRES ADJUSTING AN UNMAINTAINED BRANCH OF ROCKET

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment