From c4523362dbe0a7713d14410ebecf01e251beb697 Mon Sep 17 00:00:00 2001 From: Qi Deng Date: Wed, 20 May 2026 11:18:04 -0700 Subject: [PATCH 1/2] Filter PhantomData fields in CoerceShared borrowck field matching The `add_generic_reborrow_constraint` function matches source and dest struct fields by name when establishing borrow constraints for CoerceShared. However, it uses `all_fields()` which includes PhantomData fields, while the coherence check (`coerce_shared_info`) filters them out via `collect_struct_data_fields`. This mismatch means PhantomData fields with different names between source and dest are silently skipped, potentially dropping lifetime constraints. Fix: filter out PhantomData fields before matching, consistent with the coherence check. Co-Authored-By: Claude Opus 4.6 (1M context) --- compiler/rustc_borrowck/src/type_check/mod.rs | 15 +++++++-- .../coerce_shared_phantom_field_names.rs | 33 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/ui/reborrow/coerce_shared_phantom_field_names.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 13983f349d6a5..e9cfe331ed8a8 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2516,8 +2516,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Field-by-field relate_types is expected to work based on the wf-checks that the // CoerceShared trait performs. let ty::Adt(borrowed_adt, borrowed_args) = borrowed_ty.kind() else { unreachable!() }; - let borrowed_fields = borrowed_adt.all_fields().collect::>(); - for dest_field in dest_adt.all_fields() { + // Filter out PhantomData fields to match the coherence check in + // `coerce_shared_info`, which uses `collect_struct_data_fields`. + // Without this filter, PhantomData fields with different names + // between source and dest would be silently skipped, potentially + // dropping lifetime constraints. + let borrowed_fields: Vec<_> = borrowed_adt + .all_fields() + .filter(|f| !f.ty(tcx, borrowed_args).skip_norm_wip().is_phantom_data()) + .collect(); + let dest_data_fields = dest_adt + .all_fields() + .filter(|f| !f.ty(tcx, dest_args).skip_norm_wip().is_phantom_data()); + for dest_field in dest_data_fields { let Some(borrowed_field) = borrowed_fields.iter().find(|f| f.name == dest_field.name) else { diff --git a/tests/ui/reborrow/coerce_shared_phantom_field_names.rs b/tests/ui/reborrow/coerce_shared_phantom_field_names.rs new file mode 100644 index 0000000000000..75882c6db40be --- /dev/null +++ b/tests/ui/reborrow/coerce_shared_phantom_field_names.rs @@ -0,0 +1,33 @@ +//@ run-pass + +// Regression test: CoerceShared borrowck must filter PhantomData fields +// before matching by name, consistent with coherence's +// `collect_struct_data_fields`. Without the filter, differently-named +// PhantomData fields would cause lifetime constraints to be silently dropped. + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct Source<'a> { + data: &'a mut i32, + _marker: PhantomData<&'a ()>, +} +impl<'a> Reborrow for Source<'a> {} + +#[derive(Clone, Copy)] +struct Dest<'a> { + data: &'a i32, + _lifetime: PhantomData<&'a ()>, // Different name from Source's PhantomData field +} +impl<'a> CoerceShared> for Source<'a> {} + +fn use_ref<'a>(s: Dest<'a>) -> &'a i32 { + s.data +} + +fn main() { + let mut val = 42; + let s = Source { data: &mut val, _marker: PhantomData }; + let r = use_ref(s); + assert_eq!(*r, 42); +} From 69dd5c5405f81d3411f2e3f116321046206f2835 Mon Sep 17 00:00:00 2001 From: Qi Deng Date: Wed, 20 May 2026 14:20:14 -0700 Subject: [PATCH 2/2] Suppress dead_code warning in CoerceShared test Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/ui/reborrow/coerce_shared_phantom_field_names.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/reborrow/coerce_shared_phantom_field_names.rs b/tests/ui/reborrow/coerce_shared_phantom_field_names.rs index 75882c6db40be..92ffad7ee0794 100644 --- a/tests/ui/reborrow/coerce_shared_phantom_field_names.rs +++ b/tests/ui/reborrow/coerce_shared_phantom_field_names.rs @@ -6,6 +6,7 @@ // PhantomData fields would cause lifetime constraints to be silently dropped. #![feature(reborrow)] +#![allow(dead_code)] use std::marker::{CoerceShared, PhantomData, Reborrow}; struct Source<'a> {