Skip to content

Covariance check with _assert_covariance is insufficient, unsound #49

Closed
@steffahn

Description

@steffahn
use std::{cell::RefCell, fmt};

use self_cell::self_cell;

self_cell! {
    struct WrongVarianceExample {
        owner: (),
        #[covariant]
        dependent: Dependent,
    }
}

// this type is not covariant
type Dependent<'a> = RefCell<Box<dyn fmt::Display + 'a>>;

Dependent<'a> is not covariant in 'a, however unsize coercion still allows an OWNED value

RefCell<Box<dyn fmt::Display + 'x>>

to be coerced into

RefCell<Box<dyn fmt::Display + 'y>>

for lifetimes 'x: 'y, hence _assert_covariance compiles fine.

Unsoundness follows.

fn main() {
    let cell = WrongVarianceExample::new((), |_| RefCell::new(Box::new("")));
    let s = String::from("Hello World");

    // borrow_dependent unsound due to incorrectly checked variance
    *cell.borrow_dependent().borrow_mut() = Box::new(s.as_str());

    // s still exists
    cell.with_dependent(|_, d| println!("{}", d.borrow()));

    drop(s);

    // s is gone
    cell.with_dependent(|_, d| println!("{}", d.borrow()));
}

(run in Rust Explorer)

Hello World
p����U���

A better _assert_covariance needs to check whether coercing the Dependents behind a level of indirection is still possible.

Options include coercing Box<Dependent<'x>> to Box<Dependent<'y>>, or (more close to the actual use-case) coercing &'y Dependent<'x> into &'y Dependent<'y>.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions