Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
073e649
Add xdg_basedir API
CAD97 Jun 6, 2026
9534d0f
update module doc comment after review
CAD97 Jun 8, 2026
2b3d273
reword xdg::cache_home_dir doc per review
CAD97 Jun 8, 2026
7b46306
remove unnecessary helper generics
CAD97 Jun 8, 2026
593a847
reduce byte shuffling in XdgDirsIter
CAD97 Jun 8, 2026
6e670de
remove unused import
CAD97 Jun 8, 2026
378da80
Remove redundant word in doc comment
CAD97 Jun 11, 2026
dea3ed8
Added initial support for building a `RetagPlan`.
icmccorm Jun 12, 2026
d050769
Match expressions had not been updated to also show the match_source,…
AlexCeleste Jun 11, 2026
f6e99c1
Rename `rustc_hir_analysis/src/errors.rs` into `rustc_hir_analysis/sr…
GuillaumeGomez Jun 11, 2026
579cac2
Rename `rustc_infer/src/errors.rs` into `rustc_infer/src/diagnostics.rs`
GuillaumeGomez Jun 13, 2026
d4b2610
Rename `rustc_interface/src/errors.rs` into `rustc_interface/src/diag…
GuillaumeGomez Jun 13, 2026
190a22d
Rename `rustc_hir_typeck/src/errors.rs` into `rustc_hir_typeck/src/di…
GuillaumeGomez Jun 11, 2026
ac80064
Rollup merge of #157518 - CAD97:xdg_basedir, r=aapoalas
JonathanBrouwer Jun 13, 2026
04724ff
Rollup merge of #157752 - GuillaumeGomez:rename-err-to-diag-3, r=Jona…
JonathanBrouwer Jun 13, 2026
f889de2
Rollup merge of #157769 - AlexCeleste:thir-missing-match-source, r=Na…
JonathanBrouwer Jun 13, 2026
fafa5b2
Rollup merge of #157825 - BorrowSanitizer:codegen-emit-retag-3, r=sae…
JonathanBrouwer Jun 13, 2026
6b8ad08
Rollup merge of #157861 - GuillaumeGomez:rename-err-to-diag, r=Jonath…
JonathanBrouwer Jun 13, 2026
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
165 changes: 165 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/retag.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
//! Experimental support for emitting retags as function calls in generated code.
//!
//! We attempt to retag every argument and return value of a function, and every rvalue
//! of an assignment. The first step to retagging is to generate a [`RetagPlan`], which
//! describes which pointers within the place or operand can be retagged.

#![allow(unused)]
use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, VariantIdx, Variants};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxIndexMap;
use rustc_middle::mir::{Rvalue, WithRetag};
use rustc_middle::ty;
use rustc_middle::ty::layout::TyAndLayout;

use crate::RetagInfo;
use crate::mir::FunctionCx;
use crate::mir::operand::OperandRef;
use crate::mir::place::PlaceRef;
Expand All @@ -12,6 +23,160 @@ pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool {
!matches!(rvalue, Rvalue::Ref(..)) && !matches!(rvalue, Rvalue::Use(.., WithRetag::No))
}

/// A description of the pointers within a type that need to be retagged.
#[derive(Debug)]
enum RetagPlan<V> {
/// Indicates that a pointer should be retagged.
EmitRetag(RetagInfo<V>),
/// Indicates that one or more fields or variants of this type
/// contain pointers that need to be retagged.
Recurse {
field_plans: FxIndexMap<FieldIdx, RetagPlan<V>>,
variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
},
}

impl<V> RetagPlan<V> {
/// A helper function to move a [`RetagPlan`] into a particular field.
fn for_field(self, ix: FieldIdx) -> Self {
let mut field_plans = FxIndexMap::default();
field_plans.insert(ix, self);
RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }
}
}

impl<'a, 'tcx, V> RetagPlan<V> {
/// Attempts to create a [`RetagPlan`] for a place or operand with the given layout.
fn build<Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
is_fn_entry: bool,
) -> Option<RetagPlan<Bx::Value>> {
// If the value being retagged is smaller than a pointer, then it can't contain any
// pointers we need to retag, so we can stop recursion early. This optimization is
// crucial for ZSTs, because they can contain way more fields than we can ever visit.
if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
return None;
}
// SIMD vectors may only contain raw pointers, integers, and floating point values,
// which do not need to be retagged.
if matches!(layout.backend_repr, BackendRepr::SimdVector { .. }) {
return None;
}

// Check the type of this value to see what to do with it (retag, or recurse).
match layout.ty.kind() {
&ty::Ref(_, pointee, mt) => {
let pointee_layout = bx.layout_of(pointee);
Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
}
&ty::RawPtr(_, _) => None,
// `Box` needs special handling, since the innermost pointer is what gets retagged, but
// the outermost `Box` is what determines the permission that gets created.
ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
// Skip traversing for everything inside of `MaybeDangling`
ty::Adt(adt, _) if adt.is_maybe_dangling() => None,
_ => Self::walk_value(bx, layout, is_fn_entry),
}
}

/// Recurses through the fields and variants of a value in memory order to create a [`RetagPlan`].
fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
is_fn_entry: bool,
) -> Option<RetagPlan<Bx::Value>> {
let mut field_plans = FxIndexMap::default();
let mut variant_plans = FxIndexMap::default();

match &layout.fields {
FieldsShape::Union(_) | FieldsShape::Primitive => {}
_ => {
for ix in layout.fields.index_by_increasing_offset() {
let field_layout = layout.field(bx, ix);
if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
field_plans.insert(FieldIdx::from_usize(ix), plan);
}
}
}
}

match &layout.variants {
Variants::Single { .. } | Variants::Empty => {}
Variants::Multiple { variants, .. } => {
for ix in variants.indices() {
let variant_layout = layout.for_variant(bx, ix);
if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
variant_plans.insert(ix, plan);
}
}
}
}

(!field_plans.is_empty() || !variant_plans.is_empty())
.then(|| RetagPlan::Recurse { field_plans, variant_plans })
}

/// Emits a retag for a `Box`.
fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
is_fn_entry: bool,
) -> Option<RetagPlan<Bx::Value>> {
assert!(layout.ty.is_box());
assert_eq!(layout.fields.count(), 2, "`Box` must have exactly 2 fields");
let mut field_plans = FxIndexMap::default();

// Only retag the inner pointer of a `Box` if it came from the global allocator.
if layout.ty.is_box_global(bx.tcx()) {
let boxed_ty = layout.ty.expect_boxed_ty();
let boxed_layout = bx.layout_of(boxed_ty);
if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
// `Unique<T>`
let unique = layout.field(bx, 0);
assert_eq!(unique.fields.count(), 2);
plan = plan.for_field(FieldIdx::ZERO);

// `NonNull<T>`
let nonnull = unique.field(bx, 0);
assert_eq!(nonnull.fields.count(), 1);
plan = plan.for_field(FieldIdx::ZERO);

// `*mut T is !null`
let pattern = nonnull.field(bx, 0);
let ty::Pat(base, _) = pattern.ty.kind() else {
unreachable!("`NonNull` should contain a pattern type")
};
assert_eq!(base.builtin_deref(true), Some(boxed_ty));

field_plans.insert(FieldIdx::ZERO, plan);
}
}

// We always try to retag the second field (the allocator)
let field_layout = layout.field(bx, 1);
if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
field_plans.insert(FieldIdx::ONE, plan);
}

(!field_plans.is_empty())
.then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() })
}

/// Determines if a pointer needs to be retagged, when it points to
/// a type with the given layout. Returns `None` for mutable pointers
/// to types that are entirely covered by `UnsafePinned`, for which retags
/// are a no-op.
fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
_bx: &mut Bx,
_pointee_layout: TyAndLayout<'tcx>,
_ptr_kind: Option<Mutability>,
_is_fn_entry: bool,
) -> Option<RetagPlan<Bx::Value>> {
None
}
}

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
/// Retags the pointers within an [`OperandRef`].
pub(crate) fn codegen_retag_operand(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument};

use crate::errors::AutoDerefReachedRecursionLimit;
use crate::diagnostics::AutoDerefReachedRecursionLimit;
use crate::traits;
use crate::traits::query::evaluate_obligation::InferCtxtExt;

Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_hir_analysis/src/check/always_applicable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_span::sym;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCtxt};

use crate::errors;
use crate::diagnostics;
use crate::hir::def_id::{DefId, LocalDefId};

/// This function confirms that the `Drop` implementation identified by
Expand All @@ -42,12 +42,12 @@ pub(crate) fn check_drop_impl(
match tcx.impl_polarity(drop_impl_did) {
ty::ImplPolarity::Positive => {}
ty::ImplPolarity::Negative => {
return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Negative {
return Err(tcx.dcx().emit_err(diagnostics::DropImplPolarity::Negative {
span: tcx.def_span(drop_impl_did),
}));
}
ty::ImplPolarity::Reservation => {
return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Reservation {
return Err(tcx.dcx().emit_err(diagnostics::DropImplPolarity::Reservation {
span: tcx.def_span(drop_impl_did),
}));
}
Expand Down Expand Up @@ -393,7 +393,7 @@ fn check_drop_xor_pin_drop<'tcx>(
match (drop_span, pin_drop_span) {
(None, None) => {
if tcx.features().pin_ergonomics() {
return Err(tcx.dcx().emit_err(crate::errors::MissingOneOfTraitItem {
return Err(tcx.dcx().emit_err(crate::diagnostics::MissingOneOfTraitItem {
span: tcx.def_span(drop_impl_did),
note: None,
missing_items_msg: "drop`, `pin_drop".to_string(),
Expand All @@ -408,7 +408,7 @@ fn check_drop_xor_pin_drop<'tcx>(
if tcx.adt_def(adt_def_id).is_pin_project() {
let pin_v2_span = rustc_hir::find_attr!(tcx, adt_def_id, PinV2(attr) => *attr);
let adt_name = tcx.item_name(adt_def_id);
return Err(tcx.dcx().emit_err(crate::errors::PinV2WithoutPinDrop {
return Err(tcx.dcx().emit_err(crate::diagnostics::PinV2WithoutPinDrop {
span,
pin_v2_span,
adt_name,
Expand All @@ -424,7 +424,7 @@ fn check_drop_xor_pin_drop<'tcx>(
}
}
(Some(drop_span), Some(pin_drop_span)) => {
return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop {
return Err(tcx.dcx().emit_err(crate::diagnostics::ConflictImplDropAndPinDrop {
span: tcx.def_span(drop_impl_did),
drop_span,
pin_drop_span,
Expand Down
Loading
Loading