Skip to content

Commit c7aa270

Browse files
authored
Rollup merge of rust-lang#96820 - r-raymond:master, r=cuviper
Make RwLockReadGuard covariant Hi, first time contributor here, if anything is not as expected, please let me know. `RwLockReadGoard`'s type constructor is invariant. Since it behaves like a smart pointer to an immutable reference, there is no reason that it should not be covariant. Take e.g. ``` fn test_read_guard_covariance() { fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} let j: i32 = 5; let lock = RwLock::new(&j); { let i = 6; do_stuff(lock.read().unwrap(), &i); } drop(lock); } ``` where the compiler complains that &i doesn't live long enough. If `RwLockReadGuard` is covariant, then the above code is accepted because the lifetime can be shorter than `'a`. In order for `RwLockReadGuard` to be covariant, it can't contain a full reference to the `RwLock`, which can never be covariant (because it exposes a mutable reference to the underlying data structure). By reducing the data structure to the required pieces of `RwLock`, the rest falls in place. If there is a better way to do a test that tests successful compilation, please let me know. Fixes rust-lang#80392
2 parents 4415af5 + 048a801 commit c7aa270

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

library/std/src/sync/rwlock.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod tests;
44
use crate::cell::UnsafeCell;
55
use crate::fmt;
66
use crate::ops::{Deref, DerefMut};
7+
use crate::ptr::NonNull;
78
use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
89
use crate::sys_common::rwlock as sys;
910

@@ -101,7 +102,12 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
101102
#[stable(feature = "rust1", since = "1.0.0")]
102103
#[clippy::has_significant_drop]
103104
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
104-
lock: &'a RwLock<T>,
105+
// NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a
106+
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
107+
// `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull`
108+
// is preferable over `const* T` to allow for niche optimization.
109+
data: NonNull<T>,
110+
inner_lock: &'a sys::MovableRwLock,
105111
}
106112

107113
#[stable(feature = "rust1", since = "1.0.0")]
@@ -511,12 +517,21 @@ impl<T> From<T> for RwLock<T> {
511517
}
512518

513519
impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
520+
/// Create a new instance of `RwLockReadGuard<T>` from a `RwLock<T>`.
521+
// SAFETY: if and only if `lock.inner.read()` (or `lock.inner.try_read()`) has been
522+
// successfully called from the same thread before instantiating this object.
514523
unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockReadGuard<'rwlock, T>> {
515-
poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard { lock })
524+
poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard {
525+
data: NonNull::new_unchecked(lock.data.get()),
526+
inner_lock: &lock.inner,
527+
})
516528
}
517529
}
518530

519531
impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
532+
/// Create a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`.
533+
// SAFETY: if and only if `lock.inner.write()` (or `lock.inner.try_write()`) has been
534+
// successfully called from the same thread before instantiating this object.
520535
unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> {
521536
poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard { lock, poison: guard })
522537
}
@@ -555,7 +570,8 @@ impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
555570
type Target = T;
556571

557572
fn deref(&self) -> &T {
558-
unsafe { &*self.lock.data.get() }
573+
// SAFETY: the conditions of `RwLockGuard::new` were satisfied when created.
574+
unsafe { self.data.as_ref() }
559575
}
560576
}
561577

@@ -564,22 +580,25 @@ impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
564580
type Target = T;
565581

566582
fn deref(&self) -> &T {
583+
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
567584
unsafe { &*self.lock.data.get() }
568585
}
569586
}
570587

571588
#[stable(feature = "rust1", since = "1.0.0")]
572589
impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
573590
fn deref_mut(&mut self) -> &mut T {
591+
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
574592
unsafe { &mut *self.lock.data.get() }
575593
}
576594
}
577595

578596
#[stable(feature = "rust1", since = "1.0.0")]
579597
impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
580598
fn drop(&mut self) {
599+
// SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created.
581600
unsafe {
582-
self.lock.inner.read_unlock();
601+
self.inner_lock.read_unlock();
583602
}
584603
}
585604
}
@@ -588,6 +607,7 @@ impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
588607
impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> {
589608
fn drop(&mut self) {
590609
self.lock.poison.done(&self.poison);
610+
// SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created.
591611
unsafe {
592612
self.lock.inner.write_unlock();
593613
}

library/std/src/sync/rwlock/tests.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::sync::atomic::{AtomicUsize, Ordering};
22
use crate::sync::mpsc::channel;
3-
use crate::sync::{Arc, RwLock, TryLockError};
3+
use crate::sync::{Arc, RwLock, RwLockReadGuard, TryLockError};
44
use crate::thread;
55
use rand::{self, Rng};
66

@@ -245,3 +245,15 @@ fn test_get_mut_poison() {
245245
Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"),
246246
}
247247
}
248+
249+
#[test]
250+
fn test_read_guard_covariance() {
251+
fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {}
252+
let j: i32 = 5;
253+
let lock = RwLock::new(&j);
254+
{
255+
let i = 6;
256+
do_stuff(lock.read().unwrap(), &i);
257+
}
258+
drop(lock);
259+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// compile-flags: -O -C no-prepopulate-passes -Z mutable-noalias=yes
2+
3+
#![crate_type = "lib"]
4+
5+
use std::sync::{RwLock, RwLockReadGuard};
6+
7+
// Make sure that `RwLockReadGuard` does not get a `noalias` attribute, because
8+
// the `RwLock` might alias writes after it is dropped.
9+
10+
// CHECK-LABEL: @maybe_aliased(
11+
// CHECK-NOT: noalias
12+
// CHECK-SAME: %_data
13+
#[no_mangle]
14+
pub unsafe fn maybe_aliased(_: RwLockReadGuard<'_, i32>, _data: &RwLock<i32>) {}

0 commit comments

Comments
 (0)