Skip to content

Commit bb95b7d

Browse files
committed
Auto merge of #112307 - lcnr:operand-ref, r=compiler-errors
mir opt + codegen: handle subtyping fixes #107205 the same issue was caused in multiple places: - mir opts: both copy and destination propagation - codegen: assigning operands to locals (which also propagates values) I changed codegen to always update the type in the operands used for locals which should guard against any new occurrences of this bug going forward. I don't know how to make mir optimizations more resilient here. Hopefully the added tests will be enough to detect any trivially wrong optimizations going forward.
2 parents 6b46c99 + 46973c9 commit bb95b7d

12 files changed

+207
-39
lines changed

compiler/rustc_codegen_ssa/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(if_let_guard)]
55
#![feature(int_roundings)]
66
#![feature(let_chains)]
7+
#![feature(negative_impls)]
78
#![feature(never_type)]
89
#![feature(strict_provenance)]
910
#![feature(try_blocks)]

compiler/rustc_codegen_ssa/src/mir/block.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1729,7 +1729,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17291729
IndirectOperand(tmp, index) => {
17301730
let op = bx.load_operand(tmp);
17311731
tmp.storage_dead(bx);
1732-
self.locals[index] = LocalRef::Operand(op);
1732+
self.overwrite_local(index, LocalRef::Operand(op));
17331733
self.debug_introduce_local(bx, index);
17341734
}
17351735
DirectOperand(index) => {
@@ -1744,7 +1744,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17441744
} else {
17451745
OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout)
17461746
};
1747-
self.locals[index] = LocalRef::Operand(op);
1747+
self.overwrite_local(index, LocalRef::Operand(op));
17481748
self.debug_introduce_local(bx, index);
17491749
}
17501750
}

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
248248
}
249249

250250
fn spill_operand_to_stack(
251-
operand: &OperandRef<'tcx, Bx::Value>,
251+
operand: OperandRef<'tcx, Bx::Value>,
252252
name: Option<String>,
253253
bx: &mut Bx,
254254
) -> PlaceRef<'tcx, Bx::Value> {
@@ -375,7 +375,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
375375
return;
376376
}
377377

378-
Self::spill_operand_to_stack(operand, name, bx)
378+
Self::spill_operand_to_stack(*operand, name, bx)
379379
}
380380

381381
LocalRef::Place(place) => *place,
@@ -550,7 +550,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
550550
if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
551551
self.set_debug_loc(bx, var.source_info);
552552
let base = Self::spill_operand_to_stack(
553-
&operand,
553+
operand,
554554
Some(var.name.to_string()),
555555
bx,
556556
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//! Locals are in a private module as updating `LocalRef::Operand` has to
2+
//! be careful wrt to subtyping. To deal with this we only allow updates by using
3+
//! `FunctionCx::overwrite_local` which handles it automatically.
4+
use crate::mir::{FunctionCx, LocalRef};
5+
use crate::traits::BuilderMethods;
6+
use rustc_index::IndexVec;
7+
use rustc_middle::mir;
8+
use rustc_middle::ty::print::with_no_trimmed_paths;
9+
use std::ops::{Index, IndexMut};
10+
11+
pub(super) struct Locals<'tcx, V> {
12+
values: IndexVec<mir::Local, LocalRef<'tcx, V>>,
13+
}
14+
15+
impl<'tcx, V> Index<mir::Local> for Locals<'tcx, V> {
16+
type Output = LocalRef<'tcx, V>;
17+
#[inline]
18+
fn index(&self, index: mir::Local) -> &LocalRef<'tcx, V> {
19+
&self.values[index]
20+
}
21+
}
22+
23+
/// To mutate locals, use `FunctionCx::overwrite_local` instead.
24+
impl<'tcx, V, Idx: ?Sized> !IndexMut<Idx> for Locals<'tcx, V> {}
25+
26+
impl<'tcx, V> Locals<'tcx, V> {
27+
pub(super) fn empty() -> Locals<'tcx, V> {
28+
Locals { values: IndexVec::default() }
29+
}
30+
31+
pub(super) fn indices(&self) -> impl DoubleEndedIterator<Item = mir::Local> + Clone + 'tcx {
32+
self.values.indices()
33+
}
34+
}
35+
36+
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
37+
pub(super) fn initialize_locals(&mut self, values: Vec<LocalRef<'tcx, Bx::Value>>) {
38+
assert!(self.locals.values.is_empty());
39+
40+
for (local, value) in values.into_iter().enumerate() {
41+
match value {
42+
LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
43+
LocalRef::Operand(op) => {
44+
let local = mir::Local::from_usize(local);
45+
let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
46+
assert_eq!(expected_ty, op.layout.ty, "unexpected initial operand type");
47+
}
48+
}
49+
50+
self.locals.values.push(value);
51+
}
52+
}
53+
54+
pub(super) fn overwrite_local(
55+
&mut self,
56+
local: mir::Local,
57+
mut value: LocalRef<'tcx, Bx::Value>,
58+
) {
59+
match value {
60+
LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
61+
LocalRef::Operand(ref mut op) => {
62+
let local_ty = self.monomorphize(self.mir.local_decls[local].ty);
63+
if local_ty != op.layout.ty {
64+
// FIXME(#112651): This can be changed to an ICE afterwards.
65+
debug!("updating type of operand due to subtyping");
66+
with_no_trimmed_paths!(debug!(?op.layout.ty));
67+
with_no_trimmed_paths!(debug!(?local_ty));
68+
op.layout.ty = local_ty;
69+
}
70+
}
71+
};
72+
73+
self.locals.values[local] = value;
74+
}
75+
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

