Skip to content

Commit e7ef22d

Browse files
committed
Auto merge of #157863 - JonathanBrouwer:rollup-YNzUbyz, r=JonathanBrouwer
Rollup of 5 pull requests Successful merges: - #157518 (Add xdg_basedir API) - #157752 (Rename `errors.rs` file to `diagnostics.rs` (6/N)) - #157769 (Match expressions had not been updated to also show the match_source,…) - #157825 (Emit retags in codegen to support BorrowSanitizer (part 3)) - #157861 (Rename `errors.rs` file to `diagnostics.rs` (7/N))
2 parents 8d6b380 + 6b8ad08 commit e7ef22d

67 files changed

Lines changed: 2235 additions & 370 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_codegen_ssa/src/mir/retag.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
//! Experimental support for emitting retags as function calls in generated code.
2+
//!
3+
//! We attempt to retag every argument and return value of a function, and every rvalue
4+
//! of an assignment. The first step to retagging is to generate a [`RetagPlan`], which
5+
//! describes which pointers within the place or operand can be retagged.
26
7+
#![allow(unused)]
8+
use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, VariantIdx, Variants};
9+
use rustc_ast::Mutability;
10+
use rustc_data_structures::fx::FxIndexMap;
311
use rustc_middle::mir::{Rvalue, WithRetag};
12+
use rustc_middle::ty;
13+
use rustc_middle::ty::layout::TyAndLayout;
414

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

