Don't consider !Unpin references as noalias

Such structures may contain self-references, in which case the
same location may be accessible through a pointer that is not
based-on the noalias pointer.

This is still grey area as far as language semantics are concerned,
but checking for !Unpin as an indicator for self-referential
sturctures seems like a good approach for the meantime.
This commit is contained in:
Nikita Popov 2021-03-18 22:44:36 +01:00
parent 08c5ffd4a3
commit c3f9403f59
5 changed files with 82 additions and 4 deletions

View File

@ -993,6 +993,10 @@
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is freeze", env.value }
}
/// Query backing `TyS::is_unpin`.
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Unpin`", env.value }
}
/// Query backing `TyS::needs_drop`.
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` needs drop", env.value }

View File

@ -2318,16 +2318,25 @@ fn pointee_info_at(this: TyAndLayout<'tcx>, cx: &C, offset: Size) -> Option<Poin
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
let address_space = addr_space_of_ty(ty);
let tcx = cx.tcx();
let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env());
let kind = match mt {
hir::Mutability::Not => {
if is_freeze {
if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) {
PointerKind::Frozen
} else {
PointerKind::Shared
}
}
hir::Mutability::Mut => PointerKind::UniqueBorrowed,
hir::Mutability::Mut => {
// References to self-referential structures should not be considered
// noalias, as another pointer to the structure can be obtained, that
// is not based-on the original reference. We consider all !Unpin
// types to be potentially self-referential here.
if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) {
PointerKind::UniqueBorrowed
} else {
PointerKind::Shared
}
}
};
cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo {

View File

@ -741,6 +741,46 @@ fn is_trivially_freeze(&self) -> bool {
}
}
/// Checks whether values of this type `T` implement the `Unpin` trait.
pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self))
}
/// Fast path helper for testing if a type is `Unpin`.
///
/// Returning true means the type is known to be `Unpin`. Returning
/// `false` means nothing -- could be `Unpin`, might not be.
fn is_trivially_unpin(&self) -> bool {
match self.kind() {
ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Bool
| ty::Char
| ty::Str
| ty::Never
| ty::Ref(..)
| ty::RawPtr(_)
| ty::FnDef(..)
| ty::Error(_)
| ty::FnPtr(_) => true,
ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin),
ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(),
ty::Adt(..)
| ty::Bound(..)
| ty::Closure(..)
| ty::Dynamic(..)
| ty::Foreign(_)
| ty::Generator(..)
| ty::GeneratorWitness(_)
| ty::Infer(_)
| ty::Opaque(..)
| ty::Param(_)
| ty::Placeholder(_)
| ty::Projection(_) => false,
}
}
/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
/// non-copy and *might* have a destructor attached; if it returns
/// `false`, then `ty` definitely has no destructor (i.e., no drop glue).

View File

@ -18,6 +18,10 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
is_item_raw(tcx, query, LangItem::Freeze)
}
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Unpin)
}
fn is_item_raw<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@ -37,5 +41,11 @@ fn is_item_raw<'tcx>(
}
pub(crate) fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
*providers = ty::query::Providers {
is_copy_raw,
is_sized_raw,
is_freeze_raw,
is_unpin_raw,
..*providers
};
}

View File

@ -0,0 +1,15 @@
// compile-flags: -Z mutable-noalias=yes
#![crate_type = "lib"]
pub struct SelfRef {
self_ref: *mut SelfRef,
_pin: std::marker::PhantomPinned
}
// CHECK-LABEL: @test_self_ref(
// CHECK-NOT: noalias
#[no_mangle]
pub unsafe fn test_self_ref(s: &mut SelfRef) {
(*s.self_ref).self_ref = std::ptr::null_mut();
}