Skip to content

Commit 64b72a1

Browse files
committed
Auto merge of rust-lang#150447 - WaffleLapkin:maybe-dangling-semantics, r=RalfJung
Implement `MaybeDangling` compiler support Tracking issue: rust-lang#118166 cc @RalfJung
2 parents 70d86e3 + d6ca5c3 commit 64b72a1

17 files changed

Lines changed: 230 additions & 131 deletions

File tree

compiler/rustc_abi/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,21 +2145,22 @@ pub enum PointerKind {
21452145
}
21462146

21472147
/// Encodes extra information we have about a pointer.
2148+
///
21482149
/// Note that this information is advisory only, and backends are free to ignore it:
21492150
/// if the information is wrong, that can cause UB, but if the information is absent,
21502151
/// that must always be okay.
21512152
#[derive(Copy, Clone, Debug)]
21522153
pub struct PointeeInfo {
2153-
/// If this is `None`, then this is a raw pointer, so size and alignment are not guaranteed to
2154-
/// be reliable.
2154+
/// If this is `None`, then this is a raw pointer.
21552155
pub safe: Option<PointerKind>,
2156-
/// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes.
2156+
/// If `size` is not zero, then the pointer is either null or dereferenceable for this many bytes
2157+
/// (independent of `safe`).
2158+
///
21572159
/// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration
21582160
/// of this function call", i.e. it is UB for the memory that this pointer points to be freed
21592161
/// while this function is still running.
2160-
/// The size can be zero if the pointer is not dereferenceable.
21612162
pub size: Size,
2162-
/// If `safe` is `Some`, then the pointer is aligned as indicated.
2163+
/// The pointer is guaranteed to be aligned this much (independent of `safe`).
21632164
pub align: Align,
21642165
}
21652166

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_data_structures::profiling::SelfProfilerRef;
1010
use rustc_index::IndexVec;
1111
use rustc_middle::ty::TypeVisitableExt;
1212
use rustc_middle::ty::adjustment::PointerCoercion;
13-
use rustc_middle::ty::layout::FnAbiOf;
13+
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _};
1414
use rustc_middle::ty::print::with_no_trimmed_paths;
1515
use rustc_session::config::OutputFilenames;
1616
use rustc_span::Symbol;
@@ -924,19 +924,26 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
924924
count,
925925
}) => {
926926
let dst = codegen_operand(fx, dst);
927-
let pointee = dst
928-
.layout()
929-
.pointee_info_at(fx, rustc_abi::Size::ZERO)
930-
.expect("Expected pointer");
927+
928+
let &ty::RawPtr(pointee, _) = dst.layout().ty.kind() else {
929+
bug!("expected pointer")
930+
};
931+
let pointee_layout = fx
932+
.tcx
933+
.layout_of(fx.typing_env().as_query_input(pointee))
934+
.expect("expected pointee to have a layout");
935+
let elem_size: u64 = pointee_layout.layout.size().bytes();
936+
931937
let dst = dst.load_scalar(fx);
932938
let src = codegen_operand(fx, src).load_scalar(fx);
933939
let count = codegen_operand(fx, count).load_scalar(fx);
934-
let elem_size: u64 = pointee.size.bytes();
940+
935941
let bytes = if elem_size != 1 {
936942
fx.bcx.ins().imul_imm(count, elem_size as i64)
937943
} else {
938944
count
939945
};
946+
940947
fx.bcx.call_memcpy(fx.target_config, dst, src, bytes);
941948
}
942949
},

compiler/rustc_codegen_gcc/src/type_of.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
288288
Float(f) => cx.type_from_float(f),
289289
Pointer(address_space) => {
290290
// If we know the alignment, pick something better than i8.
291-
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) {
291+
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset)
292+
&& pointee.align > rustc_abi::Align::ONE
293+
{
292294
cx.type_pointee_for_align(pointee.align)
293295
} else {
294296
cx.type_i8()

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
715715
}
716716

717717
if let Some(pointee) = layout.pointee_info_at(bx, offset)
718-
&& let Some(_) = pointee.safe
718+
&& pointee.align > Align::ONE
719719
{
720720
bx.align_metadata(load, pointee.align);
721721
}

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
2-
use rustc_middle::span_bug;
2+
use rustc_middle::{bug, span_bug, ty};
33
use tracing::instrument;
44

