summaryrefslogtreecommitdiffstats
path: root/src/test/ui/associated-types/defaults-unsound-62211-1.rs
blob: fa6a208b4f1baf287ab7e58c27d49b698e70d7a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//! Regression test for https://github.com/rust-lang/rust/issues/62211
//!
//! The old implementation of defaults did not check whether the provided
//! default actually fulfills all bounds on the assoc. type, leading to
//! unsoundness, demonstrated here as a use-after-free.
//!
//! Note that the underlying cause of this is still not yet fixed.
//! See: https://github.com/rust-lang/rust/issues/33017

#![feature(associated_type_defaults)]

use std::{
    fmt::Display,
    ops::{AddAssign, Deref},
};

trait UncheckedCopy: Sized {
    // This Output is said to be Copy. Yet we default to Self
    // and it's accepted, not knowing if Self ineed is Copy
    type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self;
    //~^ ERROR the trait bound `Self: Copy` is not satisfied
    //~| ERROR the trait bound `Self: Deref` is not satisfied
    //~| ERROR cannot add-assign `&'static str` to `Self`
    //~| ERROR `Self` doesn't implement `std::fmt::Display`

    // We said the Output type was Copy, so we can Copy it freely!
    fn unchecked_copy(other: &Self::Output) -> Self::Output {
        (*other)
    }

    fn make_origin(s: Self) -> Self::Output {
        s.into()
    }
}

impl<T> UncheckedCopy for T {}

fn bug<T: UncheckedCopy>(origin: T) {
    let origin = T::make_origin(origin);
    let mut copy = T::unchecked_copy(&origin);

    // assert we indeed have 2 strings pointing to the same buffer.
    assert_eq!(origin.as_ptr(), copy.as_ptr());

    // Drop the origin. Any use of `copy` is UB.
    drop(origin);

    copy += "This is invalid!";
    println!("{}", copy);
}

fn main() {
    bug(String::from("hello!"));
}