+20-23
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
use crate::base;
22
use crate::traits::*;
3+
use rustc_index::bit_set::BitSet;
4+
use rustc_index::IndexVec;
35
use rustc_middle::mir;
46
use rustc_middle::mir::interpret::ErrorHandled;
7+
use rustc_middle::mir::traversal;
58
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
69
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
710
use rustc_target::abi::call::{FnAbi, PassMode};
811

912
use std::iter;
1013

11-
use rustc_index::bit_set::BitSet;
12-
use rustc_index::IndexVec;
14+
mod analyze;
15+
mod block;
16+
pub mod constant;
17+
pub mod coverageinfo;
18+
pub mod debuginfo;
19+
mod intrinsic;
20+
mod locals;
21+
pub mod operand;
22+
pub mod place;
23+
mod rvalue;
24+
mod statement;
1325

1426
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
15-
use self::place::PlaceRef;
16-
use rustc_middle::mir::traversal;
17-
1827
use self::operand::{OperandRef, OperandValue};
28+
use self::place::PlaceRef;
1929

2030
// Used for tracking the state of generated basic blocks.
2131
enum CachedLlbb<T> {
@@ -91,7 +101,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
91101
///
92102
/// Avoiding allocs can also be important for certain intrinsics,
93103
/// notably `expect`.
94-
locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>,
104+
locals: locals::Locals<'tcx, Bx::Value>,
95105

96106
/// All `VarDebugInfo` from the MIR body, partitioned by `Local`.
97107
/// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed.
@@ -192,7 +202,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
192202
cleanup_kinds,
193203
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
194204
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
195-
locals: IndexVec::new(),
205+
locals: locals::Locals::empty(),
196206
debug_context,
197207
per_local_var_debug_info: None,
198208
caller_location: None,
@@ -223,7 +233,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
223233
let memory_locals = analyze::non_ssa_locals(&fx);
224234

225235
// Allocate variable and temp allocas
226-
fx.locals = {
236+
let local_values = {
227237
let args = arg_local_refs(&mut start_bx, &mut fx, &memory_locals);
228238

229239
let mut allocate_local = |local| {
@@ -256,6 +266,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
256266
.chain(mir.vars_and_temps_iter().map(allocate_local))
257267
.collect()
258268
};
269+
fx.initialize_locals(local_values);
259270

260271
// Apply debuginfo to the newly allocated locals.
261272
fx.debug_introduce_locals(&mut start_bx);
@@ -289,14 +300,13 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
289300
.enumerate()
290301
.map(|(arg_index, local)| {
291302
let arg_decl = &mir.local_decls[local];
303+
let arg_ty = fx.monomorphize(arg_decl.ty);
292304

293305
if Some(local) == mir.spread_arg {
294306
// This argument (e.g., the last argument in the "rust-call" ABI)
295307
// is a tuple that was spread at the ABI level and now we have
296308
// to reconstruct it into a tuple local variable, from multiple
297309
// individual LLVM function arguments.
298-
299-
let arg_ty = fx.monomorphize(arg_decl.ty);
300310
let ty::Tuple(tupled_arg_tys) = arg_ty.kind() else {
301311
bug!("spread argument isn't a tuple?!");
302312
};
@@ -331,8 +341,6 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
331341
}
332342

333343
if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() {
334-
let arg_ty = fx.monomorphize(arg_decl.ty);
335-
336344
let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty));
337345
bx.va_start(va_list.llval);
338346

@@ -429,14 +437,3 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
429437

430438
args
431439
}
432-
433-
mod analyze;
434-
mod block;
435-
pub mod constant;
436-
pub mod coverageinfo;
437-
pub mod debuginfo;
438-
mod intrinsic;
439-
pub mod operand;
440-
pub mod place;
441-
mod rvalue;
442-
mod statement;

compiler/rustc_codegen_ssa/src/mir/statement.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2020
}
2121
LocalRef::PendingOperand => {
2222
let operand = self.codegen_rvalue_operand(bx, rvalue);
23-
self.locals[index] = LocalRef::Operand(operand);
23+
self.overwrite_local(index, LocalRef::Operand(operand));
2424
self.debug_introduce_local(bx, index);
2525
}
2626
LocalRef::Operand(op) => {

compiler/rustc_mir_transform/src/dest_prop.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
//! if they do not consistently refer to the same place in memory. This is satisfied if they do
3838
//! not contain any indirection through a pointer or any indexing projections.
3939
//!
40+
//! * `p` and `q` must have the **same type**. If we replace a local with a subtype or supertype,
41+
//! we may end up with a differnet vtable for that local. See the `subtyping-impacts-selection`
42+
//! tests for an example where that causes issues.
43+
//!
4044
//! * We need to make sure that the goal of "merging the memory" is actually structurally possible
4145
//! in MIR. For example, even if all the other conditions are satisfied, there is no way to
4246
//! "merge" `_5.foo` and `_6.bar`. For now, we ensure this by requiring that both `p` and `q` are
@@ -134,6 +138,7 @@ use crate::MirPass;
134138
use rustc_data_structures::fx::FxHashMap;
135139
use rustc_index::bit_set::BitSet;
136140
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
141+
use rustc_middle::mir::HasLocalDecls;
137142
use rustc_middle::mir::{dump_mir, PassWhere};
138143
use rustc_middle::mir::{
139144
traversal, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, Rvalue,
@@ -763,12 +768,22 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
763768
return;
764769
};
765770

766-
// As described at the top of the file, we do not go near things that have their address
767-
// taken.
771+
// As described at the top of the file, we do not go near things that have
772+
// their address taken.
768773
if self.borrowed.contains(src) || self.borrowed.contains(dest) {
769774
return;
770775
}
771776

777+
// As described at the top of this file, we do not touch locals which have
778+
// different types.
779+
let src_ty = self.body.local_decls()[src].ty;
780+
let dest_ty = self.body.local_decls()[dest].ty;
781+
if src_ty != dest_ty {
782+
// FIXME(#112651): This can be removed afterwards. Also update the module description.
783+
trace!("skipped `{src:?} = {dest:?}` due to subtyping: {src_ty} != {dest_ty}");
784+
return;
785+
}
786+
772787
// Also, we need to make sure that MIR actually allows the `src` to be removed
773788
if is_local_required(src, self.body) {
774789
return;

compiler/rustc_mir_transform/src/shim.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
3232
let mut result = match instance {
3333
ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
3434
ty::InstanceDef::VTableShim(def_id) => {
35-
build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
35+
let adjustment = Adjustment::Deref { source: DerefSource::MutPtr };
36+
build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id))
3637
}
3738
ty::InstanceDef::FnPtrShim(def_id, ty) => {
3839
let trait_ = tcx.trait_of_item(def_id).unwrap();
3940
let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
4041
Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
41-
Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref,
42+
Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
43+
Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
4244
None => bug!("fn pointer {:?} is not an fn", ty),
4345
};
4446

@@ -107,16 +109,26 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
107109
result
108110
}
109111

112+
#[derive(Copy, Clone, Debug, PartialEq)]
113+
enum DerefSource {
114+
/// `fn shim(&self) { inner(*self )}`.
115+
ImmRef,
116+
/// `fn shim(&mut self) { inner(*self )}`.
117+
MutRef,
118+
/// `fn shim(*mut self) { inner(*self )}`.
119+
MutPtr,
120+
}
121+
110122
#[derive(Copy, Clone, Debug, PartialEq)]
111123
enum Adjustment {
112124
/// Pass the receiver as-is.
113125
Identity,
114126

115-
/// We get passed `&[mut] self` and call the target with `*self`.
127+
/// We get passed a reference or a raw pointer to `self` and call the target with `*self`.
116128
///
117129
/// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it
118130
/// (for `VTableShim`, which effectively is passed `&own Self`).
119-
Deref,
131+
Deref { source: DerefSource },
120132

121133
/// We get passed `self: Self` and call the target with `&mut self`.
122134
///
@@ -667,8 +679,12 @@ fn build_call_shim<'tcx>(
667679
let self_arg = &mut inputs_and_output[0];
668680
*self_arg = match rcvr_adjustment.unwrap() {
669681
Adjustment::Identity => fnty,
670-
Adjustment::Deref => tcx.mk_imm_ptr(fnty),
671-
Adjustment::RefMut => tcx.mk_mut_ptr(fnty),
682+
Adjustment::Deref { source } => match source {
683+
DerefSource::ImmRef => tcx.mk_imm_ref(tcx.lifetimes.re_erased, fnty),
684+
DerefSource::MutRef => tcx.mk_mut_ref(tcx.lifetimes.re_erased, fnty),
685+
DerefSource::MutPtr => tcx.mk_mut_ptr(fnty),
686+
},
687+
Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"),
672688
};
673689
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
674690
}
@@ -699,7 +715,7 @@ fn build_call_shim<'tcx>(
699715

700716
let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment {
701717
Adjustment::Identity => Operand::Move(rcvr_place()),
702-
Adjustment::Deref => Operand::Move(tcx.mk_place_deref(rcvr_place())),
718+
Adjustment::Deref { source: _ } => Operand::Move(tcx.mk_place_deref(rcvr_place())),
703719
Adjustment::RefMut => {
704720
// let rcvr = &mut rcvr;
705721
let ref_rcvr = local_decls.push(

compiler/rustc_mir_transform/src/ssa.rs

+8
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
271271
else { continue };
272272

273273
let Some(rhs) = place.as_local() else { continue };
274+
let local_ty = body.local_decls()[local].ty;
275+
let rhs_ty = body.local_decls()[rhs].ty;
276+
if local_ty != rhs_ty {
277+
// FIXME(#112651): This can be removed afterwards.
278+
trace!("skipped `{local:?} = {rhs:?}` due to subtyping: {local_ty} != {rhs_ty}");
279+
continue;
280+
}
281+
274282
if !ssa.is_ssa(rhs) {
275283
continue;
276284
}

tests/mir-opt/fn_ptr_shim.core.ops-function-Fn-call.AddMovesForPackedDrops.before.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// MIR for `std::ops::Fn::call` before AddMovesForPackedDrops
22

3-
fn std::ops::Fn::call(_1: *const fn(), _2: ()) -> <fn() as FnOnce<()>>::Output {
3+
fn std::ops::Fn::call(_1: &fn(), _2: ()) -> <fn() as FnOnce<()>>::Output {
44
let mut _0: <fn() as std::ops::FnOnce<()>>::Output;
55

66
bb0: {

0 commit comments

Comments
 (0)