Skip to content

Commit

Permalink
Add covariant owner lifetime marker
Browse files Browse the repository at this point in the history
This addresses issue #18 where a contravariant owner could
lead to UB.
  • Loading branch information
Voultapher committed Sep 29, 2021
1 parent 8a8670a commit cf67cbe
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 27 deletions.
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ macro_rules! self_cell {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell<
$Owner,
$Dependent<'static>
>
>,

// marker to ensure that contravariant owners don't imply covariance
// over the dependent. See issue #18
owner_marker: core::marker::PhantomData<$(&$OwnerLifetime)* ()>,
}

impl $(<$OwnerLifetime>)* $StructName $(<$OwnerLifetime>)* {
Expand Down Expand Up @@ -365,6 +369,7 @@ macro_rules! self_cell {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
owner_marker: core::marker::PhantomData,
}
}
}
Expand Down Expand Up @@ -410,6 +415,7 @@ macro_rules! self_cell {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
owner_marker: core::marker::PhantomData,
})
}
Err(err) => Err(err)
Expand Down Expand Up @@ -458,6 +464,7 @@ macro_rules! self_cell {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
owner_marker: core::marker::PhantomData,
})
}
Err(err) => {
Expand Down
32 changes: 32 additions & 0 deletions tests/invalid/contravariant_owner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use self_cell::self_cell;

type X<'a> = &'a str;

self_cell! {
pub struct Foo<'a> {
owner: fn(&'a ()),

#[covariant]
dependent: X,
}
}

fn transmute_lifetime<'a, 'b>(x: &'a str) -> &'b str {
fn helper<'x>(s: &'x str) -> impl for<'z> FnOnce(&'z fn(&'x ())) -> &'z str {
move |_| s
}
let x: Foo<'a> = Foo::new(|_| (), helper(x));
let x: Foo<'static> = x; // coerce using variance
let y = Box::leak(Box::new(x));
y.borrow_dependent()
}

fn main() {
let r;
{
let s = "Hello World".to_owned();
r = transmute_lifetime(s.as_str());
dbg!(r); // "Hello World"
}
dbg!(r); // prints garbage :-)
}
14 changes: 14 additions & 0 deletions tests/invalid/contravariant_owner.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0308]: mismatched types
--> $DIR/contravariant_owner.rs:19:27
|
19 | let x: Foo<'static> = x; // coerce using variance
| ^ lifetime mismatch
|
= note: expected struct `Foo<'static>`
found struct `Foo<'a>`
note: the lifetime `'a` as defined on the function body at 14:23...
--> $DIR/contravariant_owner.rs:14:23
|
14 | fn transmute_lifetime<'a, 'b>(x: &'a str) -> &'b str {
| ^^
= note: ...does not necessarily outlive the static lifetime
34 changes: 12 additions & 22 deletions tests/invalid_manual/wrong_covariance/expected.stderr
Original file line number Diff line number Diff line change
@@ -1,45 +1,35 @@
IGNORE
IGNORE
--> IGNORE
|
--> wrong_covariance/src/main.rs:7:1
|
7 | / self_cell!(
8 | | struct NoCov {
9 | | owner: String,
10 | |
... |
13 | | }
14 | | );
| |__^ lifetime mismatch
|
= note: expected struct `Cell<&'y String>`
found struct `Cell<&'x String>`
| |__^ lifetime mismatch
|
= note: expected struct `Cell<&'y String>`
found struct `Cell<&'x String>`
note: the lifetime `'y` as defined on the function body at IGNORE
--> IGNORE
|
--> wrong_covariance/src/main.rs:7:1
|
7 | / self_cell!(
8 | | struct NoCov {
9 | | owner: String,
10 | |
... |
13 | | }
14 | | );
| |__^
| |__^
note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at IGNORE
--> IGNORE
|
--> wrong_covariance/src/main.rs:7:1
|
7 | / self_cell!(
8 | | struct NoCov {
9 | | owner: String,
10 | |
... |
13 | | }
14 | | );
| |__^
IGNORE

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `wrong_covariance`

To learn more, run the command again with --verbose.
| |__^
5 changes: 1 addition & 4 deletions tests/self_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,10 +680,7 @@ fn try_build_manual(path: &str) {
// Very naive approach.
for expected_line in expected_err.split("\n").map(str::trim) {
if !compile_err.contains(expected_line) {
eprintln!(
"Expected: '{}'\nIn, but got: '{}'",
expected_line, compile_err
);
eprintln!("Expected: '{}'\n\nIN\n\n{}", expected_line, compile_err);
panic!();
}
}
Expand Down

0 comments on commit cf67cbe

Please # to comment.