55
use super::{FunctionCx, LocalRef};
@@ -77,15 +77,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
7777
let dst_val = self.codegen_operand(bx, dst);
7878
let src_val = self.codegen_operand(bx, src);
7979
let count = self.codegen_operand(bx, count).immediate();
80-
let pointee_layout = dst_val
81-
.layout
82-
.pointee_info_at(bx, rustc_abi::Size::ZERO)
83-
.expect("Expected pointer");
84-
let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes()));
8580

86-
let align = pointee_layout.align;
81+
let &ty::RawPtr(pointee, _) = dst_val.layout.ty.kind() else {
82+
bug!("expected pointer")
83+
};
84+
let pointee_layout = bx
85+
.tcx()
86+
.layout_of(bx.typing_env().as_query_input(pointee))
87+
.expect("expected pointee to have a layout");
88+
let elem_size = pointee_layout.layout.size().bytes();
89+
let bytes = bx.mul(count, bx.const_usize(elem_size));
90+
91+
let align = pointee_layout.layout.align.abi;
8792
let dst = dst_val.immediate();
8893
let src = src_val.immediate();
94+
8995
bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None);
9096
}
9197
mir::StatementKind::FakeRead(..)

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,8 @@ language_item_table! {
341341

342342
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
343343

344-
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
344+
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1);
345+
MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1);
345346
BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0);
346347

347348
MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;

compiler/rustc_middle/src/ty/adt.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ bitflags::bitflags! {
6262
const IS_PIN_PROJECT = 1 << 12;
6363
/// Indicates whether the type is `FieldRepresentingType`.
6464
const IS_FIELD_REPRESENTING_TYPE = 1 << 13;
65+
/// Indicates whether the type is `MaybeDangling<_>`.
66+
const IS_MAYBE_DANGLING = 1 << 14;
6567
}
6668
}
6769
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -373,6 +375,9 @@ impl AdtDefData {
373375
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
374376
flags |= AdtFlags::IS_MANUALLY_DROP;
375377
}
378+
if tcx.is_lang_item(did, LangItem::MaybeDangling) {
379+
flags |= AdtFlags::IS_MAYBE_DANGLING;
380+
}
376381
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
377382
flags |= AdtFlags::IS_UNSAFE_CELL;
378383
}
@@ -500,6 +505,12 @@ impl<'tcx> AdtDef<'tcx> {
500505
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
501506
}
502507

