Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2145,21 +2145,22 @@ pub enum PointerKind {
}

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

Expand Down
19 changes: 13 additions & 6 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_index::IndexVec;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv as _};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::config::OutputFilenames;
use rustc_span::Symbol;
Expand Down Expand Up @@ -924,19 +924,26 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
count,
}) => {
let dst = codegen_operand(fx, dst);
let pointee = dst
.layout()
.pointee_info_at(fx, rustc_abi::Size::ZERO)
.expect("Expected pointer");

let &ty::RawPtr(pointee, _) = dst.layout().ty.kind() else {
bug!("expected pointer")
};
let pointee_layout = fx
.tcx
.layout_of(fx.typing_env().as_query_input(pointee))
.expect("expected pointee to have a layout");
let elem_size: u64 = pointee_layout.layout.size().bytes();

let dst = dst.load_scalar(fx);
let src = codegen_operand(fx, src).load_scalar(fx);
let count = codegen_operand(fx, count).load_scalar(fx);
let elem_size: u64 = pointee.size.bytes();

let bytes = if elem_size != 1 {
fx.bcx.ins().imul_imm(count, elem_size as i64)
} else {
count
};

fx.bcx.call_memcpy(fx.target_config, dst, src, bytes);
}
},
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_gcc/src/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
Float(f) => cx.type_from_float(f),
Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) {
let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset)
&& pointee.align > rustc_abi::Align::ONE
{
cx.type_pointee_for_align(pointee.align)
} else {
cx.type_i8()
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}

if let Some(pointee) = layout.pointee_info_at(bx, offset)
&& let Some(_) = pointee.safe
&& pointee.align > Align::ONE
{
bx.align_metadata(load, pointee.align);
}
Expand Down
20 changes: 13 additions & 7 deletions compiler/rustc_codegen_ssa/src/mir/statement.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
use rustc_middle::span_bug;
use rustc_middle::{bug, span_bug, ty};
use tracing::instrument;

use super::{FunctionCx, LocalRef};
Expand Down Expand Up @@ -77,15 +77,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let dst_val = self.codegen_operand(bx, dst);
let src_val = self.codegen_operand(bx, src);
let count = self.codegen_operand(bx, count).immediate();
let pointee_layout = dst_val
.layout
.pointee_info_at(bx, rustc_abi::Size::ZERO)
.expect("Expected pointer");
let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes()));

let align = pointee_layout.align;
let &ty::RawPtr(pointee, _) = dst_val.layout.ty.kind() else {
bug!("expected pointer")
};
let pointee_layout = bx
.tcx()
.layout_of(bx.typing_env().as_query_input(pointee))
.expect("expected pointee to have a layout");
let elem_size = pointee_layout.layout.size().bytes();
let bytes = bx.mul(count, bx.const_usize(elem_size));

let align = pointee_layout.layout.align.abi;
let dst = dst_val.immediate();
let src = src_val.immediate();

bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None);
}
mir::StatementKind::FakeRead(..)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ language_item_table! {

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

ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::Exact(1);
MaybeDangling, sym::maybe_dangling, maybe_dangling, Target::Struct, GenericRequirement::Exact(1);
BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0);

MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ bitflags::bitflags! {
const IS_PIN_PROJECT = 1 << 12;
/// Indicates whether the type is `FieldRepresentingType`.
const IS_FIELD_REPRESENTING_TYPE = 1 << 13;
/// Indicates whether the type is `MaybeDangling<_>`.
const IS_MAYBE_DANGLING = 1 << 14;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these are mutually exclusive so it is a huge waste of bits to have a separate bit for each of them... 🤷 that's pre-existing though, not for this PR to fix.

}
}
rustc_data_structures::external_bitflags_debug! { AdtFlags }
Expand Down Expand Up @@ -373,6 +375,9 @@ impl AdtDefData {
if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
flags |= AdtFlags::IS_MANUALLY_DROP;
}
if tcx.is_lang_item(did, LangItem::MaybeDangling) {
flags |= AdtFlags::IS_MAYBE_DANGLING;
}
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
flags |= AdtFlags::IS_UNSAFE_CELL;
}
Expand Down Expand Up @@ -500,6 +505,12 @@ impl<'tcx> AdtDef<'tcx> {
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
}

/// Returns `true` if this is `MaybeDangling<T>`.
#[inline]
pub fn is_maybe_dangling(self) -> bool {
self.flags().contains(AdtFlags::IS_MAYBE_DANGLING)
}

