From d6b0c00e0850ab6e70f87b01ca8fd45b73f2ed08 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 24 Sep 2025 17:42:25 +0300 Subject: [PATCH 01/34] Add basic trait resolving --- .../rustc_builtin_macros/src/deriving/mod.rs | 1 + .../src/deriving/reborrow.rs | 208 ++++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_hir_typeck/src/coercion.rs | 20 +- .../rustc_hir_typeck/src/expr_use_visitor.rs | 7 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 3 + compiler/rustc_lint/src/autorefs.rs | 1 + compiler/rustc_middle/src/ty/adjustment.rs | 4 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 3 + compiler/rustc_span/src/symbol.rs | 2 + 11 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/deriving/reborrow.rs diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index cee6952fa3460..a4c7baebb5ce5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -25,6 +25,7 @@ pub(crate) mod debug; pub(crate) mod default; pub(crate) mod from; pub(crate) mod hash; +pub(crate) mod reborrow; #[path = "cmp/eq.rs"] pub(crate) mod eq; diff --git a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs new file mode 100644 index 0000000000000..0fcf651c49ba4 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs @@ -0,0 +1,208 @@ +use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; +use rustc_data_structures::fx::FxHashSet; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::{Ident, Span, kw, sym}; +use thin_vec::{ThinVec, thin_vec}; + +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::path_std; + +pub(crate) fn expand_deriving_reborrow( + cx: &ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), + is_const: bool, +) { + // The simple form is `fn reborrow(self) -> Self { self }`, possibly with + // some additional `AssertParamIsReborrow` assertions. + // + // We can use the simple form if either of the following are true. + // - The type derives Copy and there are no generic parameters. (If we + // used the simple form with generics, we'd have to bound the generics + // with Reborrow + Copy, and then there'd be no Reborrow impl at all if the + // user fills in something that is Reborrow but not Copy. After + // specialization we can remove this no-generics limitation.) + // - The item is a union. (Unions with generic parameters still can derive + // Reborrow because they require Copy for deriving, Reborrow alone is not + // enough. Whether Reborrow is implemented for fields is irrelevant so we + // don't assert it.) + let bounds; + let is_simple; + match item { + Annotatable::Item(annitem) => match &annitem.kind { + ItemKind::Struct(_, Generics { params, .. }, _) + | ItemKind::Enum(_, Generics { params, .. }, _) => { + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_copy = cx.resolver.has_derive_copy(container_id); + if has_derive_copy + && !params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + { + bounds = vec![]; + is_simple = true; + // substructure = combine_substructure(Box::new(|c, s, sub| { + // cs_reborrow_simple("Reborrow", c, s, sub, false) + // })); + } else { + bounds = vec![]; + is_simple = false; + // substructure = + // combine_substructure(Box::new(|c, s, sub| cs_reborrow("Reborrow", c, s, sub))); + } + } + ItemKind::Union(..) => { + bounds = vec![Path(path_std!(marker::Copy))]; + is_simple = true; + // substructure = combine_substructure(Box::new(|c, s, sub| { + // cs_reborrow_simple("Reborrow", c, s, sub, true) + // })); + } + _ => cx.dcx().span_bug(span, "`#[derive(Reborrow)]` on wrong item kind"), + }, + _ => cx.dcx().span_bug(span, "`#[derive(Reborrow)]` on trait item or impl item"), + } + + let trait_def = TraitDef { + span, + path: path_std!(marker::Reborrow), + skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, + additional_bounds: bounds, + supports_unions: true, + methods: vec![], + associated_types: Vec::new(), + is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), + }; + + trait_def.expand_ext(cx, mitem, item, push, is_simple) +} + +fn _cs_reborrow_simple( + name: &str, + cx: &ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, + is_union: bool, +) -> BlockOrExpr { + let mut stmts = ThinVec::new(); + let mut seen_type_names = FxHashSet::default(); + let mut process_variant = |variant: &VariantData| { + for field in variant.fields() { + // This basic redundancy checking only prevents duplication of + // assertions like `AssertParamIsReborrow` where the type is a + // simple name. That's enough to get a lot of cases, though. + if let Some(name) = field.ty.kind.is_simple_path() + && !seen_type_names.insert(name) + { + // Already produced an assertion for this type. + // Anonymous structs or unions must be eliminated as they cannot be + // type parameters. + } else { + // let _: AssertParamIsReborrow; + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::reborrow, sym::AssertParamIsCopy], + ); + } + } + }; + + if is_union { + // Just a single assertion for unions, that the union impls `Copy`. + // let _: AssertParamIsCopy; + let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); + super::assert_ty_bounds( + cx, + &mut stmts, + self_ty, + trait_span, + &[sym::reborrow, sym::AssertParamIsCopy], + ); + } else { + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(&variant.data); + } + } + _ => cx.dcx().span_bug( + trait_span, + format!("unexpected substructure in simple `derive({name})`"), + ), + } + } + BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))) +} + +fn _cs_reborrow( + name: &str, + cx: &ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let ctor_path; + let all_fields; + let fn_path = cx.std_path(&[sym::reborrow, sym::Reborrow, sym::reborrow]); + let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| { + let args = thin_vec![field.self_expr.clone()]; + cx.expr_call_global(field.span, fn_path.clone(), args) + }; + + let vdata; + match substr.fields { + Struct(vdata_, af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident]); + all_fields = af; + vdata = *vdata_; + } + EnumMatching(.., variant, af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); + all_fields = af; + vdata = &variant.data; + } + EnumDiscr(..) | AllFieldlessEnum(..) => { + cx.dcx().span_bug(trait_span, format!("enum discriminants in `derive({name})`",)) + } + StaticEnum(..) | StaticStruct(..) => { + cx.dcx().span_bug(trait_span, format!("associated function in `derive({name})`")) + } + } + + let expr = match *vdata { + VariantData::Struct { .. } => { + let fields = all_fields + .iter() + .map(|field| { + let Some(ident) = field.name else { + cx.dcx().span_bug( + trait_span, + format!("unnamed field in normal struct in `derive({name})`",), + ); + }; + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) + }) + .collect::>(); + + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } + VariantData::Unit(..) => cx.expr_path(ctor_path), + }; + BlockOrExpr::new_expr(expr) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 120e3f849ff63..ad9e4a463535b 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -137,6 +137,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Ord: ord::expand_deriving_ord, PartialEq: partial_eq::expand_deriving_partial_eq, PartialOrd: partial_ord::expand_deriving_partial_ord, + Reborrow: reborrow::expand_deriving_reborrow, CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, From: from::expand_deriving_from, } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..e60e0a418ca07 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -448,6 +448,7 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); + CoerceSharedTarget, sym::coerce_shared_target,coerce_shared_target, Target::AssocTy, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 52ea6cdeaa0eb..a77c069627b00 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,7 +46,8 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, InferResult, RegionVariableOrigin}; use rustc_infer::traits::{ - MatchExpressionArmCause, Obligation, PredicateObligation, PredicateObligations, SelectionError, + EvaluationResult, MatchExpressionArmCause, Obligation, PredicateObligation, + PredicateObligations, SelectionError, }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ @@ -262,7 +263,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Examine the target type and consider type-specific coercions, such - // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. + // as auto-borrowing, coercing pointer mutability, pin-ergonomics, or + // generic reborrow. match *b.kind() { ty::RawPtr(_, b_mutbl) => { return self.coerce_to_raw_ptr(a, b, b_mutbl); @@ -279,6 +281,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return pin_coerce; } } + ty::Adt(_, _) + if self.tcx.features().reborrow() + && self.fcx.infcx.type_implements_trait( + self.tcx + .lang_items() + .reborrow() + .expect("Unexpectedly using core/std without reborrow"), + [b], + self.fcx.param_env, + ) == EvaluationResult::EvaluatedToOk => + { + // FIXME(reborrow): how to check for t impl Reborrow, t impl CoerceShared + panic!("Don't do the magic!"); + } _ => {} } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ad34994526ed9..9950e902f7f55 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -758,6 +758,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx }; self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } + + adjustment::Adjust::GenericReborrow(_reborrow) => { + // Do the magic! + } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -1291,7 +1295,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) | adjustment::Adjust::Borrow(_) - | adjustment::Adjust::ReborrowPin(..) => { + | adjustment::Adjust::ReborrowPin(..) + | adjustment::Adjust::GenericReborrow(..) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 91f91d9114449..1a8386a4b370a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -284,6 +284,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(const_trait_impl): We could enforce these; they correspond to // `&mut T: DerefMut` tho, so it's kinda moot. } + Adjust::GenericReborrow(_) => { + // No effects to enforce here. + } Adjust::Borrow(_) => { // No effects to enforce here. } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index ed7ac0e33244b..d6d8d04876200 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -170,6 +170,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) + | Adjust::GenericReborrow(..) | Adjust::Deref(None) | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518ae..9594a59eff7fb 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -106,6 +106,10 @@ pub enum Adjust { /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. ReborrowPin(hir::Mutability), + + /// Generate a T and use the Reborrow or CoerceShared trait to produce a + /// new T. + GenericReborrow(hir::Mutability), } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8e02424706eec..3e3d0eec5b384 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -240,6 +240,9 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?kind); kind } + Adjust::GenericReborrow(_mutbl) => { + todo!("Reborrow / CoerceShared"); + } }; Expr { temp_scope_id, ty: adjustment.target, span, kind } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c4391c3ec4fc4..7a48fb4a96382 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -340,6 +340,7 @@ symbols! { Rc, RcWeak, Ready, + Reborrow, Receiver, RefCell, RefCellRef, @@ -710,6 +711,7 @@ symbols! { cmse_nonsecure_entry, coerce_pointee_validated, coerce_shared, + coerce_shared_target, coerce_unsized, cold, cold_path, From 8cb2709d97277fca62b7156ca51d7355c6caaac1 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 24 Sep 2025 18:50:53 +0300 Subject: [PATCH 02/34] Reborrow adjustment --- compiler/rustc_hir_typeck/src/coercion.rs | 52 +++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a77c069627b00..0f7c327d0b02d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,8 +46,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, InferResult, RegionVariableOrigin}; use rustc_infer::traits::{ - EvaluationResult, MatchExpressionArmCause, Obligation, PredicateObligation, - PredicateObligations, SelectionError, + MatchExpressionArmCause, Obligation, PredicateObligation, PredicateObligations, SelectionError, }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ @@ -283,17 +282,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } ty::Adt(_, _) if self.tcx.features().reborrow() - && self.fcx.infcx.type_implements_trait( - self.tcx - .lang_items() - .reborrow() - .expect("Unexpectedly using core/std without reborrow"), - [b], - self.fcx.param_env, - ) == EvaluationResult::EvaluatedToOk => + && self + .fcx + .infcx + .type_implements_trait( + self.tcx + .lang_items() + .reborrow() + .expect("Unexpectedly using core/std without reborrow"), + [b], + self.fcx.param_env, + ) + .must_apply_modulo_regions() => { - // FIXME(reborrow): how to check for t impl Reborrow, t impl CoerceShared - panic!("Don't do the magic!"); + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_reborrow(a, b)); + if reborrow_coerce.is_ok() { + return reborrow_coerce; + } } _ => {} } @@ -862,6 +867,29 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No) } + /// Applies geneirc reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow.' + // FIXME(reborrow): this shouldn't be just an equality check. + if a == b { + // Exclusive reborrowing goes from T -> T. + return self.unify_and( + a, + b, + [], + Adjust::GenericReborrow(ty::Mutability::Mut), + ForceLeakCheck::No, + ); + } + + // FIXME(reborrow): CoerceShared. + self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Not), ForceLeakCheck::No) + } + fn coerce_from_fn_pointer( &self, a: Ty<'tcx>, From a6964fbd8dc1d82998ca699df5560850ceda855c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 3 Oct 2025 19:17:02 +0300 Subject: [PATCH 03/34] further work: reborrow now in your local MIR --- compiler/rustc_hir_typeck/src/coercion.rs | 9 ++++----- compiler/rustc_middle/src/thir.rs | 5 +++++ compiler/rustc_middle/src/thir/visit.rs | 1 + .../src/builder/expr/as_place.rs | 3 +++ .../src/builder/expr/as_rvalue.rs | 3 +++ .../src/builder/expr/category.rs | 3 ++- .../rustc_mir_build/src/builder/expr/into.rs | 18 ++++++++++++++++++ compiler/rustc_mir_build/src/check_unsafety.rs | 3 ++- compiler/rustc_mir_build/src/thir/cx/expr.rs | 16 ++++++++++++++-- .../src/thir/pattern/check_match.rs | 3 +++ compiler/rustc_mir_build/src/thir/print.rs | 8 ++++++++ compiler/rustc_ty_utils/src/consts.rs | 6 ++++++ 12 files changed, 69 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0f7c327d0b02d..86a351867d50a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -867,14 +867,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No) } - /// Applies geneirc reborrowing on type implementing `Reborrow`. + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. #[instrument(skip(self), level = "trace")] fn coerce_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); - // We need to make sure the two types are compatible for reborrow.' - // FIXME(reborrow): this shouldn't be just an equality check. + // We need to make sure the two types are compatible for reborrow. if a == b { // Exclusive reborrowing goes from T -> T. return self.unify_and( @@ -886,8 +885,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ); } - // FIXME(reborrow): CoerceShared. - self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Not), ForceLeakCheck::No) + // FIXME(reborrow): this we should check equality. + self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Mut), ForceLeakCheck::No) } fn coerce_from_fn_pointer( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 5e80314035652..61b8a5ff2ecc4 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -550,6 +550,11 @@ pub enum ExprKind<'tcx> { Yield { value: ExprId, }, + Reborrow { + source: ExprId, + mutability: Mutability, + ty: Ty<'tcx>, + }, } /// Represents the association of a field identifier and an expression. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 8e0e3aa294b85..5b90c8bda3c5d 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -188,6 +188,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), + Reborrow { source:_, mutability: _, ty: _ } => {} } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 172dbf7c31b51..1a6e324a656e7 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -591,6 +591,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp_lifetime = this.region_scope_tree.temporary_scope(expr.temp_scope_id); let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) + }, + ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + todo!(); } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 8de79ab2531f4..cef01aaa4f4f5 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -491,6 +491,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No) ); block.and(Rvalue::Use(operand)) + }, + ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + todo!(); } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 1464b5e560bf5..0d3bc655bb042 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -71,7 +71,8 @@ impl Category { | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::WrapUnsafeBinder { .. } + | ExprKind::Reborrow { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 60e05b691a83b..752212246e393 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -801,6 +801,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id)); this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() + }, + ExprKind::Reborrow { source, mutability, ty } => { + if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { + panic!("Reborrow currently requires self to be Copy"); + } + // let arg_place = match borrow_kind { + // BorrowKind::Shared => { + // unpack!(block = this.as_read_only_place(block, source)) + // } + // _ => unpack!(block = this.as_place(block, source)), + // }; + // let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); + let _1 = unpack!( + block = + this.as_temp(block, this.local_temp_lifetime(), source, mutability) + ); + this.cfg.push_assign(block, source_info, destination, Rvalue::Use(Operand::Copy(_1.into()))); + block.unit() } }; diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 4a20b05d1fc2d..03a8902b65b44 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -468,7 +468,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::If { .. } | ExprKind::InlineAsm { .. } | ExprKind::LogicalOp { .. } - | ExprKind::Use { .. } => { + | ExprKind::Use { .. } + | ExprKind::Reborrow { .. } => { // We don't need to save the old value and restore it // because all the place expressions can't have more // than one child. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 3e3d0eec5b384..f0587ea9c134b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -240,8 +240,20 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?kind); kind } - Adjust::GenericReborrow(_mutbl) => { - todo!("Reborrow / CoerceShared"); + Adjust::GenericReborrow(ty::Mutability::Mut) => { + let ty = expr.ty; + let expr = self.thir.exprs.push(expr); + let kind = ExprKind::Reborrow { + source: expr, + mutability: rustc_ast::Mutability::Mut, + ty, + }; + + kind + + } + Adjust::GenericReborrow(ty::Mutability::Not) => { + todo!("CoerceShared"); } }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 290d4ab2bfbb0..cab4ef70b1a28 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -366,6 +366,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | VarRef { .. } | ZstLiteral { .. } | Yield { .. } => true, + ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + todo!(); + } } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index db8b2518981d3..5dc74817bae2c 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -604,6 +604,14 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*value, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } + ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + print_indented!(self, "Reborrow {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + // self.print_expr(*value, depth_lvl + 2); + print_indented!(self, "mutability:", depth_lvl + 1); + print_indented!(self, "ty:", depth_lvl + 1); + print_indented!(self, "}", depth_lvl); + } } } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index f6d08bd458bdd..ebdaca1a8d572 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -206,6 +206,9 @@ fn recurse_build<'tcx>( | ExprKind::ThreadLocalRef(_) => { error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } + ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + todo!(); + } }) } @@ -304,6 +307,9 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::InlineAsm(_) | thir::ExprKind::ThreadLocalRef(_) | thir::ExprKind::Yield { .. } => false, + thir::ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + todo!(); + } } } fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { From e11633b68f822cf8ce10f08acfeb5966676d6c5e Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 3 Oct 2025 19:18:39 +0300 Subject: [PATCH 04/34] fmt --- compiler/rustc_middle/src/thir/visit.rs | 2 +- .../rustc_mir_build/src/builder/expr/as_place.rs | 4 ++-- .../rustc_mir_build/src/builder/expr/as_rvalue.rs | 4 ++-- compiler/rustc_mir_build/src/builder/expr/into.rs | 12 ++++++++---- compiler/rustc_mir_build/src/thir/cx/expr.rs | 8 ++------ .../rustc_mir_build/src/thir/pattern/check_match.rs | 2 +- compiler/rustc_mir_build/src/thir/print.rs | 2 +- compiler/rustc_ty_utils/src/consts.rs | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 5b90c8bda3c5d..060ec7b5bc0c2 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -188,7 +188,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } ThreadLocalRef(_) => {} Yield { value } => visitor.visit_expr(&visitor.thir()[value]), - Reborrow { source:_, mutability: _, ty: _ } => {} + Reborrow { source: _, mutability: _, ty: _ } => {} } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 1a6e324a656e7..3e70da312312f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -591,8 +591,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp_lifetime = this.region_scope_tree.temporary_scope(expr.temp_scope_id); let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) - }, - ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + } + ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { todo!(); } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index cef01aaa4f4f5..7232359cd457f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -491,8 +491,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No) ); block.and(Rvalue::Use(operand)) - }, - ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + } + ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { todo!(); } } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 752212246e393..6027f7618ab44 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -801,7 +801,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id)); this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() - }, + } ExprKind::Reborrow { source, mutability, ty } => { if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { panic!("Reborrow currently requires self to be Copy"); @@ -814,10 +814,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // }; // let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); let _1 = unpack!( - block = - this.as_temp(block, this.local_temp_lifetime(), source, mutability) + block = this.as_temp(block, this.local_temp_lifetime(), source, mutability) + ); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Use(Operand::Copy(_1.into())), ); - this.cfg.push_assign(block, source_info, destination, Rvalue::Use(Operand::Copy(_1.into()))); block.unit() } }; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index f0587ea9c134b..ca53ced125beb 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -243,14 +243,10 @@ impl<'tcx> ThirBuildCx<'tcx> { Adjust::GenericReborrow(ty::Mutability::Mut) => { let ty = expr.ty; let expr = self.thir.exprs.push(expr); - let kind = ExprKind::Reborrow { - source: expr, - mutability: rustc_ast::Mutability::Mut, - ty, - }; + let kind = + ExprKind::Reborrow { source: expr, mutability: rustc_ast::Mutability::Mut, ty }; kind - } Adjust::GenericReborrow(ty::Mutability::Not) => { todo!("CoerceShared"); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index cab4ef70b1a28..c4ac101823478 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -366,7 +366,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | VarRef { .. } | ZstLiteral { .. } | Yield { .. } => true, - ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { todo!(); } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 5dc74817bae2c..f5b2089f59d0a 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -604,7 +604,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*value, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } - ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { print_indented!(self, "Reborrow {", depth_lvl); print_indented!(self, "source:", depth_lvl + 1); // self.print_expr(*value, depth_lvl + 2); diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index ebdaca1a8d572..c229581b26f73 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -206,7 +206,7 @@ fn recurse_build<'tcx>( | ExprKind::ThreadLocalRef(_) => { error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? } - ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { todo!(); } }) @@ -307,7 +307,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::InlineAsm(_) | thir::ExprKind::ThreadLocalRef(_) | thir::ExprKind::Yield { .. } => false, - thir::ExprKind::Reborrow { source:_, mutability: _, ty: _ } => { + thir::ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { todo!(); } } From 819dbb7a36eae469ff3835de6d6afbf21984c16f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 9 Oct 2025 20:19:25 +0300 Subject: [PATCH 05/34] fix: Implement proper Reborrow coercion --- compiler/rustc_hir_typeck/src/coercion.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 86a351867d50a..e7534e9c7f4b1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -874,19 +874,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug_assert!(self.shallow_resolve(b) == b); // We need to make sure the two types are compatible for reborrow. - if a == b { - // Exclusive reborrowing goes from T -> T. - return self.unify_and( - a, - b, - [], - Adjust::GenericReborrow(ty::Mutability::Mut), - ForceLeakCheck::No, - ); + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // Reborrow is applicable here + self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Mut), ForceLeakCheck::No) + } else { + // FIXME: CoerceShared check goes here, error for now + Err(TypeError::Mismatch) } - - // FIXME(reborrow): this we should check equality. - self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Mut), ForceLeakCheck::No) + } } fn coerce_from_fn_pointer( From a161c7b64e56d9584fdc77356ba26a2dbe8651c5 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Oct 2025 21:14:06 +0300 Subject: [PATCH 06/34] changes --- compiler/rustc_borrowck/src/lib.rs | 20 ++++++ .../src/polonius/legacy/loan_invalidations.rs | 5 ++ compiler/rustc_borrowck/src/type_check/mod.rs | 67 ++++++++++++++++++- compiler/rustc_codegen_cranelift/src/base.rs | 5 ++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 + .../src/check_consts/check.rs | 2 + .../src/check_consts/qualifs.rs | 2 + .../src/check_consts/resolver.rs | 10 +++ .../rustc_const_eval/src/interpret/step.rs | 5 ++ compiler/rustc_middle/src/mir/pretty.rs | 2 + compiler/rustc_middle/src/mir/statement.rs | 4 +- compiler/rustc_middle/src/mir/syntax.rs | 3 + compiler/rustc_middle/src/mir/visit.rs | 10 +++ .../src/impls/borrowed_locals.rs | 2 +- .../src/move_paths/builder.rs | 5 +- .../src/dataflow_const_prop.rs | 3 +- compiler/rustc_mir_transform/src/gvn.rs | 6 ++ .../src/known_panics_lint.rs | 6 +- compiler/rustc_mir_transform/src/lint.rs | 3 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 2 +- compiler/rustc_public/src/mir/body.rs | 3 + compiler/rustc_public/src/mir/pretty.rs | 3 + compiler/rustc_public/src/mir/visit.rs | 2 +- .../src/unstable/convert/stable/mir.rs | 1 + .../clippy_utils/src/qualify_min_const_fn.rs | 1 + 26 files changed, 163 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4a059481c326b..c65ebde3ffcd7 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1593,6 +1593,26 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), + &Rvalue::Reborrow(place) => { + let access_kind =(Deep, Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow }))); + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + state, + ); + + let action = InitializationRequiringAction::Borrow; + + self.check_if_path_or_subpath_is_moved( + location, + action, + (place.as_ref(), span), + state, + ); + } } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 99567da92ffe7..f490b91f40419 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -325,6 +325,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), + &Rvalue::Reborrow(place) => { + let access_kind = (Deep, Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow }))); + + self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); + } } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 676b45e9974cb..f1991914f755e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -558,6 +558,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location:Location) { + // check rvalue is Reborrow + if let Rvalue::Reborrow(rvalue) = rvalue { + self.add_generic_reborrow_constraint(location, place, rvalue); + } else { + // rest of the cases + self.super_assign(place, rvalue, location); + } + } + fn visit_span(&mut self, span: Span) { if !span.is_dummy() { debug!(?span); @@ -1600,6 +1610,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.add_reborrow_constraint(location, *region, borrowed_place); } + Rvalue::BinaryOp( BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), @@ -1677,7 +1688,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::Reborrow(_) => {} } } @@ -2243,7 +2255,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) - | Rvalue::WrapUnsafeBinder(..) => None, + | Rvalue::WrapUnsafeBinder(..) + | Rvalue::Reborrow(_) => None, Rvalue::Aggregate(aggregate, _) => match **aggregate { AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, @@ -2441,6 +2454,56 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + fn add_generic_reborrow_constraint( + &mut self, + location: Location, + dest: &Place<'tcx>, + borrowed_place: &Place<'tcx> + ) { + // In Polonius mode, we also push a `loan_issued_at` fact + // linking the loan to the region (in some cases, though, + // there is no loan associated with this borrow expression -- + // that occurs when we are borrowing an unsafe place, for + // example). + // if let Some(polonius_facts) = polonius_facts { + // let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + // if let Some(borrow_index) = borrow_set.get_index_of(&location) { + // let region_vid = borrow_region.as_var(); + // polonius_facts.loan_issued_at.push(( + // region_vid.into(), + // borrow_index, + // location_table.mid_index(location), + // )); + // } + // } + + // If we are reborrowing the referent of another reference, we + // need to add outlives relationships. In a case like `&mut + // *p`, where the `p` has type `&'b mut Foo`, for example, we + // need to ensure that `'b: 'a`. + + debug!( + "add_generic_reborrow_constraint({:?}, {:?}, {:?})", + location, dest, borrowed_place + ); + + let tcx = self.infcx.tcx; + let def = self.body.source.def_id().expect_local(); + let upvars = tcx.closure_captures(def); + let field = + path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), self.body); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(field) + } else { + ConstraintCategory::Boring + }; + + let dest_ty = dest.ty(self.body, tcx).ty; + let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; + + self.relate_types(borrowed_ty, ty::Variance::Covariant, dest_ty, location.to_locations(), category).unwrap(); + } + fn prove_aggregate_predicates( &mut self, aggregate_kind: &AggregateKind<'tcx>, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1a916c8768243..1a8a48823003b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -624,6 +624,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let val = codegen_operand(fx, operand); lval.write_cvalue(fx, val); } + Rvalue::Reborrow(place) => { + let cplace = codegen_place(fx, place); + let val = cplace.to_cvalue(fx); + lval.write_cvalue(fx, val) + } Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let place = codegen_place(fx, place); let ref_ = place.place_ref(fx, lval.layout()); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ca8c8dd06ba61..c0b1e028958a0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -711,6 +711,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), + mir::Rvalue::Reborrow(place) => { + self.codegen_operand(bx, &mir::Operand::Copy(place)) + } } } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 95dbf42d4d441..7ced9732c30d2 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -699,6 +699,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::WrapUnsafeBinder(..) => { // Unsafe binders are always trivial to create. } + + Rvalue::Reborrow(_) => {} } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index fe3891d0dd7e0..537bc10df9219 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -276,6 +276,8 @@ where // Otherwise, proceed structurally... operands.iter().any(|o| in_operand::(cx, in_local, o)) } + + Rvalue::Reborrow(place) => in_place::(cx, in_local, place.as_ref()), } } diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index d4cc21996aea8..a2a7b871b1013 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -191,6 +191,16 @@ where } } + mir::Rvalue::Reborrow(borrowed_place) => { + if !borrowed_place.is_indirect() { + let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty; + if Q::in_any_value_of_ty(self.ccx, place_ty) { + self.state.qualif.insert(borrowed_place.local); + self.state.borrow.insert(borrowed_place.local); + } + } + } + mir::Rvalue::Cast(..) | mir::Rvalue::ShallowInitBox(..) | mir::Rvalue::Use(..) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 4aa9030cfe61c..574578780a35f 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -275,6 +275,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let op = self.eval_operand(op, None)?; self.copy_op_allow_transmute(&op, &dest)?; } + + Reborrow(place) => { + let op = self.eval_place_to_op(place, Some(dest.layout))?; + self.copy_op(&op, &dest)?; + } } trace!("{:?}", self.dump_place(&dest)); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ded02595563c9..0aa31e3402e1d 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1244,6 +1244,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { WrapUnsafeBinder(ref op, ty) => { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } + + Reborrow(ref place) => write!(fmt, "reborrow {place:?}"), } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 393e9c59c3556..ab2e95dfa8f73 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -769,7 +769,8 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) | Rvalue::ShallowInitBox(_, _) - | Rvalue::WrapUnsafeBinder(_, _) => true, + | Rvalue::WrapUnsafeBinder(_, _) + | Rvalue::Reborrow(..) => true, } } @@ -818,6 +819,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, Rvalue::WrapUnsafeBinder(_, ty) => ty, + Rvalue::Reborrow(ref place) => place.ty(local_decls, tcx).ty, } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6ec874fb15f71..a787fc64e3856 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1479,6 +1479,9 @@ pub enum Rvalue<'tcx> { /// Wraps a value in an unsafe binder. WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), + + /// A reborrowable type being reborrowed + Reborrow(Place<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 07a36aef43201..a52428654feb4 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -819,6 +819,16 @@ macro_rules! make_mir_visitor { self.visit_operand(op, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + + Rvalue::Reborrow(place) => { + self.visit_place( + place, + PlaceContext::MutatingUse( + MutatingUseContext::Borrow + ), + location + ); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 4b2c52ad7999d..77af32e1fb17e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -78,7 +78,7 @@ where // We ignore fake borrows as these get removed after analysis and shouldn't effect // the layout of generators. Rvalue::RawPtr(_, borrowed_place) - | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { + | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) | Rvalue::Reborrow(borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ced9bd735ba2b..cbc6eec852d17 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -448,7 +448,10 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } } Rvalue::CopyForDeref(..) => unreachable!(), - Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) => {} + Rvalue::Ref(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) + | Rvalue::Reborrow(..) => {} } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 604f1da1a3abb..b225ea8aeb2d2 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -467,8 +467,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), - Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"), - Rvalue::Ref(..) | Rvalue::RawPtr(..) => { + Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Reborrow(_) => { // We don't track such places. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 820998eed1005..5915343c0f323 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1031,6 +1031,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let value = match *rvalue { // Forward values. Rvalue::Use(ref mut operand) => return self.simplify_operand(operand, location), + Rvalue::Reborrow(place) => { + let mut operand = Operand::Copy(place); + let val = self.simplify_operand(&mut operand, location); + *rvalue = Rvalue::Use(operand); + return val; + } // Roots. Rvalue::Repeat(ref mut op, amount) => { diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index caaf300a88d64..74a021b2d8935 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -418,8 +418,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } // Do not try creating references (#67862) - Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) => { - trace!("skipping RawPtr | Ref for {:?}", place); + Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(place) => { + trace!("skipping RawPtr | Ref | Reborrow for {:?}", place); // This may be creating mutable references or immutable references to cells. // If that happens, the pointed to value could be mutated via that reference. @@ -551,7 +551,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { self.eval_operand(operand)?.into() } - CopyForDeref(place) => self.eval_place(place)?.into(), + CopyForDeref(place) | Reborrow(place) => self.eval_place(place)?.into(), BinaryOp(bin_op, box (ref left, ref right)) => { let left = self.eval_operand(left)?; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 88297a4efef7e..797a3bc2328fb 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -92,7 +92,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::Ref(..) | Rvalue::RawPtr(..) - | Rvalue::Discriminant(..) => false, + | Rvalue::Discriminant(..) + | Rvalue::Reborrow(_) => false, }; // The sides of an assignment must not alias. if forbid_aliasing { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2cc8e9d6739c8..8e41a6fb3b6a8 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -429,7 +429,7 @@ impl<'tcx> Validator<'_, 'tcx> { | Rvalue::WrapUnsafeBinder(operand, _) => { self.validate_operand(operand)?; } - Rvalue::CopyForDeref(place) => { + Rvalue::CopyForDeref(place) | Rvalue::Reborrow(place) => { let op = &Operand::Copy(*place); self.validate_operand(op)? } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 1afdb4639a0ce..c72c44883afd5 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1016,7 +1016,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; } match rvalue { - Rvalue::Use(_) => {} + Rvalue::Use(_) | Rvalue::Reborrow(_) => {} Rvalue::CopyForDeref(_) => { if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`CopyForDeref` should have been removed in runtime MIR"); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index e81b32ec9acf5..2031bddd19145 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -596,6 +596,8 @@ pub enum Rvalue { /// Yields the operand unchanged Use(Operand), + + Reborrow(Place), } impl Rvalue { @@ -653,6 +655,7 @@ impl Rvalue { }, Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), Rvalue::CopyForDeref(place) => place.ty(locals), + Rvalue::Reborrow(place) => place.ty(locals), } } } diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index 5ba72965cb292..9fc58d6018f70 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -391,6 +391,9 @@ fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { write!(writer, "{:?}({})", un, pretty_operand(op)) } Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), + Rvalue::Reborrow(deref) => { + write!(writer, "Reborrow({deref:?})") + } } } diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 678205171ecf2..3130500c69153 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -265,7 +265,7 @@ macro_rules! make_mir_visitor { self.visit_operand(op, location); self.visit_ty(ty, location); } - Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { + Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) | Rvalue::Reborrow(place) => { self.visit_place(place, PlaceContext::NON_MUTATING, location); } Rvalue::Ref(region, kind, place) => { diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index a77808cfb275d..74d3496e0fa39 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -244,6 +244,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { crate::mir::Rvalue::ShallowInitBox(op.stable(tables, cx), ty.stable(tables, cx)) } CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)), + Reborrow(place) => crate::mir::Rvalue::Reborrow(place.stable(tables, cx)), WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 06220f91c7458..f9505640c6c2f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -130,6 +130,7 @@ fn check_rvalue<'tcx>( check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), + Rvalue::Reborrow(place) => check_place(cx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::WrapUnsafeBinder(operand, _) From a830c43f93b6ff08f444a61ed3cf085875ba30bb Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 23 Dec 2025 20:03:50 +0200 Subject: [PATCH 07/34] fmt --- compiler/rustc_borrowck/src/lib.rs | 7 ++++++- .../src/polonius/legacy/loan_invalidations.rs | 7 ++++++- compiler/rustc_borrowck/src/type_check/mod.rs | 19 +++++++++++-------- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 +--- compiler/rustc_hir_typeck/src/coercion.rs | 9 +++++++-- .../src/impls/borrowed_locals.rs | 3 ++- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index c65ebde3ffcd7..48a502d92e0b9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1594,7 +1594,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), &Rvalue::Reborrow(place) => { - let access_kind =(Deep, Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow }))); + let access_kind = ( + Deep, + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })), + ); self.access_place( location, diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index f490b91f40419..a34d0325c5be6 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -326,7 +326,12 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), &Rvalue::Reborrow(place) => { - let access_kind = (Deep, Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow }))); + let access_kind = ( + Deep, + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })), + ); self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f1991914f755e..e319ad6e8f594 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -558,7 +558,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { - fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location:Location) { + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { // check rvalue is Reborrow if let Rvalue::Reborrow(rvalue) = rvalue { self.add_generic_reborrow_constraint(location, place, rvalue); @@ -1610,7 +1610,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.add_reborrow_constraint(location, *region, borrowed_place); } - Rvalue::BinaryOp( BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), @@ -2458,7 +2457,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &mut self, location: Location, dest: &Place<'tcx>, - borrowed_place: &Place<'tcx> + borrowed_place: &Place<'tcx>, ) { // In Polonius mode, we also push a `loan_issued_at` fact // linking the loan to the region (in some cases, though, @@ -2482,10 +2481,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // *p`, where the `p` has type `&'b mut Foo`, for example, we // need to ensure that `'b: 'a`. - debug!( - "add_generic_reborrow_constraint({:?}, {:?}, {:?})", - location, dest, borrowed_place - ); + debug!("add_generic_reborrow_constraint({:?}, {:?}, {:?})", location, dest, borrowed_place); let tcx = self.infcx.tcx; let def = self.body.source.def_id().expect_local(); @@ -2501,7 +2497,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let dest_ty = dest.ty(self.body, tcx).ty; let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; - self.relate_types(borrowed_ty, ty::Variance::Covariant, dest_ty, location.to_locations(), category).unwrap(); + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); } fn prove_aggregate_predicates( diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index c0b1e028958a0..e43a08f9ee633 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -711,9 +711,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), - mir::Rvalue::Reborrow(place) => { - self.codegen_operand(bx, &mir::Operand::Copy(place)) - } + mir::Rvalue::Reborrow(place) => self.codegen_operand(bx, &mir::Operand::Copy(place)), } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e7534e9c7f4b1..0441799b3a77c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -879,13 +879,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }; if a_def.did() == b_def.did() { // Reborrow is applicable here - self.unify_and(a, b, [], Adjust::GenericReborrow(ty::Mutability::Mut), ForceLeakCheck::No) + self.unify_and( + a, + b, + [], + Adjust::GenericReborrow(ty::Mutability::Mut), + ForceLeakCheck::No, + ) } else { // FIXME: CoerceShared check goes here, error for now Err(TypeError::Mismatch) } } - } fn coerce_from_fn_pointer( &self, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 77af32e1fb17e..ce4fd1378b159 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -78,7 +78,8 @@ where // We ignore fake borrows as these get removed after analysis and shouldn't effect // the layout of generators. Rvalue::RawPtr(_, borrowed_place) - | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) | Rvalue::Reborrow(borrowed_place) => { + | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) + | Rvalue::Reborrow(borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } From 3fa1f2ff3edc72a6c783125d317930ca9e557717 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 23 Dec 2025 20:16:31 +0200 Subject: [PATCH 08/34] fix rebase mistake --- compiler/rustc_builtin_macros/src/deriving/reborrow.rs | 4 +++- compiler/rustc_mir_transform/src/dataflow_const_prop.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs index 0fcf651c49ba4..cb41d6458282c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs +++ b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs @@ -1,4 +1,4 @@ -use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; +use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; @@ -77,6 +77,8 @@ pub(crate) fn expand_deriving_reborrow( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + document: true, + safety: Safety::Default, }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index b225ea8aeb2d2..40b664544bf1c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -467,6 +467,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), + Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"), Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Reborrow(_) => { // We don't track such places. return ValueOrPlace::TOP; From 989b63c1dd88dbccec74e0193ebf1690455e3b70 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Oct 2025 21:24:38 +0300 Subject: [PATCH 09/34] fix --- compiler/rustc_mir_build/src/builder/expr/into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 6027f7618ab44..37733f8c7d3af 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -820,7 +820,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, destination, - Rvalue::Use(Operand::Copy(_1.into())), + Rvalue::Reborrow(_1.into()), ); block.unit() } From 8116826ca35ce0e1b3c14b11a2f190290c614794 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Oct 2025 22:01:17 +0300 Subject: [PATCH 10/34] try actually gather borrows for Rvalue::Reborrow --- compiler/rustc_borrowck/src/borrow_set.rs | 30 ++++++++++++++++++- .../rustc_mir_build/src/builder/expr/into.rs | 7 +---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137fe..65c0f7addc76b 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -5,8 +5,8 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; -use rustc_middle::span_bug; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::{span_bug, ty}; use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; @@ -301,6 +301,34 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { }; self.local_map.entry(borrowed_place.local).or_default().insert(idx); + } else if let &mir::Rvalue::Reborrow(borrowed_place) = rvalue { + let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; + match borrowed_place_ty.kind() { + ty::Adt(_, args) => { + for arg in args.iter() { + let ty::GenericArgKind::Lifetime(region) = arg.kind() else { + continue; + }; + let region = region.as_var(); + let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; + let borrow = BorrowData { + kind, + region, + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place, + assigned_place: *assigned_place, + }; + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); + + self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); + + self.local_map.entry(borrowed_place.local).or_default().insert(idx); + } + } + _ => unreachable!(), + } } self.super_assign(assigned_place, rvalue, location) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 37733f8c7d3af..8271ad5f60166 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -816,12 +816,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let _1 = unpack!( block = this.as_temp(block, this.local_temp_lifetime(), source, mutability) ); - this.cfg.push_assign( - block, - source_info, - destination, - Rvalue::Reborrow(_1.into()), - ); + this.cfg.push_assign(block, source_info, destination, Rvalue::Reborrow(_1.into())); block.unit() } }; From 06311ec13c2cb9265f10e58a7e5b8b38dbfa0071 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 17 Oct 2025 19:55:17 +0300 Subject: [PATCH 11/34] actually nearly kind of sort of working --- compiler/rustc_borrowck/src/borrow_set.rs | 1 + .../rustc_mir_build/src/builder/expr/category.rs | 3 ++- compiler/rustc_mir_build/src/builder/expr/into.rs | 14 ++++++-------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 65c0f7addc76b..16140fd137f62 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -310,6 +310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { continue; }; let region = region.as_var(); + eprintln!("Lifetime borrow: {arg:?} {region:?} {location:?} {borrowed_place:?} {assigned_place:?}"); let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; let borrow = BorrowData { kind, diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 0d3bc655bb042..b706f38aae27a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -43,7 +43,8 @@ impl Category { | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } | ExprKind::PlaceUnwrapUnsafeBinder { .. } - | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::Reborrow { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 8271ad5f60166..153572fb31a38 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -802,10 +802,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } - ExprKind::Reborrow { source, mutability, ty } => { - if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { - panic!("Reborrow currently requires self to be Copy"); - } + ExprKind::Reborrow { source, mutability: _, ty: _ } => { + // if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { + // panic!("Reborrow currently requires self to be Copy"); + // } // let arg_place = match borrow_kind { // BorrowKind::Shared => { // unpack!(block = this.as_read_only_place(block, source)) @@ -813,10 +813,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // _ => unpack!(block = this.as_place(block, source)), // }; // let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); - let _1 = unpack!( - block = this.as_temp(block, this.local_temp_lifetime(), source, mutability) - ); - this.cfg.push_assign(block, source_info, destination, Rvalue::Reborrow(_1.into())); + let place = unpack!(block = this.as_place(block, source)); + this.cfg.push_assign(block, source_info, destination, Rvalue::Reborrow(place.into())); block.unit() } }; From a35386c218e2d0e59bc2031b25aeefcb5de709e8 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 17 Oct 2025 20:34:08 +0300 Subject: [PATCH 12/34] cleanup and fmt --- compiler/rustc_borrowck/src/borrow_set.rs | 50 +++++++++---------- .../rustc_mir_build/src/builder/expr/into.rs | 7 ++- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 16140fd137f62..2b7b56edfeaa0 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -303,32 +303,30 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { self.local_map.entry(borrowed_place.local).or_default().insert(idx); } else if let &mir::Rvalue::Reborrow(borrowed_place) = rvalue { let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; - match borrowed_place_ty.kind() { - ty::Adt(_, args) => { - for arg in args.iter() { - let ty::GenericArgKind::Lifetime(region) = arg.kind() else { - continue; - }; - let region = region.as_var(); - eprintln!("Lifetime borrow: {arg:?} {region:?} {location:?} {borrowed_place:?} {assigned_place:?}"); - let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; - let borrow = BorrowData { - kind, - region, - reserve_location: location, - activation_location: TwoPhaseActivation::NotTwoPhase, - borrowed_place, - assigned_place: *assigned_place, - }; - let (idx, _) = self.location_map.insert_full(location, borrow); - let idx = BorrowIndex::from(idx); - - self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); - - self.local_map.entry(borrowed_place.local).or_default().insert(idx); - } - } - _ => unreachable!(), + let ty::Adt(_, args) = borrowed_place_ty.kind() else { unreachable!() }; + for arg in args.iter() { + let ty::GenericArgKind::Lifetime(region) = arg.kind() else { + continue; + }; + let region = region.as_var(); + eprintln!( + "Lifetime borrow: {arg:?} {region:?} {location:?} {borrowed_place:?} {assigned_place:?}" + ); + let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; + let borrow = BorrowData { + kind, + region, + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place, + assigned_place: *assigned_place, + }; + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); + + self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); + + self.local_map.entry(borrowed_place.local).or_default().insert(idx); } } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 153572fb31a38..59afc652875d6 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -814,7 +814,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // }; // let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); let place = unpack!(block = this.as_place(block, source)); - this.cfg.push_assign(block, source_info, destination, Rvalue::Reborrow(place.into())); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Reborrow(place.into()), + ); block.unit() } }; From daa8aabcb8ec0e3cc6e0fc1d0c91b9af5e49b920 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Sat, 18 Oct 2025 19:10:39 +0000 Subject: [PATCH 13/34] Teach borrowck that reborrow is borrowing Signed-off-by: Xiangfei Ding --- compiler/rustc_borrowck/src/dataflow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 813ceaeb8da9f..8f49f42fdb736 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -549,7 +549,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { ) { match &stmt.kind { mir::StatementKind::Assign(box (lhs, rhs)) => { - if let mir::Rvalue::Ref(_, _, place) = rhs { + if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(place) = rhs { if place.ignore_borrow( self.tcx, self.body, From 39621bd54a9f2c14651707427b4b5a14ae00a58c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 23 Oct 2025 21:09:01 +0300 Subject: [PATCH 14/34] Start CoerceShared impl --- compiler/rustc_borrowck/src/borrow_set.rs | 9 ++- compiler/rustc_borrowck/src/dataflow.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 12 ++-- .../src/polonius/legacy/loan_invalidations.rs | 12 ++-- compiler/rustc_borrowck/src/type_check/mod.rs | 8 ++- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +- .../src/check_consts/check.rs | 2 +- .../src/check_consts/qualifs.rs | 2 +- .../src/check_consts/resolver.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 2 +- compiler/rustc_hir_typeck/src/coercion.rs | 58 +++++++++++++++++++ compiler/rustc_middle/src/mir/pretty.rs | 2 +- compiler/rustc_middle/src/mir/statement.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 2 +- compiler/rustc_middle/src/mir/visit.rs | 2 +- .../rustc_mir_build/src/builder/expr/into.rs | 4 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 8 +-- .../src/impls/borrowed_locals.rs | 2 +- .../src/dataflow_const_prop.rs | 3 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../src/known_panics_lint.rs | 4 +- compiler/rustc_mir_transform/src/lint.rs | 2 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 2 +- .../src/unstable/convert/stable/mir.rs | 2 +- library/core/src/ops/reborrow.rs | 1 + tests/ui/reborrow/custom_marker.rs | 41 +++++++++++++ 27 files changed, 152 insertions(+), 41 deletions(-) create mode 100644 tests/ui/reborrow/custom_marker.rs diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 2b7b56edfeaa0..25db35a358aea 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,6 +2,7 @@ use std::fmt; use std::ops::Index; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_hir::Mutability; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; @@ -301,7 +302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { }; self.local_map.entry(borrowed_place.local).or_default().insert(idx); - } else if let &mir::Rvalue::Reborrow(borrowed_place) = rvalue { + } else if let &mir::Rvalue::Reborrow(mutability, borrowed_place) = rvalue { let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; let ty::Adt(_, args) = borrowed_place_ty.kind() else { unreachable!() }; for arg in args.iter() { @@ -312,7 +313,11 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { eprintln!( "Lifetime borrow: {arg:?} {region:?} {location:?} {borrowed_place:?} {assigned_place:?}" ); - let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; + let kind = if mutability == Mutability::Mut { + mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } + } else { + mir::BorrowKind::Shared + }; let borrow = BorrowData { kind, region, diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8f49f42fdb736..5de83154b54ef 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -549,7 +549,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { ) { match &stmt.kind { mir::StatementKind::Assign(box (lhs, rhs)) => { - if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(place) = rhs { + if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, place) = rhs { if place.ignore_borrow( self.tcx, self.body, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 48a502d92e0b9..448d1a7e39a82 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1593,12 +1593,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), - &Rvalue::Reborrow(place) => { + &Rvalue::Reborrow(mutability, place) => { let access_kind = ( Deep, - Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: MutBorrowKind::TwoPhaseBorrow, - })), + if mutability == Mutability::Mut { + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, ); self.access_place( diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index a34d0325c5be6..2e0f19a543a0f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -325,12 +325,16 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), - &Rvalue::Reborrow(place) => { + &Rvalue::Reborrow(mutability, place) => { let access_kind = ( Deep, - Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: MutBorrowKind::TwoPhaseBorrow, - })), + if mutability == Mutability::Mut { + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::TwoPhaseBorrow, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, ); self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e319ad6e8f594..d0bd3b1a0039e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -560,7 +560,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { // check rvalue is Reborrow - if let Rvalue::Reborrow(rvalue) = rvalue { + if let Rvalue::Reborrow(_, rvalue) = rvalue { self.add_generic_reborrow_constraint(location, place, rvalue); } else { // rest of the cases @@ -2255,7 +2255,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) | Rvalue::WrapUnsafeBinder(..) - | Rvalue::Reborrow(_) => None, + | Rvalue::Reborrow(..) => None, Rvalue::Aggregate(aggregate, _) => match **aggregate { AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, @@ -2497,6 +2497,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let dest_ty = dest.ty(self.body, tcx).ty; let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; + // FIXME: for shared reborrow we need to relate the types manually, + // field by field with CoerceShared drilling down and down and down. + // We cannot just attempt to relate T and ::Target + // by calling relate_types. self.relate_types( borrowed_ty, ty::Variance::Covariant, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e43a08f9ee633..2147207b69b1f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -710,8 +710,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: operand.val, layout, move_annotation: None } } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), - mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), - mir::Rvalue::Reborrow(place) => self.codegen_operand(bx, &mir::Operand::Copy(place)), + mir::Rvalue::Reborrow(_, place) => self.codegen_operand(bx, &mir::Operand::Copy(place)), } } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 7ced9732c30d2..1bbeba2e5ad7b 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -700,7 +700,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Unsafe binders are always trivial to create. } - Rvalue::Reborrow(_) => {} + Rvalue::Reborrow(..) => {} } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 537bc10df9219..2323990025ceb 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -277,7 +277,7 @@ where operands.iter().any(|o| in_operand::(cx, in_local, o)) } - Rvalue::Reborrow(place) => in_place::(cx, in_local, place.as_ref()), + Rvalue::Reborrow(_, place) => in_place::(cx, in_local, place.as_ref()), } } diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index a2a7b871b1013..8c5c8a85afda7 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -191,7 +191,7 @@ where } } - mir::Rvalue::Reborrow(borrowed_place) => { + mir::Rvalue::Reborrow(_, borrowed_place) => { if !borrowed_place.is_indirect() { let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty; if Q::in_any_value_of_ty(self.ccx, place_ty) { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 574578780a35f..be9937bb63d33 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -276,7 +276,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op_allow_transmute(&op, &dest)?; } - Reborrow(place) => { + Reborrow(_, place) => { let op = self.eval_place_to_op(place, Some(dest.layout))?; self.copy_op(&op, &dest)?; } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0441799b3a77c..63e5b0a12c097 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -323,6 +323,24 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, b) } + ty::Adt(_, _) + if self.tcx.features().reborrow() + && self + .fcx + .infcx + .type_implements_trait( + self.tcx + .lang_items() + .coerce_shared() + .expect("Unexpectedly using core/std without reborrow"), + [a], + self.fcx.param_env, + ) + .must_apply_modulo_regions() => + { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); + if reborrow_coerce.is_ok() { reborrow_coerce } else { self.unify(a, b) } + } _ => { // Otherwise, just use unification rules. self.unify(a, b, ForceLeakCheck::No) @@ -892,6 +910,46 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } + /// Applies generic exclusive reborrowing on type implementing `Reborrow`. + #[instrument(skip(self), level = "trace")] + fn coerce_shared_reborrow(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for reborrow. + let (ty::Adt(a_def, _), ty::Adt(b_def, _)) = (a.kind(), b.kind()) else { + return Err(TypeError::Mismatch); + }; + if a_def.did() == b_def.did() { + // CoerceShared cannot be T -> T. + return Err(TypeError::Mismatch); + } + let (Some(coerce_shared_trait_did), Some(coerce_shared_target_did)) = + (self.tcx.lang_items().coerce_shared(), self.tcx.lang_items().coerce_shared_target()) + else { + return Err(TypeError::Mismatch); + }; + let _coerce_shared_trait_ref = ty::TraitRef::new(self.tcx, coerce_shared_trait_did, [a]); + // let obligation = traits::Obligation::new( + // self.tcx, + // ObligationCause::dummy(), + // self.param_env, + // ty::Binder::dummy(coerce_shared_trait_ref), + // ); + let result = Ty::new_projection(self.tcx, coerce_shared_target_did, [a]); + let InferOk { value: result, obligations } = + self.at(&self.cause, self.param_env).normalize(result); + self.unify_and(result, b, [], Adjust::GenericReborrow(ty::Mutability::Not)).map( + |mut infr| { + // infr.obligations.push(obligation); + for obl in obligations { + infr.obligations.push(obl); + } + infr + }, + ) + } + fn coerce_from_fn_pointer( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 0aa31e3402e1d..8275f06e69c41 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1245,7 +1245,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } - Reborrow(ref place) => write!(fmt, "reborrow {place:?}"), + Reborrow(_mutability, ref place) => write!(fmt, "reborrow {place:?}"), } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index ab2e95dfa8f73..5529b0a5cf362 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -819,7 +819,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, Rvalue::WrapUnsafeBinder(_, ty) => ty, - Rvalue::Reborrow(ref place) => place.ty(local_decls, tcx).ty, + Rvalue::Reborrow(_, ref place) => place.ty(local_decls, tcx).ty, } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index a787fc64e3856..d1b6de27113fe 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1481,7 +1481,7 @@ pub enum Rvalue<'tcx> { WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), /// A reborrowable type being reborrowed - Reborrow(Place<'tcx>), + Reborrow(Mutability, Place<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index a52428654feb4..9a8f9a463d132 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -820,7 +820,7 @@ macro_rules! make_mir_visitor { self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - Rvalue::Reborrow(place) => { + Rvalue::Reborrow(_mutability, place) => { self.visit_place( place, PlaceContext::MutatingUse( diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 59afc652875d6..75d04e57b2c12 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -802,7 +802,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } - ExprKind::Reborrow { source, mutability: _, ty: _ } => { + ExprKind::Reborrow { source, mutability, ty: _ } => { // if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { // panic!("Reborrow currently requires self to be Copy"); // } @@ -818,7 +818,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, destination, - Rvalue::Reborrow(place.into()), + Rvalue::Reborrow(mutability, place.into()), ); block.unit() } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ca53ced125beb..6ed81665846fc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -240,17 +240,13 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?kind); kind } - Adjust::GenericReborrow(ty::Mutability::Mut) => { + Adjust::GenericReborrow(mutability) => { let ty = expr.ty; let expr = self.thir.exprs.push(expr); - let kind = - ExprKind::Reborrow { source: expr, mutability: rustc_ast::Mutability::Mut, ty }; + let kind = ExprKind::Reborrow { source: expr, mutability, ty }; kind } - Adjust::GenericReborrow(ty::Mutability::Not) => { - todo!("CoerceShared"); - } }; Expr { temp_scope_id, ty: adjustment.target, span, kind } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index ce4fd1378b159..3ce2a3e9ade64 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -79,7 +79,7 @@ where // the layout of generators. Rvalue::RawPtr(_, borrowed_place) | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) - | Rvalue::Reborrow(borrowed_place) => { + | Rvalue::Reborrow(_, borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen_(borrowed_place.local); } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 40b664544bf1c..4eeebff2a4c64 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -467,8 +467,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), - Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"), - Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Reborrow(_) => { + Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Reborrow(..) => { // We don't track such places. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 5915343c0f323..d29525fe66aad 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1031,7 +1031,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let value = match *rvalue { // Forward values. Rvalue::Use(ref mut operand) => return self.simplify_operand(operand, location), - Rvalue::Reborrow(place) => { + Rvalue::Reborrow(_, place) => { let mut operand = Operand::Copy(place); let val = self.simplify_operand(&mut operand, location); *rvalue = Rvalue::Use(operand); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 74a021b2d8935..97c455949d289 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -418,7 +418,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } // Do not try creating references (#67862) - Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(place) => { + Rvalue::RawPtr(_, place) | Rvalue::Ref(_, _, place) | Rvalue::Reborrow(_, place) => { trace!("skipping RawPtr | Ref | Reborrow for {:?}", place); // This may be creating mutable references or immutable references to cells. @@ -551,7 +551,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { self.eval_operand(operand)?.into() } - CopyForDeref(place) | Reborrow(place) => self.eval_place(place)?.into(), + CopyForDeref(place) | Reborrow(_, place) => self.eval_place(place)?.into(), BinaryOp(bin_op, box (ref left, ref right)) => { let left = self.eval_operand(left)?; diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 797a3bc2328fb..f128a0a2e1f2e 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -93,7 +93,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { | Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) - | Rvalue::Reborrow(_) => false, + | Rvalue::Reborrow(..) => false, }; // The sides of an assignment must not alias. if forbid_aliasing { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 8e41a6fb3b6a8..1b44bba99383e 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -429,7 +429,7 @@ impl<'tcx> Validator<'_, 'tcx> { | Rvalue::WrapUnsafeBinder(operand, _) => { self.validate_operand(operand)?; } - Rvalue::CopyForDeref(place) | Rvalue::Reborrow(place) => { + Rvalue::CopyForDeref(place) | Rvalue::Reborrow(_, place) => { let op = &Operand::Copy(*place); self.validate_operand(op)? } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index c72c44883afd5..e744eab60e81a 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1016,7 +1016,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; } match rvalue { - Rvalue::Use(_) | Rvalue::Reborrow(_) => {} + Rvalue::Use(_) | Rvalue::Reborrow(..) => {} Rvalue::CopyForDeref(_) => { if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`CopyForDeref` should have been removed in runtime MIR"); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 74d3496e0fa39..f59391906148b 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -244,7 +244,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { crate::mir::Rvalue::ShallowInitBox(op.stable(tables, cx), ty.stable(tables, cx)) } CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)), - Reborrow(place) => crate::mir::Rvalue::Reborrow(place.stable(tables, cx)), + Reborrow(_, place) => crate::mir::Rvalue::Reborrow(place.stable(tables, cx)), WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs index f83f4233a4de5..3ffbc9f7b7a16 100644 --- a/library/core/src/ops/reborrow.rs +++ b/library/core/src/ops/reborrow.rs @@ -11,6 +11,7 @@ pub trait Reborrow { #[lang = "coerce_shared"] #[unstable(feature = "reborrow", issue = "145612")] pub trait CoerceShared: Reborrow { + #[lang = "coerce_shared_target"] /// The type of this value when reborrowed as shared. type Target: Copy; } diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs new file mode 100644 index 0000000000000..0423d99c89159 --- /dev/null +++ b/tests/ui/reborrow/custom_marker.rs @@ -0,0 +1,41 @@ +#![feature(reborrow)] +use std::ops::{CoerceShared, Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +impl<'a> CoerceShared for CustomMarker<'a> { + type Target = CustomMarkerRef<'a>; +} + +impl<'a> From> for CustomMarkerRef<'a> { + fn from(value: CustomMarker<'a>) -> Self { + Self(PhantomData) + } +} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn method_two<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let mut a = CustomMarker(PhantomData); + let b = method(a); + // let c = method(a); // should invalidate b + let c = method_two(a); // should invalidate b + println!("{c:?} {b:?} {a:?}"); +} + +// fn main_using_normal_references() { +// let a = &mut (); +// let b = method(a); +// let _ = method(a); +// eprintln!("{b}"); +// } From ebae325f1cb05ec7e3f229f8a8af8b208ba31f45 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 31 Oct 2025 16:38:59 +0200 Subject: [PATCH 15/34] temp --- compiler/rustc_borrowck/src/type_check/mod.rs | 60 +++-- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- .../src/coherence/builtin.rs | 229 +++++++++++++++++- .../rustc_hir_analysis/src/coherence/mod.rs | 3 +- compiler/rustc_metadata/src/rmeta/mod.rs | 1 + .../rustc_metadata/src/rmeta/parameterized.rs | 1 + compiler/rustc_middle/src/mir/pretty.rs | 4 +- compiler/rustc_middle/src/query/erase.rs | 7 +- compiler/rustc_middle/src/query/mod.rs | 8 + compiler/rustc_middle/src/ty/adjustment.rs | 12 + .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- 11 files changed, 308 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d0bd3b1a0039e..0aee6e353328d 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -560,8 +560,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { // check rvalue is Reborrow - if let Rvalue::Reborrow(_, rvalue) = rvalue { - self.add_generic_reborrow_constraint(location, place, rvalue); + if let Rvalue::Reborrow(mutability, rvalue) = rvalue { + self.add_generic_reborrow_constraint(*mutability, location, place, rvalue); } else { // rest of the cases self.super_assign(place, rvalue, location); @@ -2455,6 +2455,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn add_generic_reborrow_constraint( &mut self, + mutability: Mutability, location: Location, dest: &Place<'tcx>, borrowed_place: &Place<'tcx>, @@ -2481,7 +2482,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // *p`, where the `p` has type `&'b mut Foo`, for example, we // need to ensure that `'b: 'a`. - debug!("add_generic_reborrow_constraint({:?}, {:?}, {:?})", location, dest, borrowed_place); + debug!("add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})", mutability, location, dest, borrowed_place); let tcx = self.infcx.tcx; let def = self.body.source.def_id().expect_local(); @@ -2497,18 +2498,47 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let dest_ty = dest.ty(self.body, tcx).ty; let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; - // FIXME: for shared reborrow we need to relate the types manually, - // field by field with CoerceShared drilling down and down and down. - // We cannot just attempt to relate T and ::Target - // by calling relate_types. - self.relate_types( - borrowed_ty, - ty::Variance::Covariant, - dest_ty, - location.to_locations(), - category, - ) - .unwrap(); + if mutability.is_not() { + // FIXME: for shared reborrow we need to relate the types manually, + // field by field with CoerceShared drilling down and down and down. + // We cannot just attempt to relate T and ::Target + // by calling relate_types. + let dest_adt = dest_ty.ty_adt_def().unwrap(); + let ty::Adt(_, dest_args) = dest_ty.kind() else { + unreachable!() + }; + let ty::Adt(_, borrowed_args) = borrowed_ty.kind() else { + unreachable!() + }; + let borrowed_adt = borrowed_ty.ty_adt_def().unwrap(); + let borrowed_fields = borrowed_adt.all_fields().collect::>(); + for dest_field in dest_adt.all_fields() { + let Some(borrowed_field) = borrowed_fields.iter().find(|f| f.name == dest_field.name) else { + continue; + }; + let dest_ty = dest_field.ty(tcx, dest_args); + let borrowed_ty = borrowed_field.ty(tcx, borrowed_args); + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } else { + // Exclusive reborrow + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } + } fn prove_aggregate_predicates( diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1a8a48823003b..7a845dc5ef209 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -624,7 +624,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let val = codegen_operand(fx, operand); lval.write_cvalue(fx, val); } - Rvalue::Reborrow(place) => { + Rvalue::Reborrow(_, place) => { let cplace = codegen_place(fx, place); let val = cplace.to_cvalue(fx); lval.write_cvalue(fx, val) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61562cc1e4f30..0beae9bbd22b3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -11,7 +11,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{self, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::adjustment::{CoerceSharedInfo, CoerceUnsizedInfo}; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, @@ -42,6 +42,7 @@ pub(super) fn check_trait<'tcx>( visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; + checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?; checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; checker.check( @@ -192,6 +193,17 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E tcx.ensure_ok().coerce_unsized_info(impl_did) } +fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_coerce_shared: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + tcx.ensure_ok().coerce_shared_info(impl_did) +} + fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool { span.ctxt() .outer_expn_data() @@ -377,6 +389,221 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } +pub(crate) fn coerce_shared_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result { + debug!("compute_coerce_shared_info(impl_did={:?})", impl_did); + let span = tcx.def_span(impl_did); + let trait_name = "CoerceShared"; + + let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); + let copy_trait = tcx.require_lang_item(LangItem::Copy, span); + + let source = tcx.type_of(impl_did).instantiate_identity(); + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + + assert_eq!(trait_ref.def_id, coerce_shared_trait); + let target = trait_ref.args.type_at(1); + debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (bound)", source, target); + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (free)", source, target); + + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = ObligationCause::misc(span, impl_did); + let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, + mt_b: ty::TypeAndMut<'tcx>, + mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { + if mt_a.mutbl < mt_b.mutbl { + infcx + .err_ctxt() + .report_mismatched_types( + &cause, + param_env, + mk_ptr(mt_b.ty), + target, + ty::error::TypeError::Mutability, + ) + .emit(); + } + (mt_a.ty, mt_b.ty, copy_trait, None, span) + }; + let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) { + (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { + infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a); + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) + } + + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) + | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) + } + + (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did()); + let target_path = tcx.def_path_str(def_b.did()); + return Err(tcx.dcx().emit_err(errors::CoerceSameStruct { + span, + trait_name, + note: true, + source_path, + target_path, + })); + } + + // Here we are considering a case of converting + // `S` to `S`. As an example, let's imagine a struct `Foo`, + // which acts like a pointer to `U`, but carries along some extra data of type `T`: + // + // struct Foo { + // extra: T, + // ptr: *mut U, + // } + // + // We might have an impl that allows (e.g.) `Foo` to be unsized + // to `Foo`. That impl would look like: + // + // impl, V> CoerceUnsized> for Foo {} + // + // Here `U = [i32; 3]` and `V = [i32]`. At runtime, + // when this coercion occurs, we would be changing the + // field `ptr` from a thin pointer of type `*mut [i32; + // 3]` to a wide pointer of type `*mut [i32]` (with + // extra data `3`). **The purpose of this check is to + // make sure that we know how to do this conversion.** + // + // To check if this impl is legal, we would walk down + // the fields of `Foo` and consider their types with + // both generic parameters. We are looking to find that + // exactly one (non-phantom) field has changed its + // type, which we will expect to be the pointer that + // is becoming fat (we could probably generalize this + // to multiple thin pointers of the same type becoming + // fat, but we don't). In this case: + // + // - `extra` has type `T` before and type `T` after + // - `ptr` has type `*mut U` before and type `*mut V` after + // + // Since just one field changed, we would then check + // that `*mut U: CoerceUnsized<*mut V>` is implemented + // (in other words, that we know how to do this + // conversion). This will work out because `U: + // Unsize`, and we have a builtin rule that `*mut + // U` can be coerced to `*mut V` if `U: Unsize`. + let fields = &def_a.non_enum_variant().fields; + let diff_fields = fields + .iter_enumerated() + .filter_map(|(i, f)| { + let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b)); + + // Ignore PhantomData fields + let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); + if tcx + .try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, def_a.did()), + unnormalized_ty, + ) + .unwrap_or(unnormalized_ty) + .is_phantom_data() + { + return None; + } + + // Ignore fields that aren't changed; it may + // be that we could get away with subtyping or + // something more accepting, but we use + // equality because we want to be able to + // perform this check without computing + // variance or constraining opaque types' hidden types. + // (This is because we may have to evaluate constraint + // expressions in the course of execution.) + // See e.g., #41936. + if a == b { + return None; + } + + // Collect up all fields that were significantly changed + // i.e., those that contain T in coerce_unsized T -> U + Some((i, a, b, tcx.def_span(f.did))) + }) + .collect::>(); + + if diff_fields.is_empty() { + return Err(tcx.dcx().emit_err(errors::CoerceNoField { + span, + trait_name, + note: true, + })); + } else if diff_fields.len() > 1 { + let item = tcx.hir_expect_item(impl_did); + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = + &item.kind + { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceMulti { + span, + trait_name, + number: diff_fields.len(), + fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::>().into(), + })); + } + + let (i, a, b, field_span) = diff_fields[0]; + let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); + (a, b, coerce_shared_trait, Some(kind), field_span) + } + + _ => { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + // Register an obligation for `A: Trait`. + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, trait_def_id, [source, target]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + if is_from_coerce_pointee_derive(tcx, span) { + return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity { + span, + trait_name, + ty: trait_ref.self_ty(), + field_span, + field_ty: source, + })); + } else { + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } + } + + // Finally, resolve all regions. + ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + + Ok(CoerceSharedInfo { is_trivial: true, participating_lifetimes: 1 }) +} + pub(crate) fn coerce_unsized_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 58483dc44fe91..e509b1d0de8dc 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -130,7 +130,7 @@ fn enforce_empty_impls_for_marker_traits( /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]. pub(crate) fn provide(providers: &mut Providers) { - use self::builtin::coerce_unsized_info; + use self::builtin::{coerce_unsized_info, coerce_shared_info}; use self::inherent_impls::{ crate_incoherent_impls, crate_inherent_impls, crate_inherent_impls_validity_check, inherent_impls, @@ -145,6 +145,7 @@ pub(crate) fn provide(providers: &mut Providers) { inherent_impls, crate_inherent_impls_validity_check, crate_inherent_impls_overlap_check, + coerce_shared_info, coerce_unsized_info, orphan_check_impl, ..*providers diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index af6df0cd6eb61..0bd75c4facc45 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -440,6 +440,7 @@ define_tables! { thir_abstract_const: Table>>>, impl_parent: Table, const_conditions: Table>>, + coerce_shared_info: Table>, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table>, mir_const_qualif: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 8a9de07836db4..b1302deecb41e 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -117,6 +117,7 @@ trivially_parameterized_over_tcx! { rustc_middle::ty::TraitDef, rustc_middle::ty::Variance, rustc_middle::ty::Visibility, + rustc_middle::ty::adjustment::CoerceSharedInfo, rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_middle::ty::fast_reject::SimplifiedType, rustc_session::config::TargetModifier, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8275f06e69c41..763460b2a0b75 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1245,7 +1245,9 @@ impl<'tcx> Debug for Rvalue<'tcx> { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } - Reborrow(_mutability, ref place) => write!(fmt, "reborrow {place:?}"), + Reborrow(mutability, ref place) => if mutability.is_mut() { write!(fmt, "reborrow {place:?}") } else { + write!(fmt, "coerceshared {place:?}") + }, } } } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 940cc30c17e6e..b06d735b63f8d 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -10,7 +10,7 @@ use crate::mir::interpret::EvalToValTreeResult; use crate::mir::mono::{MonoItem, NormalizationErrorInMono}; use crate::query::CyclePlaceholder; use crate::traits::solve; -use crate::ty::adjustment::CoerceUnsizedInfo; +use crate::ty::adjustment::{CoerceSharedInfo, CoerceUnsizedInfo}; use crate::ty::{self, Ty, TyCtxt}; use crate::{mir, traits}; @@ -124,6 +124,10 @@ impl EraseType for Result>, rustc_errors::ErrorGuarantee [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; } +impl EraseType for Result { + type Result = [u8; size_of::>()]; +} + impl EraseType for Result { type Result = [u8; size_of::>()]; } @@ -341,6 +345,7 @@ trivial! { rustc_middle::traits::OverflowError, rustc_middle::traits::query::NoSolution, rustc_middle::traits::WellFormedLoc, + rustc_middle::ty::adjustment::CoerceSharedInfo, rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_middle::ty::AssocItem, rustc_middle::ty::AssocContainer, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9d17c998a8f29..141420e30d9eb 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1247,6 +1247,14 @@ rustc_queries! { return_result_from_ensure_ok } + /// Caches `CoerceShared` kinds for impls on custom types. + query coerce_shared_info(key: DefId) -> Result { + desc { |tcx| "computing CoerceShared info for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + return_result_from_ensure_ok + } + /// Caches `CoerceUnsized` kinds for impls on custom types. query coerce_unsized_info(key: DefId) -> Result { desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 9594a59eff7fb..61f6078009e30 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -196,6 +196,18 @@ pub enum AutoBorrow { RawPtr(hir::Mutability), } +/// Information for `CoerceShared` impls, storing information we +/// have computed about the coercion. +/// +/// This struct can be obtained via the `coerce_shared_impl` query. +/// Demanding this struct also has the side-effect of reporting errors +/// for inappropriate impls. +#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] +pub struct CoerceSharedInfo { + pub is_trivial: bool, + pub participating_lifetimes: u8, +} + /// Information for `CoerceUnsized` impls, storing information we /// have computed about the coercion. /// diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f9505640c6c2f..0eedf69aa8011 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -130,7 +130,7 @@ fn check_rvalue<'tcx>( check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), - Rvalue::Reborrow(place) => check_place(cx, *place, span, body, msrv), + Rvalue::Reborrow(_, place) => check_place(cx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::WrapUnsafeBinder(operand, _) From 66a54cf1598650830d0758a4dbca29f95d5dc331 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 6 Nov 2025 21:45:18 +0200 Subject: [PATCH 16/34] coerce_unsized_info --- compiler/rustc_borrowck/src/borrow_set.rs | 3 - compiler/rustc_borrowck/src/type_check/mod.rs | 18 +- .../src/coherence/builtin.rs | 254 ++++++++---------- .../rustc_hir_analysis/src/coherence/mod.rs | 2 +- compiler/rustc_hir_analysis/src/errors.rs | 8 + compiler/rustc_middle/src/mir/pretty.rs | 10 +- compiler/rustc_middle/src/ty/adjustment.rs | 5 +- 7 files changed, 136 insertions(+), 164 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 25db35a358aea..4c57e3cf39810 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -310,9 +310,6 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { continue; }; let region = region.as_var(); - eprintln!( - "Lifetime borrow: {arg:?} {region:?} {location:?} {borrowed_place:?} {assigned_place:?}" - ); let kind = if mutability == Mutability::Mut { mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } } else { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0aee6e353328d..60a7edb004d9e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2482,7 +2482,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // *p`, where the `p` has type `&'b mut Foo`, for example, we // need to ensure that `'b: 'a`. - debug!("add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})", mutability, location, dest, borrowed_place); + debug!( + "add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})", + mutability, location, dest, borrowed_place + ); let tcx = self.infcx.tcx; let def = self.body.source.def_id().expect_local(); @@ -2504,16 +2507,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // We cannot just attempt to relate T and ::Target // by calling relate_types. let dest_adt = dest_ty.ty_adt_def().unwrap(); - let ty::Adt(_, dest_args) = dest_ty.kind() else { - unreachable!() - }; - let ty::Adt(_, borrowed_args) = borrowed_ty.kind() else { - unreachable!() - }; + let ty::Adt(_, dest_args) = dest_ty.kind() else { unreachable!() }; + let ty::Adt(_, borrowed_args) = borrowed_ty.kind() else { unreachable!() }; let borrowed_adt = borrowed_ty.ty_adt_def().unwrap(); let borrowed_fields = borrowed_adt.all_fields().collect::>(); for dest_field in dest_adt.all_fields() { - let Some(borrowed_field) = borrowed_fields.iter().find(|f| f.name == dest_field.name) else { + let Some(borrowed_field) = + borrowed_fields.iter().find(|f| f.name == dest_field.name) + else { continue; }; let dest_ty = dest_field.ty(tcx, dest_args); @@ -2538,7 +2539,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) .unwrap(); } - } fn prove_aggregate_predicates( diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 0beae9bbd22b3..914096f7953a4 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -9,8 +9,8 @@ use rustc_hir as hir; use rustc_hir::ItemKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::{self, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; -use rustc_infer::traits::Obligation; +use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::adjustment::{CoerceSharedInfo, CoerceUnsizedInfo}; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ @@ -389,22 +389,64 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } +fn structurally_normalize_ty<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + span: Span, + ty: Ty<'tcx>, +) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { + let ocx = ObligationCtxt::new(infcx); + let Ok(normalized_ty) = ocx.structurally_normalize_ty( + &traits::ObligationCause::misc(span, impl_did), + tcx.param_env(impl_did), + ty, + ) else { + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + return None; + }; + let errors = ocx.try_evaluate_obligations(); + if !errors.is_empty() { + if infcx.next_trait_solver() { + unreachable!(); + } + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + debug!(?errors, "encountered errors while fulfilling"); + return None; + } + + Some((normalized_ty, ocx.into_pending_obligations())) +} + pub(crate) fn coerce_shared_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, ) -> Result { debug!("compute_coerce_shared_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let span = tcx.def_span(impl_did); let trait_name = "CoerceShared"; let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); - let copy_trait = tcx.require_lang_item(LangItem::Copy, span); + let coerce_shared_target = tcx.require_lang_item(LangItem::CoerceSharedTarget, span); + let _copy_trait = tcx.require_lang_item(LangItem::Copy, span); let source = tcx.type_of(impl_did).instantiate_identity(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); assert_eq!(trait_ref.def_id, coerce_shared_trait); - let target = trait_ref.args.type_at(1); + let Some((target, _obligations)) = structurally_normalize_ty( + tcx, + &infcx, + impl_did, + span, + Ty::new_projection(tcx, coerce_shared_target, trait_ref.args), + ) else { + // return Err(tcx.dcx().emit_err(todo!())); + todo!(); + }; debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (bound)", source, target); let param_env = tcx.param_env(impl_did); @@ -412,100 +454,22 @@ pub(crate) fn coerce_shared_info<'tcx>( debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (free)", source, target); - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let cause = ObligationCause::misc(span, impl_did); - let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, - mt_b: ty::TypeAndMut<'tcx>, - mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { - if mt_a.mutbl < mt_b.mutbl { - infcx - .err_ctxt() - .report_mismatched_types( - &cause, - param_env, - mk_ptr(mt_b.ty), - target, - ty::error::TypeError::Mutability, - ) - .emit(); - } - (mt_a.ty, mt_b.ty, copy_trait, None, span) - }; - let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) { - (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { - infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a); - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) - } - - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) - | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } - + let _cause = ObligationCause::misc(span, impl_did); + let data = match (source.kind(), target.kind()) { (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did()); - let target_path = tcx.def_path_str(def_b.did()); - return Err(tcx.dcx().emit_err(errors::CoerceSameStruct { - span, - trait_name, - note: true, - source_path, - target_path, - })); - } + // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()> } -> struct FooRef<'a> { a: u32, b: PhantomData<&'a ()> } + // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()>, c: u32 } -> struct FooRef<'a> { a: u32, b: PhantomData<&'a ()> } + // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()>, c: u32 } -> struct FooRef<'a> { c: u32, a: u32, b: PhantomData<&'a ()> } - // Here we are considering a case of converting - // `S` to `S`. As an example, let's imagine a struct `Foo`, - // which acts like a pointer to `U`, but carries along some extra data of type `T`: - // - // struct Foo { - // extra: T, - // ptr: *mut U, - // } - // - // We might have an impl that allows (e.g.) `Foo` to be unsized - // to `Foo`. That impl would look like: - // - // impl, V> CoerceUnsized> for Foo {} - // - // Here `U = [i32; 3]` and `V = [i32]`. At runtime, - // when this coercion occurs, we would be changing the - // field `ptr` from a thin pointer of type `*mut [i32; - // 3]` to a wide pointer of type `*mut [i32]` (with - // extra data `3`). **The purpose of this check is to - // make sure that we know how to do this conversion.** - // - // To check if this impl is legal, we would walk down - // the fields of `Foo` and consider their types with - // both generic parameters. We are looking to find that - // exactly one (non-phantom) field has changed its - // type, which we will expect to be the pointer that - // is becoming fat (we could probably generalize this - // to multiple thin pointers of the same type becoming - // fat, but we don't). In this case: - // - // - `extra` has type `T` before and type `T` after - // - `ptr` has type `*mut U` before and type `*mut V` after - // - // Since just one field changed, we would then check - // that `*mut U: CoerceUnsized<*mut V>` is implemented - // (in other words, that we know how to do this - // conversion). This will work out because `U: - // Unsize`, and we have a builtin rule that `*mut - // U` can be coerced to `*mut V` if `U: Unsize`. - let fields = &def_a.non_enum_variant().fields; - let diff_fields = fields + // Parent { Child, u32, i32 }, Child { u64, u32 } => ParentRef { ChildRef, u32 }, ChildRef { u64 } + + let a_data_fields = def_a + .non_enum_variant() + .fields .iter_enumerated() .filter_map(|(i, f)| { - let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b)); - // Ignore PhantomData fields let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); if tcx @@ -519,32 +483,32 @@ pub(crate) fn coerce_shared_info<'tcx>( return None; } - // Ignore fields that aren't changed; it may - // be that we could get away with subtyping or - // something more accepting, but we use - // equality because we want to be able to - // perform this check without computing - // variance or constraining opaque types' hidden types. - // (This is because we may have to evaluate constraint - // expressions in the course of execution.) - // See e.g., #41936. - if a == b { + let a = f.ty(tcx, args_a); + + // Collect up all fields that were significantly changed. + Some((i, a, tcx.def_span(f.did))) + }) + .collect::>(); + let b_data_fields = def_b + .non_enum_variant() + .fields + .iter_enumerated() + .filter_map(|(i, f)| { + let b = f.ty(tcx, args_b); + + if b.is_phantom_data() { return None; } - // Collect up all fields that were significantly changed - // i.e., those that contain T in coerce_unsized T -> U - Some((i, a, b, tcx.def_span(f.did))) + // Collect up all fields that were significantly changed. + Some((i, b, tcx.def_span(f.did))) }) .collect::>(); - if diff_fields.is_empty() { - return Err(tcx.dcx().emit_err(errors::CoerceNoField { - span, - trait_name, - note: true, - })); - } else if diff_fields.len() > 1 { + if a_data_fields.len() > 1 + || b_data_fields.len() > 1 + || a_data_fields.len() != b_data_fields.len() + { let item = tcx.hir_expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind @@ -554,54 +518,56 @@ pub(crate) fn coerce_shared_info<'tcx>( tcx.def_span(impl_did) }; + let mut fields = a_data_fields.iter().map(|(_, _, s)| *s).collect::>(); + fields.extend(b_data_fields.iter().map(|(_, _, s)| *s)); return Err(tcx.dcx().emit_err(errors::CoerceMulti { span, trait_name, - number: diff_fields.len(), - fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::>().into(), + number: a_data_fields.len() + b_data_fields.len(), + fields: fields.into(), })); } - let (i, a, b, field_span) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_shared_trait, Some(kind), field_span) + let kind = ty::adjustment::CoerceSharedInfo {}; + if a_data_fields.len() == 1 { + let (_a_i, a, span_a) = a_data_fields[0]; + let (_b_i, b, span_b) = b_data_fields[0]; + + Some((a, b, coerce_shared_trait, kind, span_a, span_b)) + } else { + None + } } _ => { - return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + return Err(tcx.dcx().emit_err(errors::CoerceSharedNonStruct { span, trait_name })); } }; - // Register an obligation for `A: Trait`. - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, impl_did); - let obligation = Obligation::new( - tcx, - cause, - param_env, - ty::TraitRef::new(tcx, trait_def_id, [source, target]), - ); - ocx.register_obligation(obligation); - let errors = ocx.evaluate_obligations_error_on_ambiguity(); - - if !errors.is_empty() { - if is_from_coerce_pointee_derive(tcx, span) { - return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity { - span, - trait_name, - ty: trait_ref.self_ty(), - field_span, - field_ty: source, - })); - } else { + if let Some((source, target, trait_def_id, _kind, _source_field_span, _target_field_span)) = + data + { + // Register an obligation for `A: Trait`. + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, trait_def_id, [source, target]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } - } - // Finally, resolve all regions. - ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + // Finally, resolve all regions. + ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + } - Ok(CoerceSharedInfo { is_trivial: true, participating_lifetimes: 1 }) + Ok(CoerceSharedInfo {}) } pub(crate) fn coerce_unsized_info<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index e509b1d0de8dc..06abcff12d953 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -130,7 +130,7 @@ fn enforce_empty_impls_for_marker_traits( /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]. pub(crate) fn provide(providers: &mut Providers) { - use self::builtin::{coerce_unsized_info, coerce_shared_info}; + use self::builtin::{coerce_shared_info, coerce_unsized_info}; use self::inherent_impls::{ crate_incoherent_impls, crate_inherent_impls, crate_inherent_impls_validity_check, inherent_impls, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2c..d24b3544885b2 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1192,6 +1192,14 @@ pub(crate) struct CoerceMulti { pub fields: MultiSpan, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_unsized_may, code = E0377)] +pub(crate) struct CoerceSharedNonStruct { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] #[diag(hir_analysis_coerce_unsized_may, code = E0377)] pub(crate) struct CoerceUnsizedNonStruct { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 763460b2a0b75..e53a4b32d590c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1245,9 +1245,13 @@ impl<'tcx> Debug for Rvalue<'tcx> { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } - Reborrow(mutability, ref place) => if mutability.is_mut() { write!(fmt, "reborrow {place:?}") } else { - write!(fmt, "coerceshared {place:?}") - }, + Reborrow(mutability, ref place) => { + if mutability.is_mut() { + write!(fmt, "reborrow {place:?}") + } else { + write!(fmt, "coerceshared {place:?}") + } + } } } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 61f6078009e30..0194020e17727 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -203,10 +203,7 @@ pub enum AutoBorrow { /// Demanding this struct also has the side-effect of reporting errors /// for inappropriate impls. #[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] -pub struct CoerceSharedInfo { - pub is_trivial: bool, - pub participating_lifetimes: u8, -} +pub struct CoerceSharedInfo {} /// Information for `CoerceUnsized` impls, storing information we /// have computed about the coercion. From de7dee9760669a44ba9794f68d4dac85c2b1f50d Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 13 Nov 2025 19:41:39 +0200 Subject: [PATCH 17/34] [PATCH] use the right region by Xiangfei Ding --- compiler/rustc_borrowck/src/borrow_set.rs | 11 +++++++++-- compiler/rustc_borrowck/src/lib.rs | 5 +++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4c57e3cf39810..44973a28d091d 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -7,7 +7,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; use rustc_middle::ty::{RegionVid, TyCtxt}; -use rustc_middle::{span_bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; @@ -304,7 +304,14 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { self.local_map.entry(borrowed_place.local).or_default().insert(idx); } else if let &mir::Rvalue::Reborrow(mutability, borrowed_place) = rvalue { let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; - let ty::Adt(_, args) = borrowed_place_ty.kind() else { unreachable!() }; + let &ty::Adt(reborrowed_adt, _args) = borrowed_place_ty.kind() else { unreachable!() }; + let &ty::Adt(target_adt, args) = assigned_place.ty(self.body, self.tcx).ty.kind() + else { + unreachable!() + }; + if target_adt.did() != reborrowed_adt.did() { + bug!("hir-typeck passed but reborrow involves mismatching types at {location:?}") + } for arg in args.iter() { let ty::GenericArgKind::Lifetime(region) = arg.kind() else { continue; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 448d1a7e39a82..d49a6295a6eed 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1254,6 +1254,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let mut error_reported = false; let borrows_in_scope = self.borrows_in_scope(location, state); + debug!(?borrows_in_scope, ?location); each_borrow_involving_path( self, @@ -1597,8 +1598,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let access_kind = ( Deep, if mutability == Mutability::Mut { - Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: MutBorrowKind::TwoPhaseBorrow, + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::Default, })) } else { Read(ReadKind::Borrow(BorrowKind::Shared)) From 14c8827e648ab749ec275a1bb8615b1e9b41a962 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 27 Nov 2025 21:58:07 +0200 Subject: [PATCH 18/34] Coherence for CoerceShared probably working, actual MIR not working --- compiler/rustc_hir/src/lang_items.rs | 3 +- .../src/coherence/builtin.rs | 75 ++++++++++++------- compiler/rustc_hir_analysis/src/errors.rs | 8 ++ compiler/rustc_hir_typeck/src/coercion.rs | 54 +++++-------- library/core/src/ops/reborrow.rs | 6 +- tests/ui/reborrow/custom_marker.rs | 5 +- tests/ui/reborrow/custom_mut_coerce_shared.rs | 4 +- 7 files changed, 78 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e60e0a418ca07..9aabf5d05a949 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,8 +447,7 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); - CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); - CoerceSharedTarget, sym::coerce_shared_target,coerce_shared_target, Target::AssocTy, GenericRequirement::Exact(0); + CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(1); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 914096f7953a4..65b8604220397 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -13,6 +13,7 @@ use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::adjustment::{CoerceSharedInfo, CoerceUnsizedInfo}; use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::relate::solver_relating::RelateExt; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, }; @@ -430,20 +431,27 @@ pub(crate) fn coerce_shared_info<'tcx>( let trait_name = "CoerceShared"; let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); - let coerce_shared_target = tcx.require_lang_item(LangItem::CoerceSharedTarget, span); let _copy_trait = tcx.require_lang_item(LangItem::Copy, span); let source = tcx.type_of(impl_did).instantiate_identity(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let lifetime_params_count = tcx + .generics_of(impl_did) + .own_params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .count(); + + if lifetime_params_count != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } assert_eq!(trait_ref.def_id, coerce_shared_trait); - let Some((target, _obligations)) = structurally_normalize_ty( - tcx, - &infcx, - impl_did, - span, - Ty::new_projection(tcx, coerce_shared_target, trait_ref.args), - ) else { + let Some((target, _obligations)) = + structurally_normalize_ty(tcx, &infcx, impl_did, span, trait_ref.args.type_at(1)) + else { // return Err(tcx.dcx().emit_err(todo!())); todo!(); }; @@ -465,6 +473,7 @@ pub(crate) fn coerce_shared_info<'tcx>( // Parent { Child, u32, i32 }, Child { u64, u32 } => ParentRef { ChildRef, u32 }, ChildRef { u64 } + let a_lifetimes_count = args_a.iter().filter(|arg| arg.as_region().is_some()).count(); let a_data_fields = def_a .non_enum_variant() .fields @@ -489,6 +498,7 @@ pub(crate) fn coerce_shared_info<'tcx>( Some((i, a, tcx.def_span(f.did))) }) .collect::>(); + let b_lifetimes_count = args_b.iter().filter(|arg| arg.as_region().is_some()).count(); let b_data_fields = def_b .non_enum_variant() .fields @@ -505,7 +515,9 @@ pub(crate) fn coerce_shared_info<'tcx>( }) .collect::>(); - if a_data_fields.len() > 1 + if a_lifetimes_count != 1 + || b_lifetimes_count != 1 + || a_data_fields.len() > 1 || b_data_fields.len() > 1 || a_data_fields.len() != b_data_fields.len() { @@ -544,27 +556,34 @@ pub(crate) fn coerce_shared_info<'tcx>( } }; - if let Some((source, target, trait_def_id, _kind, _source_field_span, _target_field_span)) = - data + // We've proven that we have two types with one lifetime each and 0 or 1 data fields each. + if let Some((source, target, trait_def_id, _kind, source_field_span, _target_field_span)) = data { - // Register an obligation for `A: Trait`. - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, impl_did); - let obligation = Obligation::new( - tcx, - cause, - param_env, - ty::TraitRef::new(tcx, trait_def_id, [source, target]), - ); - ocx.register_obligation(obligation); - let errors = ocx.evaluate_obligations_error_on_ambiguity(); - - if !errors.is_empty() { - return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + // 1 data field each. + if infcx + .eq_structurally_relating_aliases(param_env, source, target, source_field_span) + .is_err() + { + // The two data fields don't agree on a common type; this means + // that they must be `A: CoerceShared`. Register an obligation + // for that. + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, trait_def_id, [source, target]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } + // Finally, resolve all regions. + ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; } - - // Finally, resolve all regions. - ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; } Ok(CoerceSharedInfo {}) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d24b3544885b2..a248133a0dff6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1192,6 +1192,14 @@ pub(crate) struct CoerceMulti { pub fields: MultiSpan, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_unsized_may, code = E0377)] +pub(crate) struct CoerceSharedNotSingleLifetimeParam { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] #[diag(hir_analysis_coerce_unsized_may, code = E0377)] pub(crate) struct CoerceSharedNonStruct { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 63e5b0a12c097..b25730bd07420 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -323,21 +323,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, b) } - ty::Adt(_, _) - if self.tcx.features().reborrow() - && self - .fcx - .infcx - .type_implements_trait( - self.tcx - .lang_items() - .coerce_shared() - .expect("Unexpectedly using core/std without reborrow"), - [a], - self.fcx.param_env, - ) - .must_apply_modulo_regions() => - { + ty::Adt(_, _) if self.tcx.features().reborrow() => { let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); if reborrow_coerce.is_ok() { reborrow_coerce } else { self.unify(a, b) } } @@ -924,30 +910,24 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // CoerceShared cannot be T -> T. return Err(TypeError::Mismatch); } - let (Some(coerce_shared_trait_did), Some(coerce_shared_target_did)) = - (self.tcx.lang_items().coerce_shared(), self.tcx.lang_items().coerce_shared_target()) - else { + let Some(coerce_shared_trait_did) = self.tcx.lang_items().coerce_shared() else { return Err(TypeError::Mismatch); }; - let _coerce_shared_trait_ref = ty::TraitRef::new(self.tcx, coerce_shared_trait_did, [a]); - // let obligation = traits::Obligation::new( - // self.tcx, - // ObligationCause::dummy(), - // self.param_env, - // ty::Binder::dummy(coerce_shared_trait_ref), - // ); - let result = Ty::new_projection(self.tcx, coerce_shared_target_did, [a]); - let InferOk { value: result, obligations } = - self.at(&self.cause, self.param_env).normalize(result); - self.unify_and(result, b, [], Adjust::GenericReborrow(ty::Mutability::Not)).map( - |mut infr| { - // infr.obligations.push(obligation); - for obl in obligations { - infr.obligations.push(obl); - } - infr - }, - ) + let coerce_shared_trait_ref = ty::TraitRef::new(self.tcx, coerce_shared_trait_did, [a, b]); + let obligation = traits::Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(coerce_shared_trait_ref), + ); + let ocx = ObligationCtxt::new(&self.infcx); + ocx.register_obligation(obligation); + let errs = ocx.evaluate_obligations_error_on_ambiguity(); + if errs.is_empty() { + Ok(InferOk { value: (vec![], b), obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } } fn coerce_from_fn_pointer( diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs index 3ffbc9f7b7a16..09c5c574f2d75 100644 --- a/library/core/src/ops/reborrow.rs +++ b/library/core/src/ops/reborrow.rs @@ -10,8 +10,6 @@ pub trait Reborrow { /// that disables the source for writes for the lifetime of the copy. #[lang = "coerce_shared"] #[unstable(feature = "reborrow", issue = "145612")] -pub trait CoerceShared: Reborrow { - #[lang = "coerce_shared_target"] - /// The type of this value when reborrowed as shared. - type Target: Copy; +pub trait CoerceShared: Reborrow { + // Empty } diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs index 0423d99c89159..be29fdc319a4c 100644 --- a/tests/ui/reborrow/custom_marker.rs +++ b/tests/ui/reborrow/custom_marker.rs @@ -7,9 +7,8 @@ struct CustomMarker<'a>(PhantomData<&'a ()>); #[derive(Debug, Clone, Copy)] struct CustomMarkerRef<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} -impl<'a> CoerceShared for CustomMarker<'a> { - type Target = CustomMarkerRef<'a>; -} + +impl<'a> CoerceShared> for CustomMarker<'a> {} impl<'a> From> for CustomMarkerRef<'a> { fn from(value: CustomMarker<'a>) -> Self { diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index e2d25835c093a..c2623cb819601 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -3,9 +3,7 @@ use std::ops::{CoerceShared, Reborrow}; struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -impl<'a, T> CoerceShared for CustomMut<'a, T> { - type Target = CustomRef<'a, T>; -} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} struct CustomRef<'a, T>(&'a T); From d52465d6a4b2ff095befa081de3008b5227a68d0 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 5 Dec 2025 22:28:30 +0200 Subject: [PATCH 19/34] not quite working anymore --- compiler/rustc_borrowck/src/borrow_set.rs | 71 +++++++++++++++++------ compiler/rustc_hir_typeck/src/coercion.rs | 11 +++- tests/ui/reborrow/custom_marker.rs | 12 +--- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 44973a28d091d..3742a1ac95d32 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -304,39 +304,72 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { self.local_map.entry(borrowed_place.local).or_default().insert(idx); } else if let &mir::Rvalue::Reborrow(mutability, borrowed_place) = rvalue { let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; - let &ty::Adt(reborrowed_adt, _args) = borrowed_place_ty.kind() else { unreachable!() }; - let &ty::Adt(target_adt, args) = assigned_place.ty(self.body, self.tcx).ty.kind() + let &ty::Adt(reborrowed_adt, reborrowed_args) = borrowed_place_ty.kind() else { + unreachable!() + }; + let &ty::Adt(target_adt, target_args) = + assigned_place.ty(self.body, self.tcx).ty.kind() else { unreachable!() }; - if target_adt.did() != reborrowed_adt.did() { - bug!("hir-typeck passed but reborrow involves mismatching types at {location:?}") - } - for arg in args.iter() { - let ty::GenericArgKind::Lifetime(region) = arg.kind() else { - continue; + let borrow = if mutability == Mutability::Mut { + // Reborrow + if target_adt.did() != reborrowed_adt.did() { + bug!( + "hir-typeck passed but Reborrow involves mismatching types at {location:?}" + ) + } + let Some(ty::GenericArgKind::Lifetime(region)) = + reborrowed_args.get(0).map(|r| r.kind()) + else { + bug!( + "hir-typeck passed but {} does not have a lifetime argument", + if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" } + ); }; let region = region.as_var(); - let kind = if mutability == Mutability::Mut { - mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } - } else { - mir::BorrowKind::Shared - }; - let borrow = BorrowData { + let kind = mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }; + BorrowData { kind, region, reserve_location: location, activation_location: TwoPhaseActivation::NotTwoPhase, borrowed_place, assigned_place: *assigned_place, + } + } else { + // CoerceShared + if target_adt.did() == reborrowed_adt.did() { + bug!( + "hir-typeck passed but CoerceShared involves matching types at {location:?}" + ) + } + let Some(ty::GenericArgKind::Lifetime(region)) = + target_args.get(0).map(|r| r.kind()) + else { + bug!( + "hir-typeck passed but {} does not have a lifetime argument", + if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" } + ); }; - let (idx, _) = self.location_map.insert_full(location, borrow); - let idx = BorrowIndex::from(idx); + let region = region.as_var(); + let kind = mir::BorrowKind::Shared; + BorrowData { + kind, + region, + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place, + assigned_place: *assigned_place, + } + }; + let kind = borrow.kind; + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); - self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); + self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); - self.local_map.entry(borrowed_place.local).or_default().insert(idx); - } + self.local_map.entry(borrowed_place.local).or_default().insert(idx); } self.super_assign(assigned_place, rvalue, location) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index b25730bd07420..564ba2afb85b1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -924,7 +924,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ocx.register_obligation(obligation); let errs = ocx.evaluate_obligations_error_on_ambiguity(); if errs.is_empty() { - Ok(InferOk { value: (vec![], b), obligations: ocx.into_pending_obligations() }) + Ok(InferOk { + value: ( + vec![Adjustment { + kind: Adjust::GenericReborrow(ty::Mutability::Not), + target: b, + }], + b, + ), + obligations: ocx.into_pending_obligations(), + }) } else { Err(TypeError::Mismatch) } diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs index be29fdc319a4c..a919dd1f870b2 100644 --- a/tests/ui/reborrow/custom_marker.rs +++ b/tests/ui/reborrow/custom_marker.rs @@ -7,14 +7,8 @@ struct CustomMarker<'a>(PhantomData<&'a ()>); #[derive(Debug, Clone, Copy)] struct CustomMarkerRef<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} - impl<'a> CoerceShared> for CustomMarker<'a> {} -impl<'a> From> for CustomMarkerRef<'a> { - fn from(value: CustomMarker<'a>) -> Self { - Self(PhantomData) - } -} fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() @@ -25,11 +19,11 @@ fn method_two<'a>(_a: CustomMarkerRef<'a>) -> &'a () { } fn main() { - let mut a = CustomMarker(PhantomData); - let b = method(a); + let a = CustomMarker(PhantomData); + // let b = method(a); // let c = method(a); // should invalidate b let c = method_two(a); // should invalidate b - println!("{c:?} {b:?} {a:?}"); + println!("{c:?} {a:?}"); } // fn main_using_normal_references() { From 27dacbc6e0ef7d3232c331c40001d4c4e8d2fa81 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 17 Dec 2025 19:49:39 +0200 Subject: [PATCH 20/34] fix CoerceShared borrow_set --- compiler/rustc_borrowck/src/borrow_set.rs | 6 ++---- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 3742a1ac95d32..906bf879d6309 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -307,9 +307,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { let &ty::Adt(reborrowed_adt, reborrowed_args) = borrowed_place_ty.kind() else { unreachable!() }; - let &ty::Adt(target_adt, target_args) = - assigned_place.ty(self.body, self.tcx).ty.kind() - else { + let &ty::Adt(target_adt, _) = assigned_place.ty(self.body, self.tcx).ty.kind() else { unreachable!() }; let borrow = if mutability == Mutability::Mut { @@ -345,7 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { ) } let Some(ty::GenericArgKind::Lifetime(region)) = - target_args.get(0).map(|r| r.kind()) + reborrowed_args.get(0).map(|r| r.kind()) else { bug!( "hir-typeck passed but {} does not have a lifetime argument", diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 60a7edb004d9e..5f5a625053bc3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2506,10 +2506,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // field by field with CoerceShared drilling down and down and down. // We cannot just attempt to relate T and ::Target // by calling relate_types. - let dest_adt = dest_ty.ty_adt_def().unwrap(); - let ty::Adt(_, dest_args) = dest_ty.kind() else { unreachable!() }; - let ty::Adt(_, borrowed_args) = borrowed_ty.kind() else { unreachable!() }; - let borrowed_adt = borrowed_ty.ty_adt_def().unwrap(); + let ty::Adt(dest_adt, dest_args) = dest_ty.kind() else { unreachable!() }; + 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() { let Some(borrowed_field) = From f7c4d078e3632da51d69c2f795ff808743a5f091 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 18 Dec 2025 21:30:42 +0200 Subject: [PATCH 21/34] make CoerceShared work --- compiler/rustc_borrowck/src/type_check/mod.rs | 7 +++++-- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5f5a625053bc3..5a846fbd05426 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -645,8 +645,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { debug!(?rv_ty); let rv_ty = self.normalize(rv_ty, location); debug!("normalized rv_ty: {:?}", rv_ty); - if let Err(terr) = - self.sub_types(rv_ty, place_ty, location.to_locations(), category) + // Note: we've checked Reborrow/CoerceShared type matches + // separately in fn visit_assign. + if !matches!(rv, Rvalue::Reborrow(_, _)) + && let Err(terr) = + self.sub_types(rv_ty, place_ty, location.to_locations(), category) { span_mirbug!( self, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 2147207b69b1f..d6afc6146f0b9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -710,6 +710,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: operand.val, layout, move_annotation: None } } mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), + // Note: Exclusive reborrowing is always equal to a memcpy, as the + // types do not change. Generic shared reborrowing is not + // (necessarily) a simple memcpy, but currently the coherence check + // places such restrictions on the CoerceShared trait as to + // guarantee that it is. mir::Rvalue::Reborrow(_, place) => self.codegen_operand(bx, &mir::Operand::Copy(place)), } } From c2411da46fc2c8c1234ccc4fc7af6f225b985f6c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 15 Jan 2026 19:19:42 +0200 Subject: [PATCH 22/34] Make Reborrow work --- compiler/rustc_borrowck/src/lib.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +- .../src/coherence/builtin.rs | 179 +++++++++++++++++- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../src/builder/expr/category.rs | 3 +- .../src/dataflow_const_prop.rs | 1 + 7 files changed, 183 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d49a6295a6eed..274bc79eafd4d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1610,7 +1610,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { location, (place, span), access_kind, - LocalMutationIsAllowed::No, + LocalMutationIsAllowed::Yes, state, ); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5a846fbd05426..6440870bcb9a2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1691,7 +1691,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Discriminant(..) - | Rvalue::Reborrow(_) => {} + | Rvalue::Reborrow(..) => {} } } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d6afc6146f0b9..e9d9d56c4b44d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -709,13 +709,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.cx().layout_of(binder_ty); OperandRef { val: operand.val, layout, move_annotation: None } } - mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), // Note: Exclusive reborrowing is always equal to a memcpy, as the // types do not change. Generic shared reborrowing is not // (necessarily) a simple memcpy, but currently the coherence check // places such restrictions on the CoerceShared trait as to // guarantee that it is. mir::Rvalue::Reborrow(_, place) => self.codegen_operand(bx, &mir::Operand::Copy(place)), + mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), + mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"), } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 65b8604220397..77915cffce8c5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -43,6 +43,7 @@ pub(super) fn check_trait<'tcx>( visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; + checker.check(lang_items.reborrow(), visit_implementation_of_reborrow)?; checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?; checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; @@ -194,6 +195,17 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E tcx.ensure_ok().coerce_unsized_info(impl_did) } +fn visit_implementation_of_reborrow(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_reborrow: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + reborrow_info(tcx, impl_did) +} + fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let impl_did = checker.impl_def_id; @@ -421,6 +433,134 @@ fn structurally_normalize_ty<'tcx>( Some((normalized_ty, ocx.into_pending_obligations())) } +pub(crate) fn reborrow_info<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + debug!("compute_reborrow_info(impl_did={:?})", impl_did); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let span = tcx.def_span(impl_did); + let trait_name = "Reborrow"; + + let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span); + + let source = tcx.type_of(impl_did).instantiate_identity(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity(); + let lifetime_params_count = tcx + .generics_of(impl_did) + .own_params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .count(); + + if lifetime_params_count != 1 { + return Err(tcx + .dcx() + .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name })); + } + + assert_eq!(trait_ref.def_id, reborrow_trait); + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let _cause = ObligationCause::misc(span, impl_did); + let (def, args) = match source.kind() { + &ty::Adt(def, args) if def.is_struct() => + { + (def, args) + } + _ => { + return Err(tcx.dcx().emit_err(errors::CoerceSharedNonStruct { span, trait_name })); + } + }; + + let lifetimes_count = args.iter().filter(|arg| arg.as_region().is_some()).count(); + let data_fields = def + .non_enum_variant() + .fields + .iter() + .filter_map(|f| { + // Ignore PhantomData fields + let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); + if tcx + .try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, def.did()), + unnormalized_ty, + ) + .unwrap_or(unnormalized_ty) + .is_phantom_data() + { + return None; + } + + let ty = f.ty(tcx, args); + + // Collect up all fields that were significantly changed. + Some((ty, tcx.def_span(f.did))) + }) + .collect::>(); + + if lifetimes_count != 1 { + let item = tcx.hir_expect_item(impl_did); + let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = + &item.kind + { + of_trait.trait_ref.path.span + } else { + tcx.def_span(impl_did) + }; + + todo!(); + // let mut lifetimes = args.iter().filter_map(|arg| arg.as_region()).collect::>(); + // return Err(tcx.dcx().emit_err(errors::CoerceMulti { + // span, + // trait_name, + // number: lifetimes_count, + // fields: todo!(), + // })); + } + + if data_fields.is_empty() { + return Ok(()); + } + + // We've found some data fields. They must all be either be Copy or Reborrow. + for (field, span) in data_fields { + assert_field_type_is_reborrow(tcx, &infcx, reborrow_trait, impl_did, param_env, field, span).or_else(|err| { + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; + }) + } + + Ok(()) +} + +fn assert_field_type_is_reborrow<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + reborrow_trait: DefId, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, reborrow_trait, [ty]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + Err(errors) + } else { + Ok(()) + } +} + pub(crate) fn coerce_shared_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, @@ -431,10 +571,9 @@ pub(crate) fn coerce_shared_info<'tcx>( let trait_name = "CoerceShared"; let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); - let _copy_trait = tcx.require_lang_item(LangItem::Copy, span); let source = tcx.type_of(impl_did).instantiate_identity(); - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity(); let lifetime_params_count = tcx .generics_of(impl_did) .own_params @@ -559,7 +698,11 @@ pub(crate) fn coerce_shared_info<'tcx>( // We've proven that we have two types with one lifetime each and 0 or 1 data fields each. if let Some((source, target, trait_def_id, _kind, source_field_span, _target_field_span)) = data { - // 1 data field each. + // struct Source(SourceData); + // struct Target(TargetData); + // + // 1 data field each; they must be the same type and Copy, or relate to one another using + // CoerceShared. if infcx .eq_structurally_relating_aliases(param_env, source, target, source_field_span) .is_err() @@ -583,12 +726,42 @@ pub(crate) fn coerce_shared_info<'tcx>( } // Finally, resolve all regions. ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; + } else { + // Types match: check that it is Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?; } } Ok(CoerceSharedInfo {}) } +fn assert_field_type_is_copy<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let copy_trait = tcx.require_lang_item(LangItem::Copy, span); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, copy_trait, [ty]), + ); + ocx.register_obligation(obligation); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + Err(infcx.err_ctxt().report_fulfillment_errors(errors)) + } else { + Ok(()) + } +} + pub(crate) fn coerce_unsized_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 564ba2afb85b1..a7fc50f357fa5 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -325,7 +325,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } ty::Adt(_, _) if self.tcx.features().reborrow() => { let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); - if reborrow_coerce.is_ok() { reborrow_coerce } else { self.unify(a, b) } + if reborrow_coerce.is_ok() { reborrow_coerce } else { self.unify(a, b, ForceLeakCheck::No) } } _ => { // Otherwise, just use unification rules. diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index b706f38aae27a..a5d826f7817fb 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -72,8 +72,7 @@ impl Category { | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::WrapUnsafeBinder { .. } - | ExprKind::Reborrow { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 4eeebff2a4c64..e969936e99bbc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -467,6 +467,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), Rvalue::Use(operand) => return self.handle_operand(operand, state), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), + Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"), Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Reborrow(..) => { // We don't track such places. return ValueOrPlace::TOP; From 77a6156e63aa5d66b37f402bb6a0a072ba20517c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 15 Jan 2026 22:14:33 +0200 Subject: [PATCH 23/34] errors handled --- compiler/rustc_hir_analysis/messages.ftl | 4 + .../src/coherence/builtin.rs | 134 ++++++------------ compiler/rustc_hir_analysis/src/errors.rs | 6 +- compiler/rustc_hir_typeck/src/coercion.rs | 6 +- 4 files changed, 59 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d9f8eba65c4a2..4e4e7f38915bd 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -105,6 +105,10 @@ hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only ap hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other +hir_analysis_coerce_shared_multi = implementing `{$trait_name}` does not allow multiple lifetimes or fields to be coerced + +hir_analysis_coerce_shared_not_zero = implementing `{$trait_name}` requires that a single lifetime parameter is passed between source and target + hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}` .label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 77915cffce8c5..34cf0130962ee 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -23,7 +23,7 @@ use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, }; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCause, ObligationCtxt}; use tracing::debug; use crate::errors; @@ -463,14 +463,11 @@ pub(crate) fn reborrow_info<'tcx>( let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - let _cause = ObligationCause::misc(span, impl_did); let (def, args) = match source.kind() { - &ty::Adt(def, args) if def.is_struct() => - { - (def, args) - } + &ty::Adt(def, args) if def.is_struct() => (def, args), _ => { - return Err(tcx.dcx().emit_err(errors::CoerceSharedNonStruct { span, trait_name })); + // Note: reusing error here as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); } }; @@ -481,43 +478,23 @@ pub(crate) fn reborrow_info<'tcx>( .iter() .filter_map(|f| { // Ignore PhantomData fields - let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); - if tcx - .try_normalize_erasing_regions( - ty::TypingEnv::non_body_analysis(tcx, def.did()), - unnormalized_ty, - ) - .unwrap_or(unnormalized_ty) - .is_phantom_data() - { + let ty = f.ty(tcx, args); + if ty.is_phantom_data() { return None; } - - let ty = f.ty(tcx, args); - - // Collect up all fields that were significantly changed. Some((ty, tcx.def_span(f.did))) }) .collect::>(); if lifetimes_count != 1 { let item = tcx.hir_expect_item(impl_did); - let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = - &item.kind - { + let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind { of_trait.trait_ref.path.span } else { tcx.def_span(impl_did) }; - todo!(); - // let mut lifetimes = args.iter().filter_map(|arg| arg.as_region()).collect::>(); - // return Err(tcx.dcx().emit_err(errors::CoerceMulti { - // span, - // trait_name, - // number: lifetimes_count, - // fields: todo!(), - // })); + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); } if data_fields.is_empty() { @@ -526,9 +503,23 @@ pub(crate) fn reborrow_info<'tcx>( // We've found some data fields. They must all be either be Copy or Reborrow. for (field, span) in data_fields { - assert_field_type_is_reborrow(tcx, &infcx, reborrow_trait, impl_did, param_env, field, span).or_else(|err| { - assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; - }) + if assert_field_type_is_reborrow( + tcx, + &infcx, + reborrow_trait, + impl_did, + param_env, + field, + span, + ) + .is_ok() + { + // Field implements Reborrow. + return Ok(()); + } + + // Field does not implement Reborrow: it must be Copy. + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; } Ok(()) @@ -542,23 +533,15 @@ fn assert_field_type_is_reborrow<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span, -) -> Result<(), ErrorGuaranteed> { +) -> Result<(), Vec>> { let ocx = ObligationCtxt::new_with_diagnostics(infcx); let cause = traits::ObligationCause::misc(span, impl_did); - let obligation = Obligation::new( - tcx, - cause, - param_env, - ty::TraitRef::new(tcx, reborrow_trait, [ty]), - ); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, reborrow_trait, [ty])); ocx.register_obligation(obligation); let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if !errors.is_empty() { - Err(errors) - } else { - Ok(()) - } + if !errors.is_empty() { Err(errors) } else { Ok(()) } } pub(crate) fn coerce_shared_info<'tcx>( @@ -591,49 +574,33 @@ pub(crate) fn coerce_shared_info<'tcx>( let Some((target, _obligations)) = structurally_normalize_ty(tcx, &infcx, impl_did, span, trait_ref.args.type_at(1)) else { - // return Err(tcx.dcx().emit_err(todo!())); - todo!(); + todo!("something went wrong with structurally_normalize_ty"); }; - debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (bound)", source, target); let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - debug!("visit_implementation_of_coerce_shared: {:?} -> {:?} (free)", source, target); - - let _cause = ObligationCause::misc(span, impl_did); let data = match (source.kind(), target.kind()) { (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { - // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()> } -> struct FooRef<'a> { a: u32, b: PhantomData<&'a ()> } - // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()>, c: u32 } -> struct FooRef<'a> { a: u32, b: PhantomData<&'a ()> } - // struct FooMut<'a> { a: u32, b: PhantomData<&'a ()>, c: u32 } -> struct FooRef<'a> { c: u32, a: u32, b: PhantomData<&'a ()> } - - // Parent { Child, u32, i32 }, Child { u64, u32 } => ParentRef { ChildRef, u32 }, ChildRef { u64 } - + // Check that both A and B have exactly one lifetime argument, and that they have the + // same number of data fields that is not more than 1. The eventual intention is to + // support multiple lifetime arguments (with the reborrowed lifetimes inferred from + // usage one way or another) and multiple data fields with B allowed to leave out fields + // from A. The current state is just the simplest choice. let a_lifetimes_count = args_a.iter().filter(|arg| arg.as_region().is_some()).count(); let a_data_fields = def_a .non_enum_variant() .fields .iter_enumerated() .filter_map(|(i, f)| { - // Ignore PhantomData fields - let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); - if tcx - .try_normalize_erasing_regions( - ty::TypingEnv::non_body_analysis(tcx, def_a.did()), - unnormalized_ty, - ) - .unwrap_or(unnormalized_ty) - .is_phantom_data() - { + let a = f.ty(tcx, args_b); + + if a.is_phantom_data() { return None; } - let a = f.ty(tcx, args_a); - - // Collect up all fields that were significantly changed. Some((i, a, tcx.def_span(f.did))) }) .collect::>(); @@ -649,7 +616,6 @@ pub(crate) fn coerce_shared_info<'tcx>( return None; } - // Collect up all fields that were significantly changed. Some((i, b, tcx.def_span(f.did))) }) .collect::>(); @@ -669,29 +635,27 @@ pub(crate) fn coerce_shared_info<'tcx>( tcx.def_span(impl_did) }; - let mut fields = a_data_fields.iter().map(|(_, _, s)| *s).collect::>(); - fields.extend(b_data_fields.iter().map(|(_, _, s)| *s)); - return Err(tcx.dcx().emit_err(errors::CoerceMulti { - span, - trait_name, - number: a_data_fields.len() + b_data_fields.len(), - fields: fields.into(), - })); + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); } let kind = ty::adjustment::CoerceSharedInfo {}; if a_data_fields.len() == 1 { + // We found one data field for both: we'll attempt to perform CoerceShared between + // them below. let (_a_i, a, span_a) = a_data_fields[0]; let (_b_i, b, span_b) = b_data_fields[0]; Some((a, b, coerce_shared_trait, kind, span_a, span_b)) } else { + // We found no data fields in either: this is a reborrowable marker type being + // coerced into a shared marker. That is fine too. None } } _ => { - return Err(tcx.dcx().emit_err(errors::CoerceSharedNonStruct { span, trait_name })); + // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); } }; @@ -746,12 +710,8 @@ fn assert_field_type_is_copy<'tcx>( let copy_trait = tcx.require_lang_item(LangItem::Copy, span); let ocx = ObligationCtxt::new_with_diagnostics(infcx); let cause = traits::ObligationCause::misc(span, impl_did); - let obligation = Obligation::new( - tcx, - cause, - param_env, - ty::TraitRef::new(tcx, copy_trait, [ty]), - ); + let obligation = + Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, copy_trait, [ty])); ocx.register_obligation(obligation); let errors = ocx.evaluate_obligations_error_on_ambiguity(); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a248133a0dff6..aa7784d673427 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1193,7 +1193,7 @@ pub(crate) struct CoerceMulti { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_may, code = E0377)] +#[diag(hir_analysis_coerce_shared_not_zero)] pub(crate) struct CoerceSharedNotSingleLifetimeParam { #[primary_span] pub span: Span, @@ -1201,8 +1201,8 @@ pub(crate) struct CoerceSharedNotSingleLifetimeParam { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_may, code = E0377)] -pub(crate) struct CoerceSharedNonStruct { +#[diag(hir_analysis_coerce_shared_multi)] +pub(crate) struct CoerceSharedMulti { #[primary_span] pub span: Span, pub trait_name: &'static str, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a7fc50f357fa5..d475f6b2fb952 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -325,7 +325,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } ty::Adt(_, _) if self.tcx.features().reborrow() => { let reborrow_coerce = self.commit_if_ok(|_| self.coerce_shared_reborrow(a, b)); - if reborrow_coerce.is_ok() { reborrow_coerce } else { self.unify(a, b, ForceLeakCheck::No) } + if reborrow_coerce.is_ok() { + reborrow_coerce + } else { + self.unify(a, b, ForceLeakCheck::No) + } } _ => { // Otherwise, just use unification rules. From 1f968e7503008d3d172ce54552e3ac4b50f30626 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 27 Jan 2026 18:42:53 +0200 Subject: [PATCH 24/34] Fix lifetime of reborrow by Ding Xiang Fei --- compiler/rustc_borrowck/src/borrow_set.rs | 10 ++++--- compiler/rustc_borrowck/src/type_check/mod.rs | 26 +++++++++++++------ .../src/coherence/builtin.rs | 18 +++++++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 906bf879d6309..af373479f7b6c 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -304,10 +304,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { self.local_map.entry(borrowed_place.local).or_default().insert(idx); } else if let &mir::Rvalue::Reborrow(mutability, borrowed_place) = rvalue { let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; - let &ty::Adt(reborrowed_adt, reborrowed_args) = borrowed_place_ty.kind() else { + let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else { unreachable!() }; - let &ty::Adt(target_adt, _) = assigned_place.ty(self.body, self.tcx).ty.kind() else { + let &ty::Adt(target_adt, assigned_args) = + assigned_place.ty(self.body, self.tcx).ty.kind() + else { unreachable!() }; let borrow = if mutability == Mutability::Mut { @@ -318,7 +320,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { ) } let Some(ty::GenericArgKind::Lifetime(region)) = - reborrowed_args.get(0).map(|r| r.kind()) + assigned_args.get(0).map(|r| r.kind()) else { bug!( "hir-typeck passed but {} does not have a lifetime argument", @@ -343,7 +345,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { ) } let Some(ty::GenericArgKind::Lifetime(region)) = - reborrowed_args.get(0).map(|r| r.kind()) + assigned_args.get(0).map(|r| r.kind()) else { bug!( "hir-typeck passed but {} does not have a lifetime argument", diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6440870bcb9a2..dd56cf2a8188d 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2503,6 +2503,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let dest_ty = dest.ty(self.body, tcx).ty; let borrowed_ty = borrowed_place.ty(self.body, tcx).ty; + let ty::Adt(_, args) = dest_ty.kind() else { bug!() }; + let [arg, ..] = ***args else { bug!() }; + let ty::GenericArgKind::Lifetime(reborrow_region) = arg.kind() else { bug!() }; + self.constraints.liveness_constraints.add_location(reborrow_region.as_var(), location); if mutability.is_not() { // FIXME: for shared reborrow we need to relate the types manually, @@ -2520,14 +2524,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; let dest_ty = dest_field.ty(tcx, dest_args); let borrowed_ty = borrowed_field.ty(tcx, borrowed_args); - self.relate_types( - borrowed_ty, - ty::Variance::Covariant, - dest_ty, - location.to_locations(), - category, - ) - .unwrap(); + if borrowed_ty.ref_mutability() == Some(Mutability::Mut) + && dest_ty.ref_mutability() == Some(Mutability::Not) + { + // self.add_reborrow_constraint(location, borrow_region, borrowed_place); + } else { + self.relate_types( + borrowed_ty, + ty::Variance::Covariant, + dest_ty, + location.to_locations(), + category, + ) + .unwrap(); + } } } else { // Exclusive reborrow diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 34cf0130962ee..13698a2682597 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -534,6 +534,10 @@ fn assert_field_type_is_reborrow<'tcx>( ty: Ty<'tcx>, span: Span, ) -> Result<(), Vec>> { + if ty.ref_mutability() == Some(ty::Mutability::Mut) { + // Mutable references are Reborrow but not really. + return Ok(()); + } let ocx = ObligationCtxt::new_with_diagnostics(infcx); let cause = traits::ObligationCause::misc(span, impl_did); let obligation = @@ -667,6 +671,20 @@ pub(crate) fn coerce_shared_info<'tcx>( // // 1 data field each; they must be the same type and Copy, or relate to one another using // CoerceShared. + if source.ref_mutability() == Some(ty::Mutability::Mut) + && target.ref_mutability() == Some(ty::Mutability::Not) + && infcx + .eq_structurally_relating_aliases( + param_env, + source.peel_refs(), + target.peel_refs(), + source_field_span, + ) + .is_ok() + { + // &mut T implements CoerceShared to &T, except not really. + return Ok(CoerceSharedInfo {}); + } if infcx .eq_structurally_relating_aliases(param_env, source, target, source_field_span) .is_err() From 3b1957adfe5aabfffb120ddb9991c65345e3c36c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 27 Jan 2026 18:43:24 +0200 Subject: [PATCH 25/34] Start adding more comprehensive tests --- tests/ui/reborrow/custom_marker.rs | 23 +++---------------- .../reborrow/custom_marker_coerce_shared.rs | 22 ++++++++++++++++++ .../custom_marker_coerce_shared_copy.rs | 22 ++++++++++++++++++ .../custom_marker_coerce_shared_move.rs | 22 ++++++++++++++++++ tests/ui/reborrow/custom_marker_mut_a_b.rs | 18 +++++++++++++++ tests/ui/reborrow/custom_marker_mut_self.rs | 17 ++++++++++++++ tests/ui/reborrow/custom_marker_mut_self_a.rs | 18 +++++++++++++++ tests/ui/reborrow/custom_marker_mut_self_b.rs | 18 +++++++++++++++ tests/ui/reborrow/custom_mut_coerce_shared.rs | 6 +---- 9 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_copy.rs create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_move.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_a_b.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self_a.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_self_b.rs diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs index a919dd1f870b2..e70a8bea4abd5 100644 --- a/tests/ui/reborrow/custom_marker.rs +++ b/tests/ui/reborrow/custom_marker.rs @@ -1,34 +1,17 @@ #![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; +use std::ops::{Reborrow}; use std::marker::PhantomData; #[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); -#[derive(Debug, Clone, Copy)] -struct CustomMarkerRef<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} -impl<'a> CoerceShared> for CustomMarker<'a> {} - fn method<'a>(_a: CustomMarker<'a>) -> &'a () { &() } -fn method_two<'a>(_a: CustomMarkerRef<'a>) -> &'a () { - &() -} - fn main() { let a = CustomMarker(PhantomData); - // let b = method(a); - // let c = method(a); // should invalidate b - let c = method_two(a); // should invalidate b - println!("{c:?} {a:?}"); + let _ = method(a); + let _ = method(a); } - -// fn main_using_normal_references() { -// let a = &mut (); -// let b = method(a); -// let _ = method(a); -// eprintln!("{b}"); -// } diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs new file mode 100644 index 0000000000000..cc4013376bdae --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -0,0 +1,22 @@ +#![feature(reborrow)] +use std::ops::{CoerceShared, Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs new file mode 100644 index 0000000000000..c05e6309f19d9 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -0,0 +1,22 @@ +#![feature(reborrow)] +use std::ops::{CoerceShared, Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (&a, b, c); +} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs new file mode 100644 index 0000000000000..0fbb04342283e --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -0,0 +1,22 @@ +#![feature(reborrow)] +use std::ops::{CoerceShared, Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(Debug, Clone, Copy)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); +impl<'a> CoerceShared> for CustomMarker<'a> {} + + +fn method<'a>(_a: CustomMarkerRef<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (a, b, c); +} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs new file mode 100644 index 0000000000000..4ae9ee9bc1643 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::ops::{Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let c = method(a); + let _ = (b, c); +} diff --git a/tests/ui/reborrow/custom_marker_mut_self.rs b/tests/ui/reborrow/custom_marker_mut_self.rs new file mode 100644 index 0000000000000..a4b1d269f6b8f --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::ops::{Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = (a, b); //~ERROR cannot move out of `a` because it is borrowed +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.rs b/tests/ui/reborrow/custom_marker_mut_self_a.rs new file mode 100644 index 0000000000000..846ebf5669cdd --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::ops::{Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let b = method(a); + let _ = method(a); + let _ = (a, b); +} diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.rs b/tests/ui/reborrow/custom_marker_mut_self_b.rs new file mode 100644 index 0000000000000..3360f941b7357 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::ops::{Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); + let b = method(a); + let _ = (a, b); +} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index c2623cb819601..95342d098510c 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -14,13 +14,9 @@ impl<'a, T> Clone for CustomRef<'a, T> { } impl<'a, T> Copy for CustomRef<'a, T> {} -fn method(a: CustomRef<'_, ()>) {} //~NOTE function defined here +fn method(a: CustomRef<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); method(a); - //~^ ERROR mismatched types - //~| NOTE expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - //~| NOTE arguments to this function are incorrect - //~| NOTE expected struct `CustomRef<'_, ()>` } From 97994e9bf1eba8669f18da49d52ca7747247ddad Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 27 Jan 2026 19:22:13 +0200 Subject: [PATCH 26/34] fix after rebase --- compiler/rustc_borrowck/src/borrow_set.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index af373479f7b6c..4a9e2f4a9774c 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -363,12 +363,9 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { assigned_place: *assigned_place, } }; - let kind = borrow.kind; let (idx, _) = self.location_map.insert_full(location, borrow); let idx = BorrowIndex::from(idx); - self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); - self.local_map.entry(borrowed_place.local).or_default().insert(idx); } From 1e7b514a4d9512b9d02492d68a261b7145fb1761 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 29 Jan 2026 20:58:26 +0200 Subject: [PATCH 27/34] fix &mut -> & CoerceShared --- compiler/rustc_borrowck/src/type_check/mod.rs | 24 ++++++++++++-- compiler/rustc_middle/src/mir/statement.rs | 6 ++++ .../src/add_subtyping_projections.rs | 3 ++ compiler/rustc_mir_transform/src/validate.rs | 32 +++++++++++-------- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index dd56cf2a8188d..79ef007e8202b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2524,10 +2524,28 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; let dest_ty = dest_field.ty(tcx, dest_args); let borrowed_ty = borrowed_field.ty(tcx, borrowed_args); - if borrowed_ty.ref_mutability() == Some(Mutability::Mut) - && dest_ty.ref_mutability() == Some(Mutability::Not) + if let ( + ty::Ref(borrow_region, _, Mutability::Mut), + ty::Ref(ref_region, _, Mutability::Not), + ) = (borrowed_ty.kind(), dest_ty.kind()) { - // self.add_reborrow_constraint(location, borrow_region, borrowed_place); + self.relate_types( + borrowed_ty.peel_refs(), + ty::Variance::Covariant, + dest_ty.peel_refs(), + location.to_locations(), + category, + ) + .unwrap(); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.as_var(), + sub: borrow_region.as_var(), + locations: location.to_locations(), + span: location.to_locations().span(self.body), + category, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }); } else { self.relate_types( borrowed_ty, diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 5529b0a5cf362..5ea123a87acbd 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -774,6 +774,12 @@ impl<'tcx> Rvalue<'tcx> { } } + /// Returns true if rvalue is a generic Reborrow coercion (usage of Reborrow or CoerceShared + /// trait). + pub fn is_generic_reborrow(&self) -> bool { + matches!(self, Self::Reborrow(..)) + } + pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx> where D: ?Sized + HasLocalDecls<'tcx>, diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index a6a60fddf9097..fc31d502087fd 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -23,6 +23,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { rvalue: &mut Rvalue<'tcx>, location: Location, ) { + if rvalue.is_generic_reborrow() { + return; + } // We don't need to do anything for deref temps as they are // not part of the source code, but used for desugaring purposes. if self.local_decls[place.local].is_deref_temp() { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index e744eab60e81a..1bd6ef1b0c8df 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -122,18 +122,19 @@ struct CfgChecker<'a, 'tcx> { impl<'a, 'tcx> CfgChecker<'a, 'tcx> { #[track_caller] - fn fail(&self, location: Location, msg: impl AsRef) { + fn fail(&self, _location: Location, _msg: impl AsRef) { + panic!("FAIL in CfgChecker"); // We might see broken MIR when other errors have already occurred. - if self.tcx.dcx().has_errors().is_none() { - span_bug!( - self.body.source_info(location).span, - "broken MIR in {:?} ({}) at {:?}:\n{}", - self.body.source.instance, - self.when, - location, - msg.as_ref(), - ); - } + // if self.tcx.dcx().has_errors().is_none() { + // span_bug!( + // self.body.source_info(location).span, + // "broken MIR in {:?} ({}) at {:?}:\n{}", + // self.body.source.instance, + // self.when, + // location, + // msg.as_ref(), + // ); + // } } fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) { @@ -571,8 +572,9 @@ struct TypeChecker<'a, 'tcx> { } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn fail(&mut self, location: Location, msg: impl Into) { - self.failures.push((location, msg.into())); + fn fail(&mut self, _location: Location, _msg: impl Into) { + panic!("FAIL in validate.rs"); + // self.failures.push((location, msg.into())); } /// Check if src can be assigned into dest. @@ -1469,6 +1471,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty; let right_ty = rvalue.ty(&self.body.local_decls, self.tcx); + if matches!(rvalue, Rvalue::Reborrow(..)) { + // Trust me bro: Reborrow/CoerceShared is only inserted if types match. + return; + } if !self.mir_assign_valid_types(right_ty, left_ty) { self.fail( location, From 37bdf23787c0c2edfbdbe0b3161c993f6f33b4b7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 29 Jan 2026 20:59:34 +0200 Subject: [PATCH 28/34] some tests --- tests/ui/reborrow/custom_marker_deref.rs | 17 +++++++++++++++++ tests/ui/reborrow/custom_mut_coerce_shared.rs | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/ui/reborrow/custom_marker_deref.rs diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs new file mode 100644 index 0000000000000..b9cf6d141f5a0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::ops::{Reborrow}; +use std::marker::PhantomData; + +#[derive(Debug)] +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let mut a = CustomMarker(PhantomData); + let b = &mut a; + let _ = method(*b); +} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index 95342d098510c..9fa77856de02d 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -14,7 +14,7 @@ impl<'a, T> Clone for CustomRef<'a, T> { } impl<'a, T> Copy for CustomRef<'a, T> {} -fn method(a: CustomRef<'_, ()>) {} +fn method(_a: CustomRef<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); From 2f3dde6957bf7f43103679294ff334efa23229c4 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 2 Feb 2026 21:39:07 +0200 Subject: [PATCH 29/34] self-review fixes --- .../rustc_mir_build/src/builder/expr/into.rs | 10 ------- compiler/rustc_mir_build/src/thir/print.rs | 1 - compiler/rustc_mir_transform/src/validate.rs | 26 +++++++++---------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 75d04e57b2c12..0825eb0abaeb3 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -803,16 +803,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } ExprKind::Reborrow { source, mutability, ty: _ } => { - // if !this.tcx.type_is_copy_modulo_regions(this.typing_env(), ty) { - // panic!("Reborrow currently requires self to be Copy"); - // } - // let arg_place = match borrow_kind { - // BorrowKind::Shared => { - // unpack!(block = this.as_read_only_place(block, source)) - // } - // _ => unpack!(block = this.as_place(block, source)), - // }; - // let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); let place = unpack!(block = this.as_place(block, source)); this.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index f5b2089f59d0a..fdef7630df878 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -607,7 +607,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { print_indented!(self, "Reborrow {", depth_lvl); print_indented!(self, "source:", depth_lvl + 1); - // self.print_expr(*value, depth_lvl + 2); print_indented!(self, "mutability:", depth_lvl + 1); print_indented!(self, "ty:", depth_lvl + 1); print_indented!(self, "}", depth_lvl); diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 1bd6ef1b0c8df..cf02c9af45fdc 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -122,19 +122,18 @@ struct CfgChecker<'a, 'tcx> { impl<'a, 'tcx> CfgChecker<'a, 'tcx> { #[track_caller] - fn fail(&self, _location: Location, _msg: impl AsRef) { - panic!("FAIL in CfgChecker"); + fn fail(&self, location: Location, msg: impl AsRef) { // We might see broken MIR when other errors have already occurred. - // if self.tcx.dcx().has_errors().is_none() { - // span_bug!( - // self.body.source_info(location).span, - // "broken MIR in {:?} ({}) at {:?}:\n{}", - // self.body.source.instance, - // self.when, - // location, - // msg.as_ref(), - // ); - // } + if self.tcx.dcx().has_errors().is_none() { + span_bug!( + self.body.source_info(location).span, + "broken MIR in {:?} ({}) at {:?}:\n{}", + self.body.source.instance, + self.when, + location, + msg.as_ref(), + ); + } } fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) { @@ -573,8 +572,7 @@ struct TypeChecker<'a, 'tcx> { impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn fail(&mut self, _location: Location, _msg: impl Into) { - panic!("FAIL in validate.rs"); - // self.failures.push((location, msg.into())); + self.failures.push((location, msg.into())); } /// Check if src can be assigned into dest. From a6ea388f8e85a7b3c73d441d3177b8cae061b122 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 2 Feb 2026 23:12:10 +0200 Subject: [PATCH 30/34] move back to marker --- compiler/rustc_mir_transform/src/validate.rs | 2 +- library/core/src/marker.rs | 16 ++++++++++++++++ library/core/src/ops/mod.rs | 3 --- library/core/src/ops/reborrow.rs | 15 --------------- 4 files changed, 17 insertions(+), 19 deletions(-) delete mode 100644 library/core/src/ops/reborrow.rs diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index cf02c9af45fdc..fce65376b0abd 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -571,7 +571,7 @@ struct TypeChecker<'a, 'tcx> { } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn fail(&mut self, _location: Location, _msg: impl Into) { + fn fail(&mut self, location: Location, msg: impl Into) { self.failures.push((location, msg.into())); } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 57416455e9de8..919a1cfb36828 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1337,3 +1337,19 @@ pub macro CoercePointee($item:item) { pub trait CoercePointeeValidated { /* compiler built-in */ } + +/// Allows value to be reborrowed as exclusive, creating a copy of the value +/// that disables the source for reads and writes for the lifetime of the copy. +#[lang = "reborrow"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait Reborrow { + /* compiler built-in */ +} + +/// Allows reborrowable value to be reborrowed as shared, creating a copy +/// that disables the source for writes for the lifetime of the copy. +#[lang = "coerce_shared"] +#[unstable(feature = "reborrow", issue = "145612")] +pub trait CoerceShared: Reborrow { + /* compiler built-in */ +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index ab1ad407ee282..87dd873fdb57d 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -149,7 +149,6 @@ mod function; mod index; mod index_range; mod range; -mod reborrow; mod try_trait; mod unsize; @@ -190,8 +189,6 @@ pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; -#[unstable(feature = "reborrow", issue = "145612")] -pub use self::reborrow::{CoerceShared, Reborrow}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] diff --git a/library/core/src/ops/reborrow.rs b/library/core/src/ops/reborrow.rs deleted file mode 100644 index 09c5c574f2d75..0000000000000 --- a/library/core/src/ops/reborrow.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// Allows value to be reborrowed as exclusive, creating a copy of the value -/// that disables the source for reads and writes for the lifetime of the copy. -#[lang = "reborrow"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait Reborrow { - // Empty. -} - -/// Allows reborrowable value to be reborrowed as shared, creating a copy -/// that disables the source for writes for the lifetime of the copy. -#[lang = "coerce_shared"] -#[unstable(feature = "reborrow", issue = "145612")] -pub trait CoerceShared: Reborrow { - // Empty -} From 92ff599009d9a62d35bcdc11a68ba29ca11b284b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 2 Feb 2026 23:14:15 +0200 Subject: [PATCH 31/34] fix and add tests --- .../feature-gate-reborrow-coerce-shared.rs | 2 +- ...feature-gate-reborrow-coerce-shared.stderr | 4 +-- .../ui/feature-gates/feature-gate-reborrow.rs | 2 +- .../feature-gate-reborrow.stderr | 4 +-- tests/ui/reborrow/custom_marker.rs | 6 ++-- .../reborrow/custom_marker_coerce_shared.rs | 6 ++-- .../custom_marker_coerce_shared_copy.rs | 6 ++-- .../custom_marker_coerce_shared_move.rs | 7 ++--- .../custom_marker_coerce_shared_move.stderr | 16 ++++++++++ tests/ui/reborrow/custom_marker_deref.rs | 6 ++-- tests/ui/reborrow/custom_marker_mut_a_b.rs | 5 ++-- .../ui/reborrow/custom_marker_mut_a_b.stderr | 14 +++++++++ tests/ui/reborrow/custom_marker_mut_self.rs | 4 +-- .../ui/reborrow/custom_marker_mut_self.stderr | 15 ++++++++++ tests/ui/reborrow/custom_marker_mut_self_a.rs | 6 ++-- .../reborrow/custom_marker_mut_self_a.stderr | 28 ++++++++++++++++++ tests/ui/reborrow/custom_marker_mut_self_b.rs | 5 ++-- .../reborrow/custom_marker_mut_self_b.stderr | 16 ++++++++++ .../reborrow/custom_marker_two_lifetimes.rs | 8 +++++ .../custom_marker_two_lifetimes.stderr | 8 +++++ tests/ui/reborrow/custom_mut.rs | 9 ++++-- tests/ui/reborrow/custom_mut.stderr | 29 ------------------- tests/ui/reborrow/custom_mut_coerce_shared.rs | 5 +++- .../reborrow/custom_mut_coerce_shared.stderr | 19 ------------ 24 files changed, 144 insertions(+), 86 deletions(-) create mode 100644 tests/ui/reborrow/custom_marker_coerce_shared_move.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_a_b.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self_a.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_self_b.stderr create mode 100644 tests/ui/reborrow/custom_marker_two_lifetimes.rs create mode 100644 tests/ui/reborrow/custom_marker_two_lifetimes.stderr delete mode 100644 tests/ui/reborrow/custom_mut.stderr delete mode 100644 tests/ui/reborrow/custom_mut_coerce_shared.stderr diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs index c8ca453708912..48a14959d8d64 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.rs @@ -1,3 +1,3 @@ -use std::ops::CoerceShared; //~ ERROR use of unstable library feature `reborrow` +use std::marker::CoerceShared; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr index dbbbcdf2fd57d..c4c5e06778af3 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow-coerce-shared.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow-coerce-shared.rs:1:5 | -LL | use std::ops::CoerceShared; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::marker::CoerceShared; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-reborrow.rs b/tests/ui/feature-gates/feature-gate-reborrow.rs index 96eecfb28a106..f016f6c6bfa59 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.rs +++ b/tests/ui/feature-gates/feature-gate-reborrow.rs @@ -1,3 +1,3 @@ -use std::ops::Reborrow; //~ ERROR use of unstable library feature `reborrow` +use std::marker::Reborrow; //~ ERROR use of unstable library feature `reborrow` fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-reborrow.stderr b/tests/ui/feature-gates/feature-gate-reborrow.stderr index 1224909f564bc..5e3033f3bf1fe 100644 --- a/tests/ui/feature-gates/feature-gate-reborrow.stderr +++ b/tests/ui/feature-gates/feature-gate-reborrow.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `reborrow` --> $DIR/feature-gate-reborrow.rs:1:5 | -LL | use std::ops::Reborrow; - | ^^^^^^^^^^^^^^^^^^ +LL | use std::marker::Reborrow; + | ^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #145612 for more information = help: add `#![feature(reborrow)]` to the crate attributes to enable diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs index e70a8bea4abd5..80689d81d0cc1 100644 --- a/tests/ui/reborrow/custom_marker.rs +++ b/tests/ui/reborrow/custom_marker.rs @@ -1,8 +1,8 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs index cc4013376bdae..17c7bac98d17a 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -1,8 +1,8 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; -use std::marker::PhantomData; +use std::marker::{CoerceShared, PhantomData, Reborrow}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Debug, Clone, Copy)] diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs index c05e6309f19d9..56bc1f896da0f 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -1,8 +1,8 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; -use std::marker::PhantomData; +use std::marker::{CoerceShared, PhantomData, Reborrow}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Debug, Clone, Copy)] diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs index 0fbb04342283e..532d13da258c8 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -1,11 +1,9 @@ #![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; -use std::marker::PhantomData; +use std::marker::{CoerceShared, PhantomData, Reborrow}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] struct CustomMarkerRef<'a>(PhantomData<&'a ()>); impl<'a> CoerceShared> for CustomMarker<'a> {} @@ -19,4 +17,5 @@ fn main() { let b = method(a); let c = method(a); let _ = (a, b, c); + //~^ ERROR: cannot move out of `a` because it is borrowed } diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr new file mode 100644 index 0000000000000..90382af3ce30e --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_coerce_shared_move.rs:19:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let c = method(a); +LL | let _ = (a, b, c); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs index b9cf6d141f5a0..74b9bac22ed0e 100644 --- a/tests/ui/reborrow/custom_marker_deref.rs +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -1,8 +1,8 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs index 4ae9ee9bc1643..3baf320b583b7 100644 --- a/tests/ui/reborrow/custom_marker_mut_a_b.rs +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -1,8 +1,6 @@ #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} @@ -14,5 +12,6 @@ fn main() { let a = CustomMarker(PhantomData); let b = method(a); let c = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time let _ = (b, c); } diff --git a/tests/ui/reborrow/custom_marker_mut_a_b.stderr b/tests/ui/reborrow/custom_marker_mut_a_b.stderr new file mode 100644 index 0000000000000..36e3bc2918032 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.stderr @@ -0,0 +1,14 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_a_b.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let c = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (b, c); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self.rs b/tests/ui/reborrow/custom_marker_mut_self.rs index a4b1d269f6b8f..a688f503517d0 100644 --- a/tests/ui/reborrow/custom_marker_mut_self.rs +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -1,8 +1,6 @@ #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} diff --git a/tests/ui/reborrow/custom_marker_mut_self.stderr b/tests/ui/reborrow/custom_marker_mut_self.stderr new file mode 100644 index 0000000000000..77262eed339db --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.stderr @@ -0,0 +1,15 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self.rs:14:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.rs b/tests/ui/reborrow/custom_marker_mut_self_a.rs index 846ebf5669cdd..f4cc8defb05e6 100644 --- a/tests/ui/reborrow/custom_marker_mut_self_a.rs +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -1,8 +1,6 @@ #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} @@ -14,5 +12,7 @@ fn main() { let a = CustomMarker(PhantomData); let b = method(a); let _ = method(a); + //~^ ERROR: cannot borrow `a` as mutable more than once at a time let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed } diff --git a/tests/ui/reborrow/custom_marker_mut_self_a.stderr b/tests/ui/reborrow/custom_marker_mut_self_a.stderr new file mode 100644 index 0000000000000..4241b6ec15a20 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.stderr @@ -0,0 +1,28 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_marker_mut_self_a.rs:14:20 + | +LL | let b = method(a); + | - first mutable borrow occurs here +LL | let _ = method(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = (a, b); + | - first borrow later used here + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_a.rs:16:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let b = method(a); + | - borrow of `a` occurs here +... +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0505. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.rs b/tests/ui/reborrow/custom_marker_mut_self_b.rs index 3360f941b7357..16356954908b0 100644 --- a/tests/ui/reborrow/custom_marker_mut_self_b.rs +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -1,8 +1,6 @@ #![feature(reborrow)] -use std::ops::{Reborrow}; -use std::marker::PhantomData; +use std::marker::{Reborrow, PhantomData}; -#[derive(Debug)] struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} @@ -15,4 +13,5 @@ fn main() { let _ = method(a); let b = method(a); let _ = (a, b); + //~^ ERROR: cannot move out of `a` because it is borrowed } diff --git a/tests/ui/reborrow/custom_marker_mut_self_b.stderr b/tests/ui/reborrow/custom_marker_mut_self_b.stderr new file mode 100644 index 0000000000000..adca4331f1bbf --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/custom_marker_mut_self_b.rs:15:14 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | let _ = method(a); +LL | let b = method(a); + | - borrow of `a` occurs here +LL | let _ = (a, b); + | ^ - borrow later used here + | | + | move out of `a` occurs here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.rs b/tests/ui/reborrow/custom_marker_two_lifetimes.rs new file mode 100644 index 0000000000000..d03282145054d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.rs @@ -0,0 +1,8 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a, 'b>(PhantomData<&'a mut ()>, PhantomData<&'b ()>); +impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} +//~^ ERROR: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + +fn main() {} diff --git a/tests/ui/reborrow/custom_marker_two_lifetimes.stderr b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr new file mode 100644 index 0000000000000..ce5c4d09aeb79 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_two_lifetimes.stderr @@ -0,0 +1,8 @@ +error: implementing `Reborrow` requires that a single lifetime parameter is passed between source and target + --> $DIR/custom_marker_two_lifetimes.rs:5:1 + | +LL | impl<'a, 'b> Reborrow for CustomMarker<'a, 'b> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/custom_mut.rs b/tests/ui/reborrow/custom_mut.rs index 1e7c469323822..39b5ed4906102 100644 --- a/tests/ui/reborrow/custom_mut.rs +++ b/tests/ui/reborrow/custom_mut.rs @@ -1,13 +1,16 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::Reborrow; +use std::marker::Reborrow; +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} -fn method(a: CustomMut<'_, ()>) {} +fn method(_: CustomMut<'_, ()>) {} fn main() { let a = CustomMut(&mut ()); let _ = method(a); - let _ = method(a); //~ERROR use of moved value: `a` + let _ = method(a); } diff --git a/tests/ui/reborrow/custom_mut.stderr b/tests/ui/reborrow/custom_mut.stderr deleted file mode 100644 index 3b3f47b62d6fa..0000000000000 --- a/tests/ui/reborrow/custom_mut.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0382]: use of moved value: `a` - --> $DIR/custom_mut.rs:12:20 - | -LL | let a = CustomMut(&mut ()); - | - move occurs because `a` has type `CustomMut<'_, ()>`, which does not implement the `Copy` trait -LL | let _ = method(a); - | - value moved here -LL | let _ = method(a); - | ^ value used here after move - | -note: consider changing this parameter type in function `method` to borrow instead if owning the value isn't necessary - --> $DIR/custom_mut.rs:7:14 - | -LL | fn method(a: CustomMut<'_, ()>) {} - | ------ ^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function -note: if `CustomMut<'_, ()>` implemented `Clone`, you could clone the value - --> $DIR/custom_mut.rs:4:1 - | -LL | struct CustomMut<'a, T>(&'a mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let _ = method(a); - | - you could clone this value - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.rs b/tests/ui/reborrow/custom_mut_coerce_shared.rs index 9fa77856de02d..dcc02db5802bd 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,6 +1,9 @@ +//@ run-pass + #![feature(reborrow)] -use std::ops::{CoerceShared, Reborrow}; +use std::marker::{CoerceShared, Reborrow}; +#[allow(unused)] struct CustomMut<'a, T>(&'a mut T); impl<'a, T> Reborrow for CustomMut<'a, T> {} impl<'a, T> CoerceShared> for CustomMut<'a, T> {} diff --git a/tests/ui/reborrow/custom_mut_coerce_shared.stderr b/tests/ui/reborrow/custom_mut_coerce_shared.stderr deleted file mode 100644 index 508651badc0a4..0000000000000 --- a/tests/ui/reborrow/custom_mut_coerce_shared.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/custom_mut_coerce_shared.rs:23:12 - | -LL | method(a); - | ------ ^ expected `CustomRef<'_, ()>`, found `CustomMut<'_, ()>` - | | - | arguments to this function are incorrect - | - = note: expected struct `CustomRef<'_, ()>` - found struct `CustomMut<'_, ()>` -note: function defined here - --> $DIR/custom_mut_coerce_shared.rs:19:4 - | -LL | fn method(a: CustomRef<'_, ()>) {} - | ^^^^^^ -------------------- - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. From 235d4718c3c27434a4b21a14d26d3e5159f98133 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 2 Feb 2026 23:15:58 +0200 Subject: [PATCH 32/34] remove Reborrow derive for now --- .../rustc_builtin_macros/src/deriving/mod.rs | 1 - .../src/deriving/reborrow.rs | 210 ------------------ compiler/rustc_builtin_macros/src/lib.rs | 1 - 3 files changed, 212 deletions(-) delete mode 100644 compiler/rustc_builtin_macros/src/deriving/reborrow.rs diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index a4c7baebb5ce5..cee6952fa3460 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -25,7 +25,6 @@ pub(crate) mod debug; pub(crate) mod default; pub(crate) mod from; pub(crate) mod hash; -pub(crate) mod reborrow; #[path = "cmp/eq.rs"] pub(crate) mod eq; diff --git a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs b/compiler/rustc_builtin_macros/src/deriving/reborrow.rs deleted file mode 100644 index cb41d6458282c..0000000000000 --- a/compiler/rustc_builtin_macros/src/deriving/reborrow.rs +++ /dev/null @@ -1,210 +0,0 @@ -use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData}; -use rustc_data_structures::fx::FxHashSet; -use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Ident, Span, kw, sym}; -use thin_vec::{ThinVec, thin_vec}; - -use crate::deriving::generic::ty::*; -use crate::deriving::generic::*; -use crate::deriving::path_std; - -pub(crate) fn expand_deriving_reborrow( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - // The simple form is `fn reborrow(self) -> Self { self }`, possibly with - // some additional `AssertParamIsReborrow` assertions. - // - // We can use the simple form if either of the following are true. - // - The type derives Copy and there are no generic parameters. (If we - // used the simple form with generics, we'd have to bound the generics - // with Reborrow + Copy, and then there'd be no Reborrow impl at all if the - // user fills in something that is Reborrow but not Copy. After - // specialization we can remove this no-generics limitation.) - // - The item is a union. (Unions with generic parameters still can derive - // Reborrow because they require Copy for deriving, Reborrow alone is not - // enough. Whether Reborrow is implemented for fields is irrelevant so we - // don't assert it.) - let bounds; - let is_simple; - match item { - Annotatable::Item(annitem) => match &annitem.kind { - ItemKind::Struct(_, Generics { params, .. }, _) - | ItemKind::Enum(_, Generics { params, .. }, _) => { - let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); - let has_derive_copy = cx.resolver.has_derive_copy(container_id); - if has_derive_copy - && !params - .iter() - .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) - { - bounds = vec![]; - is_simple = true; - // substructure = combine_substructure(Box::new(|c, s, sub| { - // cs_reborrow_simple("Reborrow", c, s, sub, false) - // })); - } else { - bounds = vec![]; - is_simple = false; - // substructure = - // combine_substructure(Box::new(|c, s, sub| cs_reborrow("Reborrow", c, s, sub))); - } - } - ItemKind::Union(..) => { - bounds = vec![Path(path_std!(marker::Copy))]; - is_simple = true; - // substructure = combine_substructure(Box::new(|c, s, sub| { - // cs_reborrow_simple("Reborrow", c, s, sub, true) - // })); - } - _ => cx.dcx().span_bug(span, "`#[derive(Reborrow)]` on wrong item kind"), - }, - _ => cx.dcx().span_bug(span, "`#[derive(Reborrow)]` on trait item or impl item"), - } - - let trait_def = TraitDef { - span, - path: path_std!(marker::Reborrow), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: true, - additional_bounds: bounds, - supports_unions: true, - methods: vec![], - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - document: true, - safety: Safety::Default, - }; - - trait_def.expand_ext(cx, mitem, item, push, is_simple) -} - -fn _cs_reborrow_simple( - name: &str, - cx: &ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, - is_union: bool, -) -> BlockOrExpr { - let mut stmts = ThinVec::new(); - let mut seen_type_names = FxHashSet::default(); - let mut process_variant = |variant: &VariantData| { - for field in variant.fields() { - // This basic redundancy checking only prevents duplication of - // assertions like `AssertParamIsReborrow` where the type is a - // simple name. That's enough to get a lot of cases, though. - if let Some(name) = field.ty.kind.is_simple_path() - && !seen_type_names.insert(name) - { - // Already produced an assertion for this type. - // Anonymous structs or unions must be eliminated as they cannot be - // type parameters. - } else { - // let _: AssertParamIsReborrow; - super::assert_ty_bounds( - cx, - &mut stmts, - field.ty.clone(), - field.span, - &[sym::reborrow, sym::AssertParamIsCopy], - ); - } - } - }; - - if is_union { - // Just a single assertion for unions, that the union impls `Copy`. - // let _: AssertParamIsCopy; - let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); - super::assert_ty_bounds( - cx, - &mut stmts, - self_ty, - trait_span, - &[sym::reborrow, sym::AssertParamIsCopy], - ); - } else { - match *substr.fields { - StaticStruct(vdata, ..) => { - process_variant(vdata); - } - StaticEnum(enum_def, ..) => { - for variant in &enum_def.variants { - process_variant(&variant.data); - } - } - _ => cx.dcx().span_bug( - trait_span, - format!("unexpected substructure in simple `derive({name})`"), - ), - } - } - BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))) -} - -fn _cs_reborrow( - name: &str, - cx: &ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, -) -> BlockOrExpr { - let ctor_path; - let all_fields; - let fn_path = cx.std_path(&[sym::reborrow, sym::Reborrow, sym::reborrow]); - let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| { - let args = thin_vec![field.self_expr.clone()]; - cx.expr_call_global(field.span, fn_path.clone(), args) - }; - - let vdata; - match substr.fields { - Struct(vdata_, af) => { - ctor_path = cx.path(trait_span, vec![substr.type_ident]); - all_fields = af; - vdata = *vdata_; - } - EnumMatching(.., variant, af) => { - ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); - all_fields = af; - vdata = &variant.data; - } - EnumDiscr(..) | AllFieldlessEnum(..) => { - cx.dcx().span_bug(trait_span, format!("enum discriminants in `derive({name})`",)) - } - StaticEnum(..) | StaticStruct(..) => { - cx.dcx().span_bug(trait_span, format!("associated function in `derive({name})`")) - } - } - - let expr = match *vdata { - VariantData::Struct { .. } => { - let fields = all_fields - .iter() - .map(|field| { - let Some(ident) = field.name else { - cx.dcx().span_bug( - trait_span, - format!("unnamed field in normal struct in `derive({name})`",), - ); - }; - let call = subcall(cx, field); - cx.field_imm(field.span, ident, call) - }) - .collect::>(); - - cx.expr_struct(trait_span, ctor_path, fields) - } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => cx.expr_path(ctor_path), - }; - BlockOrExpr::new_expr(expr) -} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index ad9e4a463535b..120e3f849ff63 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -137,7 +137,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Ord: ord::expand_deriving_ord, PartialEq: partial_eq::expand_deriving_partial_eq, PartialOrd: partial_ord::expand_deriving_partial_ord, - Reborrow: reborrow::expand_deriving_reborrow, CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, From: from::expand_deriving_from, } From 4af26614a597bf3201422542e777e434cbe54803 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Feb 2026 22:20:58 +0200 Subject: [PATCH 33/34] add comments, fix Reborrow as_rvalue --- compiler/rustc_hir_typeck/src/expr_use_visitor.rs | 7 ++++++- compiler/rustc_middle/src/mir/syntax.rs | 14 +++++++++++++- compiler/rustc_middle/src/thir.rs | 7 +++++++ compiler/rustc_middle/src/ty/adjustment.rs | 8 ++++++-- .../rustc_mir_build/src/builder/expr/as_rvalue.rs | 5 +++-- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 9950e902f7f55..79a5df6a789b3 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -760,7 +760,12 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } adjustment::Adjust::GenericReborrow(_reborrow) => { - // Do the magic! + // To build an expression as a place expression, it needs to be a field + // projection or deref at the outmost layer. So it is field projection or deref + // on an adjusted value. But this means that adjustment is applied on a + // subexpression that is not the final operand/rvalue for function call or + // assignment. This is a contradiction. + unreachable!(); } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index d1b6de27113fe..3a43729c9c612 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1480,7 +1480,19 @@ pub enum Rvalue<'tcx> { /// Wraps a value in an unsafe binder. WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), - /// A reborrowable type being reborrowed + /// Creates a bitwise copy of the indicated place with the same type (if Mut) or its + /// CoerceShared target type (if Not), and disables the place for writes (and reads, if Mut) for + /// the copy's lifetime. The type is known to be an ADT with exactly one lifetime parameter, and + /// it is known to implement the Reborrow trait (for Mut), and the CoerceShared trait (only if + /// Not). The CoerceShared target type is known to implement Copy and have the same memory + /// layout as the source type. + /// + /// Future work may add support for multiple lifetimes and changing memory layout as part of + /// CoerceShared. These may be end up implemented as multiple MIR operations. + /// + /// This is produced by the [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow Reborrow(Mutability, Place<'tcx>), } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 61b8a5ff2ecc4..1a004fd494f03 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -550,6 +550,13 @@ pub enum ExprKind<'tcx> { Yield { value: ExprId, }, + /// Use of an ADT that implements the Reborrow (for Mut) or CoerceShared traits (for Not). This + /// expression is produced by the [`Adjust::GenericReborrow`] in places where normally the ADT + /// would be moved or assigned over. Instead, this produces an [`Rvalue::Reborrow`] which + /// produces a bitwise copy of the source ADT and disables the source for the copy's lifetime. + /// + /// [`Adjust::GenericReborrow`]: crate::ty::adjustment::Adjust::GenericReborrow + /// [`Rvalue::Reborrow`]: mir::Rvalue::Reborrow Reborrow { source: ExprId, mutability: Mutability, diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 0194020e17727..3eaf85fee8aa5 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -107,8 +107,12 @@ pub enum Adjust { /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. ReborrowPin(hir::Mutability), - /// Generate a T and use the Reborrow or CoerceShared trait to produce a - /// new T. + /// Take a user-type T implementing the Reborrow trait (for Mut) or the CoerceShared trait (for + /// Not) and reborrow as `T` or `CoreceShared`. + /// + /// This produces an [`ExprKind::Reborrow`]. + /// + /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow GenericReborrow(hir::Mutability), } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 7232359cd457f..5e102a4b068b8 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -492,8 +492,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.and(Rvalue::Use(operand)) } - ExprKind::Reborrow { source: _, mutability: _, ty: _ } => { - todo!(); + ExprKind::Reborrow { source, mutability, ty: _ } => { + let temp = unpack!(block = this.as_temp(block, scope, source, mutability)); + block.and(Rvalue::Reborrow(mutability, temp.into())) } } } From 41f7cf58279c40e4d38e9160117793cd6e747c2f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Feb 2026 22:22:34 +0200 Subject: [PATCH 34/34] add one test --- .../ui/reborrow/custom_marker_assign_deref.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/ui/reborrow/custom_marker_assign_deref.rs diff --git a/tests/ui/reborrow/custom_marker_assign_deref.rs b/tests/ui/reborrow/custom_marker_assign_deref.rs new file mode 100644 index 0000000000000..79ea2a35acdaf --- /dev/null +++ b/tests/ui/reborrow/custom_marker_assign_deref.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +impl<'a> std::ops::Deref for CustomMarker<'a> { + type Target = CustomMarker<'a>; + fn deref(&self) -> &Self::Target { + self + } +} + +impl<'a> std::ops::DerefMut for CustomMarker<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self + } +} + +fn main() { + let mut a = CustomMarker(PhantomData); + + *a = CustomMarker(PhantomData); +}