26+
/// A description of the pointers within a type that need to be retagged.
27+
#[derive(Debug)]
28+
enum RetagPlan<V> {
29+
/// Indicates that a pointer should be retagged.
30+
EmitRetag(RetagInfo<V>),
31+
/// Indicates that one or more fields or variants of this type
32+
/// contain pointers that need to be retagged.
33+
Recurse {
34+
field_plans: FxIndexMap<FieldIdx, RetagPlan<V>>,
35+
variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
36+
},
37+
}
38+
39+
impl<V> RetagPlan<V> {
40+
/// A helper function to move a [`RetagPlan`] into a particular field.
41+
fn for_field(self, ix: FieldIdx) -> Self {
42+
let mut field_plans = FxIndexMap::default();
43+
field_plans.insert(ix, self);
44+
RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }
45+
}
46+
}
47+
48+
impl<'a, 'tcx, V> RetagPlan<V> {
49+
/// Attempts to create a [`RetagPlan`] for a place or operand with the given layout.
50+
fn build<Bx: BuilderMethods<'a, 'tcx>>(
51+
bx: &mut Bx,
52+
layout: TyAndLayout<'tcx>,
53+
is_fn_entry: bool,
54+
) -> Option<RetagPlan<Bx::Value>> {
55+
// If the value being retagged is smaller than a pointer, then it can't contain any
56+
// pointers we need to retag, so we can stop recursion early. This optimization is
57+
// crucial for ZSTs, because they can contain way more fields than we can ever visit.
58+
if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
59+
return None;
60+
}
61+
// SIMD vectors may only contain raw pointers, integers, and floating point values,
62+
// which do not need to be retagged.
63+
if matches!(layout.backend_repr, BackendRepr::SimdVector { .. }) {
64+
return None;
65+
}
66+
67+
// Check the type of this value to see what to do with it (retag, or recurse).
68+
match layout.ty.kind() {
69+
&ty::Ref(_, pointee, mt) => {
70+
let pointee_layout = bx.layout_of(pointee);
71+
Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
72+
}
73+
&ty::RawPtr(_, _) => None,
74+
// `Box` needs special handling, since the innermost pointer is what gets retagged, but
75+
// the outermost `Box` is what determines the permission that gets created.
76+
ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
77+
// Skip traversing for everything inside of `MaybeDangling`
78+
ty::Adt(adt, _) if adt.is_maybe_dangling() => None,
79+
_ => Self::walk_value(bx, layout, is_fn_entry),
80+
}
81+
}
82+
83+
/// Recurses through the fields and variants of a value in memory order to create a [`RetagPlan`].
84+
fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
85+
bx: &mut Bx,
86+
layout: TyAndLayout<'tcx>,
87+
is_fn_entry: bool,
88+
) -> Option<RetagPlan<Bx::Value>> {
89+
let mut field_plans = FxIndexMap::default();
90+
let mut variant_plans = FxIndexMap::default();
91+
92+
match &layout.fields {
93+
FieldsShape::Union(_) | FieldsShape::Primitive => {}
94+
_ => {
95+
for ix in layout.fields.index_by_increasing_offset() {
96+
let field_layout = layout.field(bx, ix);
97+
if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
98+
field_plans.insert(FieldIdx::from_usize(ix), plan);
99+
}
100+
}
101+
}
102+
}
103+
104+
match &layout.variants {
105+
Variants::Single { .. } | Variants::Empty => {}
106+
Variants::Multiple { variants, .. } => {
107+
for ix in variants.indices() {
108+
let variant_layout = layout.for_variant(bx, ix);
109+
if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
110+
variant_plans.insert(ix, plan);
111+
}
112+
}
113+
}
114+
}
115+
116+
(!field_plans.is_empty() || !variant_plans.is_empty())
117+
.then(|| RetagPlan::Recurse { field_plans, variant_plans })
118+
}
119+
120+
/// Emits a retag for a `Box`.
121+
fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
122+
bx: &mut Bx,
123+
layout: TyAndLayout<'tcx>,
124+
is_fn_entry: bool,
125+
) -> Option<RetagPlan<Bx::Value>> {
126+
assert!(layout.ty.is_box());
127+
assert_eq!(layout.fields.count(), 2, "`Box` must have exactly 2 fields");
128+
let mut field_plans = FxIndexMap::default();
129+
130+
// Only retag the inner pointer of a `Box` if it came from the global allocator.
131+
if layout.ty.is_box_global(bx.tcx()) {
132+
let boxed_ty = layout.ty.expect_boxed_ty();
133+
let boxed_layout = bx.layout_of(boxed_ty);
134+
if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
135+
// `Unique<T>`
136+
let unique = layout.field(bx, 0);
137+
assert_eq!(unique.fields.count(), 2);
138+
plan = plan.for_field(FieldIdx::ZERO);
139+
140+
// `NonNull<T>`
141+
let nonnull = unique.field(bx, 0);
142+
assert_eq!(nonnull.fields.count(), 1);
143+
plan = plan.for_field(FieldIdx::ZERO);
144+
145+
// `*mut T is !null`
146+
let pattern = nonnull.field(bx, 0);
147+
let ty::Pat(base, _) = pattern.ty.kind() else {
148+
unreachable!("`NonNull` should contain a pattern type")
149+
};
150+
assert_eq!(base.builtin_deref(true), Some(boxed_ty));
151+
152+
field_plans.insert(FieldIdx::ZERO, plan);
153+
}
154+
}
155+
156+
// We always try to retag the second field (the allocator)
157+
let field_layout = layout.field(bx, 1);
158+
if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
159+
field_plans.insert(FieldIdx::ONE, plan);
160+
}
161+
162+
(!field_plans.is_empty())
163+
.then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() })
164+
}
165+
166+
/// Determines if a pointer needs to be retagged, when it points to
167+
/// a type with the given layout. Returns `None` for mutable pointers
168+
/// to types that are entirely covered by `UnsafePinned`, for which retags
169+
/// are a no-op.
170+
fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
171+
_bx: &mut Bx,
172+
_pointee_layout: TyAndLayout<'tcx>,
173+
_ptr_kind: Option<Mutability>,
174+
_is_fn_entry: bool,
175+
) -> Option<RetagPlan<Bx::Value>> {
176+
None
177+
}
178+
}
179+
15180
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
16181
/// Retags the pointers within an [`OperandRef`].
17182
pub(crate) fn codegen_retag_operand(

compiler/rustc_hir_analysis/src/autoderef.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_span::{ErrorGuaranteed, Span};
77
use rustc_trait_selection::traits::ObligationCtxt;
88
use tracing::{debug, instrument};
99

10-
use crate::errors::AutoDerefReachedRecursionLimit;
10+
use crate::diagnostics::AutoDerefReachedRecursionLimit;
1111
use crate::traits;
1212
use crate::traits::query::evaluate_obligation::InferCtxtExt;
1313

compiler/rustc_hir_analysis/src/check/always_applicable.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_span::sym;
1616
use rustc_trait_selection::regions::InferCtxtRegionExt;
1717
use rustc_trait_selection::traits::{self, ObligationCtxt};
1818

19-
use crate::errors;
19+
use crate::diagnostics;
2020
use crate::hir::def_id::{DefId, LocalDefId};
2121

2222
/// This function confirms that the `Drop` implementation identified by
@@ -42,12 +42,12 @@ pub(crate) fn check_drop_impl(
4242
match tcx.impl_polarity(drop_impl_did) {
4343
ty::ImplPolarity::Positive => {}
4444
ty::ImplPolarity::Negative => {
45-
return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Negative {
45+
return Err(tcx.dcx().emit_err(diagnostics::DropImplPolarity::Negative {
4646
span: tcx.def_span(drop_impl_did),
4747
}));
4848
}
4949
ty::ImplPolarity::Reservation => {
50-
return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Reservation {
50+
return Err(tcx.dcx().emit_err(diagnostics::DropImplPolarity::Reservation {
5151
span: tcx.def_span(drop_impl_did),
5252
}));
5353
}
@@ -393,7 +393,7 @@ fn check_drop_xor_pin_drop<'tcx>(
393393
match (drop_span, pin_drop_span) {
394394
(None, None) => {
395395
if tcx.features().pin_ergonomics() {
396-
return Err(tcx.dcx().emit_err(crate::errors::MissingOneOfTraitItem {
396+
return Err(tcx.dcx().emit_err(crate::diagnostics::MissingOneOfTraitItem {
397397
span: tcx.def_span(drop_impl_did),
398398
note: None,
399399
missing_items_msg: "drop`, `pin_drop".to_string(),
@@ -408,7 +408,7 @@ fn check_drop_xor_pin_drop<'tcx>(
408408
if tcx.adt_def(adt_def_id).is_pin_project() {
409409
let pin_v2_span = rustc_hir::find_attr!(tcx, adt_def_id, PinV2(attr) => *attr);
410410
let adt_name = tcx.item_name(adt_def_id);
411-
return Err(tcx.dcx().emit_err(crate::errors::PinV2WithoutPinDrop {
411+
return Err(tcx.dcx().emit_err(crate::diagnostics::PinV2WithoutPinDrop {
412412
span,
413413
pin_v2_span,
414414
adt_name,
@@ -424,7 +424,7 @@ fn check_drop_xor_pin_drop<'tcx>(
424424
}
425425
}
426426
(Some(drop_span), Some(pin_drop_span)) => {
427-
return Err(tcx.dcx().emit_err(crate::errors::ConflictImplDropAndPinDrop {
427+
return Err(tcx.dcx().emit_err(crate::diagnostics::ConflictImplDropAndPinDrop {
428428
span: tcx.def_span(drop_impl_did),
429429
drop_span,
430430
pin_drop_span,

0 commit comments

Comments
 (0)