/// Returns `true` if this is `Pin<T>`.
#[inline]
pub fn is_pin(self) -> bool {
Expand Down
115 changes: 68 additions & 47 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,41 +1022,80 @@ where
let tcx = cx.tcx();
let typing_env = cx.typing_env();

// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
let optimize = tcx.sess.opts.optimize != OptLevel::No;

let pointee_info = match *this.ty.kind() {
ty::RawPtr(p_ty, _) if offset.bytes() == 0 => {
tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
})
ty::RawPtr(_, _) | ty::FnPtr(..) if offset.bytes() == 0 => {
Some(PointeeInfo { safe: None, size: Size::ZERO, align: Align::ONE })
}
ty::FnPtr(..) if offset.bytes() == 0 => {
tcx.layout_of(typing_env.as_query_input(this.ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| {
let (size, kind);
match mt {
hir::Mutability::Not => {
let frozen = optimize && ty.is_freeze(tcx, typing_env);

// Non-frozen shared references are not necessarily dereferenceable for the entire duration of the function
// (see <https://github.com/rust-lang/rust/pull/98017>)
// (if we had "dereferenceable on entry", we could support this)
size = if frozen { layout.size } else { Size::ZERO };

kind = PointerKind::SharedRef { frozen };
}
hir::Mutability::Mut => {
let unpin = optimize
&& ty.is_unpin(tcx, typing_env)
&& ty.is_unsafe_unpin(tcx, typing_env);

// Mutable references to potentially self-referential types are not
// necessarily dereferenceable for the entire duration of the function
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>)
// (if we had "dereferenceable on entry", we could support this)
size = if unpin { layout.size } else { Size::ZERO };

kind = PointerKind::MutableRef { unpin };
}
};
PointeeInfo { safe: Some(kind), size, align: layout.align.abi }
})
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
let optimize = tcx.sess.opts.optimize != OptLevel::No;
let kind = match mt {
hir::Mutability::Not => {
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
}
hir::Mutability::Mut => PointerKind::MutableRef {

ty::Adt(..)
if offset.bytes() == 0
&& let Some(pointee) = this.ty.boxed_ty() =>
{
tcx.layout_of(typing_env.as_query_input(pointee)).ok().map(|layout| PointeeInfo {
safe: Some(PointerKind::Box {
// Same logic as for mutable references above.
unpin: optimize
&& ty.is_unpin(tcx, typing_env)
&& ty.is_unsafe_unpin(tcx, typing_env),
},
};
&& pointee.is_unpin(tcx, typing_env)
&& pointee.is_unsafe_unpin(tcx, typing_env),
global: this.ty.is_box_global(tcx),
}),

// `Box` are not necessarily dereferenceable for the entire duration of the function as
// they can be deallocated at any time.
// (if we had "dereferenceable on entry", we could support this)
size: Size::ZERO,

tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
})
}

ty::Adt(adt_def, ..) if adt_def.is_maybe_dangling() => {
Self::ty_and_layout_pointee_info_at(this.field(cx, 0), cx, offset).map(|info| {
PointeeInfo {
// Mark the pointer as raw
// (thus removing noalias/readonly/etc in case of the llvm backend)
safe: None,
// Make sure we don't assert dereferenceability of the pointer.
size: Size::ZERO,
// Preserve the alignment assertion! That is required even inside `MaybeDangling`.
align: info.align,
}
})
}

Expand Down Expand Up @@ -1098,7 +1137,7 @@ where
}
}
Variants::Multiple { .. } => None,
_ => Some(this),
Variants::Empty | Variants::Single { .. } => Some(this),
};

if let Some(variant) = data_variant
Expand Down Expand Up @@ -1135,24 +1174,6 @@ where
}
}

// Fixup info for the first field of a `Box`. Recursive traversal will have found
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
// will still be `None`.
if let Some(ref mut pointee) = result {
if offset.bytes() == 0
&& let Some(boxed_ty) = this.ty.boxed_ty()
{
debug_assert!(pointee.safe.is_none());
let optimize = tcx.sess.opts.optimize != OptLevel::No;
pointee.safe = Some(PointerKind::Box {
unpin: optimize
&& boxed_ty.is_unpin(tcx, typing_env)
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
global: this.ty.is_box_global(tcx),
});
}
}

result
}
};
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,7 @@ symbols! {
maxnumf128,
may_dangle,
may_unwind,
maybe_dangling,
maybe_uninit,
maybe_uninit_uninit,
maybe_uninit_zeroed,
Expand Down
Loading