Skip to content

Odd behaviour on self-referential types that use UnsafeCell #1665

@Kestrer

Description

@Kestrer

When running this code under Miri (playground link):

use std::cell::UnsafeCell;
use std::marker::PhantomPinned;
use std::pin::Pin;

pub struct SelfReferential {
    number: UnsafeCell<i32>,
    number_mut_ref: Option<*const UnsafeCell<i32>>,
    _pinned: PhantomPinned,
}
impl SelfReferential {
    pub fn new() -> Self {
        Self {
            number: UnsafeCell::new(0),
            number_mut_ref: None,
            _pinned: PhantomPinned,
        }
    }
    pub fn initialize(self: Pin<&mut Self>, number: i32) {
        let this = unsafe { self.get_unchecked_mut() };
        this.number = UnsafeCell::new(number);
        this.number_mut_ref = Some(&this.number);
    }
    pub fn add_one(self: Pin<&mut Self>) {
        let this = unsafe { self.get_unchecked_mut() };
        // If this line that is seemingly a no-op is commented, this code fails.
        unsafe { &*this.number.get() };
        let number_ptr = this.number_mut_ref.unwrap();
        *unsafe { &mut *(&*number_ptr).get() } += 1;
    }
}

fn main() {
    let v = SelfReferential::new();
    pin_utils::pin_mut!(v);
    v.as_mut().initialize(5);
    v.as_mut().add_one();
}

No errors are produced. However, if that line is commented, there is a violation of stacked borrows.

What is the intended behaviour here? And is this a valid way to implement safe self-referential types?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions