Skip to content

Commit c3f9403

Browse files
committed
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.
1 parent 08c5ffd commit c3f9403

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

compiler/rustc_middle/src/query/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,10 @@ rustc_queries! {
993993
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
994994
desc { "computing whether `{}` is freeze", env.value }
995995
}
996+
/// Query backing `TyS::is_unpin`.
997+
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
998+
desc { "computing whether `{}` is `Unpin`", env.value }
999+
}
9961000
/// Query backing `TyS::needs_drop`.
9971001
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
9981002
desc { "computing whether `{}` needs drop", env.value }

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,16 +2318,25 @@ where
23182318
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
23192319
let address_space = addr_space_of_ty(ty);
23202320
let tcx = cx.tcx();
2321-
let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env());
23222321
let kind = match mt {
23232322
hir::Mutability::Not => {
2324-
if is_freeze {
2323+
if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) {
23252324
PointerKind::Frozen
23262325
} else {
23272326
PointerKind::Shared
23282327
}
23292328
}
2330-
hir::Mutability::Mut => PointerKind::UniqueBorrowed,
2329+
hir::Mutability::Mut => {
2330+
// References to self-referential structures should not be considered
2331+
// noalias, as another pointer to the structure can be obtained, that
2332+
// is not based-on the original reference. We consider all !Unpin
2333+
// types to be potentially self-referential here.
2334+
if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) {
2335+
PointerKind::UniqueBorrowed
2336+
} else {
2337+
PointerKind::Shared
2338+
}
2339+
}
23312340
};
23322341

23332342
cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo {

compiler/rustc_middle/src/ty/util.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,46 @@ impl<'tcx> ty::TyS<'tcx> {
741741
}
742742
}
743743

744+
/// Checks whether values of this type `T` implement the `Unpin` trait.
745+
pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
746+
self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self))
747+
}
748+
749+
/// Fast path helper for testing if a type is `Unpin`.
750+
///
751+
/// Returning true means the type is known to be `Unpin`. Returning
752+
/// `false` means nothing -- could be `Unpin`, might not be.
753+
fn is_trivially_unpin(&self) -> bool {
754+
match self.kind() {
755+
ty::Int(_)
756+
| ty::Uint(_)
757+
| ty::Float(_)
758+
| ty::Bool
759+
| ty::Char
760+
| ty::Str
761+
| ty::Never
762+
| ty::Ref(..)
763+
| ty::RawPtr(_)
764+
| ty::FnDef(..)
765+
| ty::Error(_)
766+
| ty::FnPtr(_) => true,
767+
ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin),
768+
ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(),
769+
ty::Adt(..)
770+
| ty::Bound(..)
771+
| ty::Closure(..)
772+
| ty::Dynamic(..)
773+
| ty::Foreign(_)
774+
| ty::Generator(..)
775+
| ty::GeneratorWitness(_)
776+
| ty::Infer(_)
777+
| ty::Opaque(..)
778+
| ty::Param(_)
779+
| ty::Placeholder(_)
780+
| ty::Projection(_) => false,
781+
}
782+
}
783+
744784
/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
745785
/// non-copy and *might* have a destructor attached; if it returns
746786
/// `false`, then `ty` definitely has no destructor (i.e., no drop glue).

compiler/rustc_ty_utils/src/common_traits.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1818
is_item_raw(tcx, query, LangItem::Freeze)
1919
}
2020

21+
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
22+
is_item_raw(tcx, query, LangItem::Unpin)
23+
}
24+
2125
fn is_item_raw<'tcx>(
2226
tcx: TyCtxt<'tcx>,
2327
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -37,5 +41,11 @@ fn is_item_raw<'tcx>(
3741
}
3842

3943
pub(crate) fn provide(providers: &mut ty::query::Providers) {
40-
*providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
44+
*providers = ty::query::Providers {
45+
is_copy_raw,
46+
is_sized_raw,
47+
is_freeze_raw,
48+
is_unpin_raw,
49+
..*providers
50+
};
4151
}

src/test/codegen/noalias-unpin.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// compile-flags: -Z mutable-noalias=yes
2+
3+
#![crate_type = "lib"]
4+
5+
pub struct SelfRef {
6+
self_ref: *mut SelfRef,
7+
_pin: std::marker::PhantomPinned
8+
}
9+
10+
// CHECK-LABEL: @test_self_ref(
11+
// CHECK-NOT: noalias
12+
#[no_mangle]
13+
pub unsafe fn test_self_ref(s: &mut SelfRef) {
14+
(*s.self_ref).self_ref = std::ptr::null_mut();
15+
}

0 commit comments

Comments
 (0)