diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4644c210137fe..4a9e2f4a9774c 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,11 +2,12 @@ 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}; -use rustc_middle::span_bug; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; @@ -300,6 +301,71 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { idx }; + 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 { + unreachable!() + }; + let &ty::Adt(target_adt, assigned_args) = + assigned_place.ty(self.body, self.tcx).ty.kind() + else { + unreachable!() + }; + 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)) = + assigned_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 = 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)) = + assigned_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 = mir::BorrowKind::Shared; + 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.local_map.entry(borrowed_place.local).or_default().insert(idx); } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 813ceaeb8da9f..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) = 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 4a059481c326b..274bc79eafd4d 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, @@ -1593,6 +1594,35 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), + &Rvalue::Reborrow(mutability, place) => { + let access_kind = ( + Deep, + if mutability == Mutability::Mut { + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: MutBorrowKind::Default, + })) + } else { + Read(ReadKind::Borrow(BorrowKind::Shared)) + }, + ); + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::Yes, + 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..2e0f19a543a0f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -325,6 +325,20 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { } Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in borrowck"), + &Rvalue::Reborrow(mutability, place) => { + let access_kind = ( + Deep, + 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 676b45e9974cb..79ef007e8202b 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(mutability, rvalue) = rvalue { + self.add_generic_reborrow_constraint(*mutability, 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); @@ -635,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, @@ -1677,7 +1690,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::Reborrow(..) => {} } } @@ -2243,7 +2257,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 +2456,120 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + fn add_generic_reborrow_constraint( + &mut self, + mutability: Mutability, + 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({:?}, {:?}, {:?}, {:?})", + mutability, 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; + 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, + // 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 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) = + 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); + if let ( + ty::Ref(borrow_region, _, Mutability::Mut), + ty::Ref(ref_region, _, Mutability::Not), + ) = (borrowed_ty.kind(), dest_ty.kind()) + { + 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, + 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( &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..7a845dc5ef209 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..e9d9d56c4b44d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -709,6 +709,12 @@ 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 } } + // 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_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 95dbf42d4d441..1bbeba2e5ad7b 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..2323990025ceb 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..8c5c8a85afda7 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..be9937bb63d33 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_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..9aabf5d05a949 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,7 +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); + 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/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 61562cc1e4f30..13698a2682597 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -9,10 +9,11 @@ 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_middle::ty::adjustment::CoerceUnsizedInfo; +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::relate::solver_relating::RelateExt; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, }; @@ -22,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; @@ -42,6 +43,8 @@ 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)?; checker.check( @@ -192,6 +195,28 @@ 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; + 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 +402,344 @@ 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 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 (def, args) = match source.kind() { + &ty::Adt(def, args) if def.is_struct() => (def, args), + _ => { + // Note: reusing error here as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { 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 ty = f.ty(tcx, args); + if ty.is_phantom_data() { + return None; + } + 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) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name })); + } + + 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 { + 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(()) +} + +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<(), 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 = + 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, +) -> 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 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, coerce_shared_trait); + let Some((target, _obligations)) = + structurally_normalize_ty(tcx, &infcx, impl_did, span, trait_ref.args.type_at(1)) + else { + todo!("something went wrong with structurally_normalize_ty"); + }; + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + 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() => + { + // 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)| { + let a = f.ty(tcx, args_b); + + if a.is_phantom_data() { + return None; + } + + 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 + .iter_enumerated() + .filter_map(|(i, f)| { + let b = f.ty(tcx, args_b); + + if b.is_phantom_data() { + return None; + } + + Some((i, b, tcx.def_span(f.did))) + }) + .collect::>(); + + 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() + { + 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::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 + } + } + + _ => { + // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })); + } + }; + + // 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 + { + // 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 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() + { + // 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, [])?; + } 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_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 58483dc44fe91..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; + 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, @@ -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_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2c..aa7784d673427 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1192,6 +1192,22 @@ pub(crate) struct CoerceMulti { pub fields: MultiSpan, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_shared_not_zero)] +pub(crate) struct CoerceSharedNotSingleLifetimeParam { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_shared_multi)] +pub(crate) struct CoerceSharedMulti { + #[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_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 52ea6cdeaa0eb..d475f6b2fb952 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -262,7 +262,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 +280,26 @@ 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, + ) + .must_apply_modulo_regions() => + { + let reborrow_coerce = self.commit_if_ok(|_| self.coerce_reborrow(a, b)); + if reborrow_coerce.is_ok() { + return reborrow_coerce; + } + } _ => {} } @@ -302,6 +323,14 @@ 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() => { + 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) + } + } _ => { // Otherwise, just use unification rules. self.unify(a, b, ForceLeakCheck::No) @@ -846,6 +875,74 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No) } + /// 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. + 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) + } + } + + /// 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) = 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, 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![Adjustment { + kind: Adjust::GenericReborrow(ty::Mutability::Not), + target: b, + }], + b, + ), + obligations: ocx.into_pending_obligations(), + }) + } else { + Err(TypeError::Mismatch) + } + } + fn coerce_from_fn_pointer( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ad34994526ed9..79a5df6a789b3 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -758,6 +758,15 @@ 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) => { + // 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)?; } @@ -1291,7 +1300,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_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 ded02595563c9..e53a4b32d590c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1244,6 +1244,14 @@ impl<'tcx> Debug for Rvalue<'tcx> { WrapUnsafeBinder(ref op, ty) => { 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:?}") + } + } } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 393e9c59c3556..5ea123a87acbd 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -769,10 +769,17 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) | Rvalue::ShallowInitBox(_, _) - | Rvalue::WrapUnsafeBinder(_, _) => true, + | Rvalue::WrapUnsafeBinder(_, _) + | Rvalue::Reborrow(..) => true, } } + /// 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>, @@ -818,6 +825,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..3a43729c9c612 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1479,6 +1479,21 @@ pub enum Rvalue<'tcx> { /// Wraps a value in an unsafe binder. WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), + + /// 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>), } #[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..9a8f9a463d132 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(_mutability, place) => { + self.visit_place( + place, + PlaceContext::MutatingUse( + MutatingUseContext::Borrow + ), + location + ); + } } } 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/thir.rs b/compiler/rustc_middle/src/thir.rs index 5e80314035652..1a004fd494f03 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -550,6 +550,18 @@ 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, + 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..060ec7b5bc0c2 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_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518ae..3eaf85fee8aa5 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -106,6 +106,14 @@ pub enum Adjust { /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. ReborrowPin(hir::Mutability), + + /// 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), } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` @@ -192,6 +200,15 @@ 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 {} + /// Information for `CoerceUnsized` impls, storing information we /// have computed about the coercion. /// 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..3e70da312312f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -592,6 +592,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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..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,6 +492,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.and(Rvalue::Use(operand)) } + ExprKind::Reborrow { source, mutability, ty: _ } => { + let temp = unpack!(block = this.as_temp(block, scope, source, mutability)); + block.and(Rvalue::Reborrow(mutability, temp.into())) + } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 1464b5e560bf5..a5d826f7817fb 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 60e05b691a83b..0825eb0abaeb3 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -802,6 +802,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } + ExprKind::Reborrow { source, mutability, ty: _ } => { + let place = unpack!(block = this.as_place(block, source)); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Reborrow(mutability, place.into()), + ); + block.unit() + } }; if !expr_is_block_or_scope { 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 8e02424706eec..6ed81665846fc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -240,6 +240,13 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?kind); kind } + Adjust::GenericReborrow(mutability) => { + let ty = expr.ty; + let expr = self.thir.exprs.push(expr); + let kind = ExprKind::Reborrow { source: expr, mutability, ty }; + + kind + } }; Expr { temp_scope_id, ty: adjustment.target, span, kind } 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..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,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..fdef7630df878 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -604,6 +604,13 @@ 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); + 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_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 4b2c52ad7999d..3ce2a3e9ade64 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::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/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/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 604f1da1a3abb..e969936e99bbc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -468,7 +468,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { 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..d29525fe66aad 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..97c455949d289 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..f128a0a2e1f2e 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..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::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..fce65376b0abd 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"); @@ -1469,6 +1469,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, 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..f59391906148b 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/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, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index f6d08bd458bdd..c229581b26f73 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 { 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 f83f4233a4de5..0000000000000 --- a/library/core/src/ops/reborrow.rs +++ /dev/null @@ -1,16 +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 { - /// The type of this value when reborrowed as shared. - type Target: Copy; -} 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..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,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, _) 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 new file mode 100644 index 0000000000000..80689d81d0cc1 --- /dev/null +++ b/tests/ui/reborrow/custom_marker.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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 _ = method(a); +} 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); +} 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..17c7bac98d17a --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +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..56bc1f896da0f --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -0,0 +1,22 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +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..532d13da258c8 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} +#[derive(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); + //~^ 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 new file mode 100644 index 0000000000000..74b9bac22ed0e --- /dev/null +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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_marker_mut_a_b.rs b/tests/ui/reborrow/custom_marker_mut_a_b.rs new file mode 100644 index 0000000000000..3baf320b583b7 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_a_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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); + //~^ 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 new file mode 100644 index 0000000000000..a688f503517d0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self.rs @@ -0,0 +1,15 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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.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 new file mode 100644 index 0000000000000..f4cc8defb05e6 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_a.rs @@ -0,0 +1,18 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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); + //~^ 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 new file mode 100644 index 0000000000000..16356954908b0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_self_b.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +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); + //~^ 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 e2d25835c093a..dcc02db5802bd 100644 --- a/tests/ui/reborrow/custom_mut_coerce_shared.rs +++ b/tests/ui/reborrow/custom_mut_coerce_shared.rs @@ -1,11 +1,12 @@ +//@ 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> { - type Target = CustomRef<'a, T>; -} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} struct CustomRef<'a, T>(&'a T); @@ -16,13 +17,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<'_, ()>` } 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`.