508+
/// Returns `true` if this is `MaybeDangling<T>`.
509+
#[inline]
510+
pub fn is_maybe_dangling(self) -> bool {
511+
self.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
512+
}
513+
503514
/// Returns `true` if this is `Pin<T>`.
504515
#[inline]
505516
pub fn is_pin(self) -> bool {

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,41 +1022,80 @@ where
10221022
let tcx = cx.tcx();
10231023
let typing_env = cx.typing_env();
10241024

1025+
// Use conservative pointer kind if not optimizing. This saves us the
1026+
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
1027+
// attributes in LLVM have compile-time cost even in unoptimized builds).
1028+
let optimize = tcx.sess.opts.optimize != OptLevel::No;
1029+
10251030
let pointee_info = match *this.ty.kind() {
1026-
ty::RawPtr(p_ty, _) if offset.bytes() == 0 => {
1027-
tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo {
1028-
size: layout.size,
1029-
align: layout.align.abi,
1030-
safe: None,
1031-
})
1031+
ty::RawPtr(_, _) | ty::FnPtr(..) if offset.bytes() == 0 => {
1032+
Some(PointeeInfo { safe: None, size: Size::ZERO, align: Align::ONE })
10321033
}
1033-
ty::FnPtr(..) if offset.bytes() == 0 => {
1034-
tcx.layout_of(typing_env.as_query_input(this.ty)).ok().map(|layout| PointeeInfo {
1035-
size: layout.size,
1036-
align: layout.align.abi,
1037-
safe: None,
1034+
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
1035+
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| {
1036+
let (size, kind);
1037+
match mt {
1038+
hir::Mutability::Not => {
1039+
let frozen = optimize && ty.is_freeze(tcx, typing_env);
1040+
1041+
// Non-frozen shared references are not necessarily dereferenceable for the entire duration of the function
1042+
// (see <https://github.com/rust-lang/rust/pull/98017>)
1043+
// (if we had "dereferenceable on entry", we could support this)
1044+
size = if frozen { layout.size } else { Size::ZERO };
1045+
1046+
kind = PointerKind::SharedRef { frozen };
1047+
}
1048+
hir::Mutability::Mut => {
1049+
let unpin = optimize
1050+
&& ty.is_unpin(tcx, typing_env)
1051+
&& ty.is_unsafe_unpin(tcx, typing_env);
1052+
1053+
// Mutable references to potentially self-referential types are not
1054+
// necessarily dereferenceable for the entire duration of the function
1055+
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>)
1056+
// (if we had "dereferenceable on entry", we could support this)
1057+
size = if unpin { layout.size } else { Size::ZERO };
1058+
1059+
kind = PointerKind::MutableRef { unpin };
1060+
}
1061+
};
1062+
PointeeInfo { safe: Some(kind), size, align: layout.align.abi }
10381063
})
10391064
}
1040-
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
1041-
// Use conservative pointer kind if not optimizing. This saves us the
1042-
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
1043-
// attributes in LLVM have compile-time cost even in unoptimized builds).
1044-
let optimize = tcx.sess.opts.optimize != OptLevel::No;
1045-
let kind = match mt {
1046-
hir::Mutability::Not => {
1047-
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
1048-
}
1049-
hir::Mutability::Mut => PointerKind::MutableRef {
1065+
1066+
ty::Adt(..)
1067+
if offset.bytes() == 0
1068+
&& let Some(pointee) = this.ty.boxed_ty() =>
1069+
{
1070+
tcx.layout_of(typing_env.as_query_input(pointee)).ok().map(|layout| PointeeInfo {
1071+
safe: Some(PointerKind::Box {
1072+
// Same logic as for mutable references above.
10501073
unpin: optimize
1051-
&& ty.is_unpin(tcx, typing_env)
1052-
&& ty.is_unsafe_unpin(tcx, typing_env),
1053-
},
1054-
};
1074+
&& pointee.is_unpin(tcx, typing_env)
1075+
&& pointee.is_unsafe_unpin(tcx, typing_env),
1076+
global: this.ty.is_box_global(tcx),
1077+
}),
1078+
1079+
// `Box` are not necessarily dereferenceable for the entire duration of the function as
1080+
// they can be deallocated at any time.
1081+
// (if we had "dereferenceable on entry", we could support this)
1082+
size: Size::ZERO,
10551083

1056-
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
1057-
size: layout.size,
10581084
align: layout.align.abi,
1059-
safe: Some(kind),
1085+
})
1086+
}
1087+
1088+
ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => {
1089+
Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset).map(|info| {
1090+
PointeeInfo {
1091+
// Mark the pointer as raw
1092+
// (thus removing noalias/readonly/etc in case of the llvm backend)
1093+
safe: None,
1094+
// Make sure we don't assert dereferenceability of the pointer.
1095+
size: Size::ZERO,
1096+
// Preserve the alignment assertion! That is required even inside `MaybeDangling`.
1097+
align: info.align,
1098+
}
10601099
})
10611100
}
10621101

@@ -1098,7 +1137,7 @@ where
10981137
}
10991138
}
11001139
Variants::Multiple { .. } => None,
1101-
_ => Some(this),
1140+
Variants::Empty | Variants::Single { .. } => Some(this),
11021141
};
11031142

11041143
if let Some(variant) = data_variant
@@ -1135,24 +1174,6 @@ where
11351174
}
11361175
}
11371176

1138-
// Fixup info for the first field of a `Box`. Recursive traversal will have found
1139-
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
1140-
// will still be `None`.
1141-
if let Some(ref mut pointee) = result {
1142-
if offset.bytes() == 0
1143-
&& let Some(boxed_ty) = this.ty.boxed_ty()
1144-
{
1145-
debug_assert!(pointee.safe.is_none());
1146-
let optimize = tcx.sess.opts.optimize != OptLevel::No;
1147-
pointee.safe = Some(PointerKind::Box {
1148-
unpin: optimize
1149-
&& boxed_ty.is_unpin(tcx, typing_env)
1150-
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
1151-
global: this.ty.is_box_global(tcx),
1152-
});
1153-
}
1154-
}
1155-
11561177
result
11571178
}
11581179
};

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,7 @@ symbols! {
12171217
maxnumf128,
12181218
may_dangle,
12191219
may_unwind,
1220+
maybe_dangling,
12201221
maybe_uninit,
12211222
maybe_uninit_uninit,
12221223
maybe_uninit_zeroed,

0 commit comments

Comments
 (0)