From 2060ed2fdda87878a867317450715cfd0add4318 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 5 Jun 2026 18:20:46 +0200 Subject: [PATCH 1/7] Add CoerceShared field-wise reborrow WF checks Localize the WF helper logic to builtin coherence and add the directly related UI coverage. --- .../src/coherence/builtin.rs | 429 ++++++++++++++---- .../rustc_hir_analysis/src/diagnostics.rs | 64 ++- .../auxiliary/reborrow_foreign_private.rs | 16 + .../coerce-shared-associated-type-field.rs | 3 +- .../coerce-shared-decl-macro-hygiene.rs | 3 +- .../coerce-shared-field-lifetime-swap.rs | 4 +- .../coerce-shared-field-lifetime-swap.stderr | 12 +- .../reborrow/coerce-shared-field-relations.rs | 5 +- .../coerce-shared-field-relations.stderr | 19 +- .../coerce-shared-foreign-private-field.rs | 3 +- ...coerce-shared-foreign-private-field.stderr | 7 + ...erce-shared-foreign-private-tuple-field.rs | 26 +- ...-shared-foreign-private-tuple-field.stderr | 7 + .../coerce-shared-lifetime-mismatch.rs | 4 +- .../coerce-shared-lifetime-mismatch.stderr | 10 +- .../coerce-shared-missing-target-field.rs | 4 +- .../coerce-shared-missing-target-field.stderr | 9 +- .../coerce-shared-mut-ref-field-validation.rs | 4 +- ...rce-shared-mut-ref-field-validation.stderr | 13 +- .../reborrow/coerce-shared-wrong-generic.rs | 4 +- .../coerce-shared-wrong-generic.stderr | 13 +- ...er-coerce-shared-corrected-issue-156309.rs | 25 + 22 files changed, 530 insertions(+), 154 deletions(-) create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr create mode 100644 tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 1b174631aa890..3c97fae66ab22 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::ItemKind; +use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; @@ -17,7 +18,7 @@ use rustc_middle::ty::relate::solver_relating::RelateExt; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, suggest_constraining_type_params, }; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, @@ -535,7 +536,7 @@ pub(crate) fn reborrow_info<'tcx>( }; let lifetimes_count = generic_lifetime_params_count(args); - let data_fields = collect_struct_data_fields(tcx, def, args); + let data_fields = wf_data_fields(tcx, def, args); if lifetimes_count != 1 { let item = tcx.hir_expect_item(impl_did); @@ -553,15 +554,15 @@ 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 { + for field in data_fields { if assert_field_type_is_reborrow( tcx, &infcx, reborrow_trait, impl_did, param_env, - field, - span, + field.ty, + field.span, ) .is_ok() { @@ -570,7 +571,7 @@ pub(crate) fn reborrow_info<'tcx>( } // Field does not implement Reborrow: it must be Copy. - assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; + assert_field_type_is_copy(tcx, impl_did, param_env, field.ty, field.span)?; } Ok(()) @@ -633,26 +634,14 @@ pub(crate) fn coerce_shared_info<'tcx>( let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - let data = match (source.kind(), target.kind()) { + 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 = generic_lifetime_params_count(args_a); - let a_data_fields = collect_struct_data_fields(tcx, def_a, args_a); - let b_lifetimes_count = generic_lifetime_params_count(args_b); - let b_data_fields = collect_struct_data_fields(tcx, def_b, args_b); - - 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 a_lifetime = single_region_arg(args_a); + let b_lifetime = single_region_arg(args_b); + + if a_lifetime.is_none() || b_lifetime.is_none() { let item = tcx.hir_expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind @@ -667,79 +656,43 @@ pub(crate) fn coerce_shared_info<'tcx>( .emit_err(diagnostics::CoerceSharedMulti { span, trait_name })); } - if a_data_fields.len() == 1 { - // We found one data field for both: we'll attempt to perform CoerceShared between - // them below. - let (a, span_a) = a_data_fields[0]; - let (b, span_b) = b_data_fields[0]; + if a_lifetime != b_lifetime { + 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) + }; - Some((a, b, coerce_shared_trait, 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(diagnostics::CoerceSharedLifetimeMismatch { span, trait_name })); } - } - _ => { - // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. - return Err(tcx - .dcx() - .emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })); - } - }; + validate_reborrow_field_access(tcx, impl_did, def_a, trait_name, span)?; + validate_reborrow_field_access(tcx, impl_did, def_b, trait_name, span)?; - // 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, 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(()); - } - 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( + validate_coerce_shared_fields( tcx, - cause, + impl_did, param_env, - ty::TraitRef::new(tcx, trait_def_id, [source, target]), - ); - ocx.register_obligation(obligation); - let errors = ocx.evaluate_obligations_error_on_ambiguity(); + coerce_shared_trait, + trait_name, + span, + def_a, + args_a, + def_b, + args_b, + ) + } - 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)?; + _ => { + // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. + Err(tcx.dcx().emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })) } } - - Ok(()) } fn trait_impl_lifetime_params_count(tcx: TyCtxt<'_>, did: LocalDefId) -> usize { @@ -754,36 +707,312 @@ fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize { args.iter().filter(|arg| arg.as_region().is_some()).count() } -// FIXME(#155345): This should return `Unnormalized` -fn collect_struct_data_fields<'tcx>( +#[derive(Clone, Copy, Debug)] +struct WfField<'tcx> { + ident: Ident, + name: Symbol, + ty: Ty<'tcx>, + span: Span, +} + +#[derive(Clone, Copy, Debug)] +struct WfFieldPair<'tcx> { + source: WfField<'tcx>, + target: WfField<'tcx>, +} + +#[derive(Clone, Copy, Debug)] +enum WfFieldPairError<'tcx> { + FieldStyleMismatch, + MissingSourceField { target: WfField<'tcx> }, +} + +fn single_region_arg<'tcx>(args: ty::GenericArgsRef<'tcx>) -> Option> { + let mut lifetimes = args.iter().filter_map(|arg| arg.as_region()); + let lifetime = lifetimes.next()?; + lifetimes.next().is_none().then_some(lifetime) +} + +fn wf_data_fields<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, args: ty::GenericArgsRef<'tcx>, -) -> Vec<(Ty<'tcx>, Span)> { +) -> Vec> { def.non_enum_variant() .fields .iter() - .filter_map(|f| { - // Ignore PhantomData fields - let ty = f.ty(tcx, args).skip_norm_wip(); - if ty.is_phantom_data() { - return None; - } - Some((ty, tcx.def_span(f.did))) + .filter_map(|field| { + let ty = field.ty(tcx, args).skip_norm_wip(); + (!ty.is_phantom_data()).then_some(WfField { + ident: field.ident(tcx), + name: field.name, + ty, + span: tcx.def_span(field.did), + }) }) .collect() } +// This is a coherence/WF check only. It verifies that the impl describes a +// structurally valid field relation; later runtime lowering still starts from +// the single reborrow adjustment and is not modeled here. +fn coerce_shared_wf_field_pairs<'tcx>( + tcx: TyCtxt<'tcx>, + source_def: ty::AdtDef<'tcx>, + source_args: ty::GenericArgsRef<'tcx>, + target_def: ty::AdtDef<'tcx>, + target_args: ty::GenericArgsRef<'tcx>, +) -> Result>, WfFieldPairError<'tcx>> { + let source_variant = source_def.non_enum_variant(); + let target_variant = target_def.non_enum_variant(); + if source_variant.ctor_kind() != target_variant.ctor_kind() { + return Err(WfFieldPairError::FieldStyleMismatch); + } + + let by_position = matches!(target_variant.ctor_kind(), Some(CtorKind::Fn)); + let source_fields = wf_data_fields(tcx, source_def, source_args); + let target_fields = wf_data_fields(tcx, target_def, target_args); + + if by_position { + target_fields + .into_iter() + .zip(source_fields.into_iter().map(Some).chain(std::iter::repeat(None))) + .map(|(target, source)| { + let source = source.ok_or(WfFieldPairError::MissingSourceField { target })?; + Ok(WfFieldPair { source, target }) + }) + .collect() + } else { + target_fields + .into_iter() + .map(|target| { + let source = source_fields + .iter() + .copied() + .find(|source| { + tcx.hygienic_eq(target.ident, source.ident, source_variant.def_id) + }) + .ok_or(WfFieldPairError::MissingSourceField { target })?; + + Ok(WfFieldPair { source, target }) + }) + .collect() + } +} + +fn validate_reborrow_field_access( + tcx: TyCtxt<'_>, + impl_did: LocalDefId, + def: ty::AdtDef<'_>, + trait_name: &'static str, + span: Span, +) -> Result<(), ErrorGuaranteed> { + let module = tcx.parent_module_from_def_id(impl_did); + let variant = def.non_enum_variant(); + if variant.field_list_has_applicable_non_exhaustive() { + return Err(tcx + .dcx() + .emit_err(diagnostics::CoerceSharedInaccessibleField { span, trait_name })); + } + + for field in &variant.fields { + if !field.vis.is_accessible_from(module, tcx) { + return Err(tcx + .dcx() + .emit_err(diagnostics::CoerceSharedInaccessibleField { span, trait_name })); + } + } + + Ok(()) +} + +fn validate_coerce_shared_fields<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + coerce_shared_trait: DefId, + trait_name: &'static str, + span: Span, + source_def: ty::AdtDef<'tcx>, + source_args: ty::GenericArgsRef<'tcx>, + target_def: ty::AdtDef<'tcx>, + target_args: ty::GenericArgsRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let field_pairs = + match coerce_shared_wf_field_pairs(tcx, source_def, source_args, target_def, target_args) { + Ok(field_pairs) => field_pairs, + Err(WfFieldPairError::FieldStyleMismatch) => { + return Err(tcx + .dcx() + .emit_err(diagnostics::CoerceSharedFieldStyleMismatch { span, trait_name })); + } + Err(WfFieldPairError::MissingSourceField { target }) => { + return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMissingField { + span: target.span, + trait_name, + })); + } + }; + + for field_pair in field_pairs { + validate_coerce_shared_field( + tcx, + impl_did, + param_env, + coerce_shared_trait, + trait_name, + span, + field_pair.source, + field_pair.target, + )?; + } + + Ok(()) +} + +fn validate_coerce_shared_field<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + coerce_shared_trait: DefId, + trait_name: &'static str, + span: Span, + source: WfField<'tcx>, + target: WfField<'tcx>, +) -> Result<(), ErrorGuaranteed> { + if matches!( + (source.ty.kind(), target.ty.kind()), + (&ty::Ref(_, _, ty::Mutability::Mut), &ty::Ref(_, _, ty::Mutability::Not)) + | (&ty::Alias(..), _) + | (_, &ty::Alias(..)) + ) && field_tys_satisfy_relation_after_normalization_and_resolution( + tcx, + impl_did, + param_env, + source.ty, + target.ty, + source.span, + FieldRelation::MutRefToSharedRef, + ) { + return Ok(()); + } + + if field_tys_satisfy_relation_after_normalization_and_resolution( + tcx, + impl_did, + param_env, + source.ty, + target.ty, + source.span, + FieldRelation::Equal, + ) { + return assert_field_type_is_copy(tcx, impl_did, param_env, source.ty, source.span); + } + + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, impl_did); + ocx.register_obligation(Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, coerce_shared_trait, [source.ty, target.ty]), + )); + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + + if !errors.is_empty() { + return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedFieldMismatch { + span: target.span, + source_span: source.span, + source_name: source.name, + source_ty: source.ty, + target_name: target.name, + target_ty: target.ty, + trait_name, + })); + } + + ocx.resolve_regions_and_report_errors(impl_did, param_env, []) +} + +enum FieldRelation { + Equal, + MutRefToSharedRef, +} + +fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>( + tcx: TyCtxt<'tcx>, + impl_did: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, + span: Span, + relation: FieldRelation, +) -> bool { + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = traits::ObligationCause::misc(span, impl_did); + let ocx = ObligationCtxt::new(&infcx); + + let Ok(source_ty) = + ocx.structurally_normalize_ty(&cause, param_env, Unnormalized::new_wip(source_ty)) + else { + return false; + }; + let Ok(target_ty) = + ocx.structurally_normalize_ty(&cause, param_env, Unnormalized::new_wip(target_ty)) + else { + return false; + }; + + if !ocx.evaluate_obligations_error_on_ambiguity().is_empty() { + return false; + } + + let (source_ty, target_ty) = match relation { + FieldRelation::Equal => (source_ty, target_ty), + FieldRelation::MutRefToSharedRef => { + let ( + &ty::Ref(source_region, source_referent_ty, ty::Mutability::Mut), + &ty::Ref(target_region, target_referent_ty, ty::Mutability::Not), + ) = (source_ty.kind(), target_ty.kind()) + else { + return false; + }; + infcx.sub_regions( + SubregionOrigin::RelateObjectBound(span), + target_region, + source_region, + ty::VisibleForLeakCheck::Yes, + ); + (source_referent_ty, target_referent_ty) + } + }; + + let Ok(goals) = infcx.eq_structurally_relating_aliases(param_env, source_ty, target_ty, span) + else { + return false; + }; + + ocx.register_obligations( + goals + .into_iter() + .map(|goal| Obligation::new(tcx, cause.clone(), goal.param_env, goal.predicate)), + ); + + ocx.evaluate_obligations_error_on_ambiguity().is_empty() + && ocx.resolve_regions(impl_did, param_env, []).is_empty() +} + 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 infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let copy_trait = tcx.require_lang_item(LangItem::Copy, span); - let ocx = ObligationCtxt::new_with_diagnostics(infcx); + 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])); @@ -793,7 +1022,7 @@ fn assert_field_type_is_copy<'tcx>( if !errors.is_empty() { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } else { - Ok(()) + ocx.resolve_regions_and_report_errors(impl_did, param_env, []) } } diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 5997f16b42917..8d47713a0dd9b 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -1347,13 +1347,75 @@ pub(crate) struct CoerceSharedNotSingleLifetimeParam { } #[derive(Diagnostic)] -#[diag("implementing `{$trait_name}` does not allow multiple lifetimes or fields to be coerced")] +#[diag( + "implementing `{$trait_name}` requires exactly one lifetime argument in the reborrowed type" +)] pub(crate) struct CoerceSharedMulti { #[primary_span] pub span: Span, pub trait_name: &'static str, } +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires source and target to use the same reborrow lifetime \ + argument" +)] +pub(crate) struct CoerceSharedLifetimeMismatch { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires corresponding fields to match, \ + be reborrowable with `CoerceShared`, or coerce a mutable reference field \ + to a shared reference field" +)] +pub(crate) struct CoerceSharedFieldMismatch<'tcx> { + #[primary_span] + #[label("target field `{$target_name}` has type `{$target_ty}`")] + pub span: Span, + #[label("source field `{$source_name}` has type `{$source_ty}`")] + pub source_span: Span, + pub source_name: Symbol, + pub source_ty: Ty<'tcx>, + pub target_name: Symbol, + pub target_ty: Ty<'tcx>, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires every target field to have a corresponding source field" +)] +pub(crate) struct CoerceSharedMissingField { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires source and target structs to use the same field style" +)] +pub(crate) struct CoerceSharedFieldStyleMismatch { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag( + "implementing `{$trait_name}` requires all source and target fields to be accessible from the impl" +)] +pub(crate) struct CoerceSharedInaccessibleField { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] #[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)] pub(crate) struct CoerceUnsizedNonStruct { diff --git a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs index d9a1029211136..4f14ed56d07bc 100644 --- a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs +++ b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs @@ -1,6 +1,22 @@ #![allow(dead_code)] +use std::marker::PhantomData; + #[derive(Clone, Copy)] pub struct ForeignRef<'a> { value: &'a i32, } + +// SAFETY invariant: the pointer is valid as `&'a i32`. +#[derive(Clone, Copy)] +pub struct ForeignPtrRef<'a>((*const i32, PhantomData<&'a ()>)); + +impl<'a> ForeignPtrRef<'a> { + pub fn new(r: &'a i32) -> Self { + ForeignPtrRef((r, PhantomData)) + } + + pub fn to_ref(self) -> &'a i32 { + unsafe { &*self.0.0 } + } +} diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.rs b/tests/ui/reborrow/coerce-shared-associated-type-field.rs index df744e9442dd8..e39f04f6ce2c0 100644 --- a/tests/ui/reborrow/coerce-shared-associated-type-field.rs +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow)] #![allow(dead_code)] @@ -25,6 +27,5 @@ struct MyRef<'a> { impl Reborrow for MyMut<'_> {} impl<'a> CoerceShared> for MyMut<'a> {} -//~^ ERROR fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs index 3b6e9e25d8bb4..b4246bfefb17d 100644 --- a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow, decl_macro)] #![allow(incomplete_features)] @@ -18,7 +20,6 @@ macro my_macro($field:ident) { impl Reborrow for MyMut<'_> {} impl<'a> CoerceShared> for MyMut<'a> {} - //~^ ERROR } my_macro!(field); diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs index d4558dd35d775..2fbd720da0ea9 100644 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -12,10 +14,10 @@ impl Reborrow for MyMut<'_> {} #[derive(Copy, Clone)] struct MyRef<'a> { x: &'a (), + //~^ ERROR y: &'static (), } impl<'a> CoerceShared> for MyMut<'a> {} -//~^ ERROR fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr index bce87c68eb912..48f3716d67a8a 100644 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr @@ -1,8 +1,10 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-field-lifetime-swap.rs:18:10 +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-field-lifetime-swap.rs:16:5 | -LL | impl<'a> CoerceShared> for MyMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | x: &'static (), + | -------------- source field `x` has type `&'static ()` +... +LL | x: &'a (), + | ^^^^^^^^^ target field `x` has type `&'a ()` error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs index b2104efb6028d..f2856af355935 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.rs +++ b/tests/ui/reborrow/coerce-shared-field-relations.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -24,6 +26,7 @@ impl<'a, T> Reborrow for RenamedMut<'a, T> {} #[derive(Clone, Copy)] struct RenamedRef<'a, T> { target: &'a T, + //~^ ERROR } impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} @@ -37,11 +40,11 @@ impl<'a, T> Reborrow for BadMut<'a, T> {} #[derive(Clone, Copy)] struct BadRef<'a, T> { value: &'a u32, + //~^ ERROR _marker: std::marker::PhantomData, } impl<'a, T> CoerceShared> for BadMut<'a, T> {} -//~^ ERROR fn good(_value: CustomRef<'_, u32>) {} diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr index 033a29e1e554e..7be8b3c6c8208 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.stderr +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -1,9 +1,16 @@ -error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied - --> $DIR/coerce-shared-field-relations.rs:43:1 +error: implementing `CoerceShared` requires every target field to have a corresponding source field + --> $DIR/coerce-shared-field-relations.rs:28:5 | -LL | impl<'a, T> CoerceShared> for BadMut<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut T` +LL | target: &'a T, + | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-field-relations.rs:42:5 + | +LL | value: &'a mut T, + | ---------------- source field `value` has type `&'a mut T` +... +LL | value: &'a u32, + | ^^^^^^^^^^^^^^ target field `value` has type `&'a u32` -For more information about this error, try `rustc --explain E0277`. +error: aborting due to 2 previous errors diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs index 3adda6733e16f..e01dcf5d95487 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs @@ -1,4 +1,4 @@ -//@ check-pass +//@ normalize-stderr: "\n\n\z" -> "\n" //@ aux-build: reborrow_foreign_private.rs @@ -16,5 +16,6 @@ struct LocalMut<'a> { impl<'a> Reborrow for LocalMut<'a> {} impl<'a> CoerceShared> for LocalMut<'a> {} +//~^ ERROR fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr new file mode 100644 index 0000000000000..f03cc89782fff --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr @@ -0,0 +1,7 @@ +error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl + --> $DIR/coerce-shared-foreign-private-field.rs:18:1 + | +LL | impl<'a> CoerceShared> for LocalMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs index 4a70fd49e38ff..d45df376f5b8f 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs @@ -1,22 +1,24 @@ -//@ check-pass +//@ normalize-stderr: "\n\n\z" -> "\n" -#![feature(reborrow)] +//@ aux-build: reborrow_foreign_private.rs -use std::marker::{CoerceShared, PhantomData, Reborrow}; +#![feature(reborrow)] -mod foreign_ptr { - use std::marker::PhantomData; +extern crate reborrow_foreign_private; - #[derive(Clone, Copy)] - pub struct ForeignPtrRef<'a>(*const i32, PhantomData<&'a ()>); -} - -use foreign_ptr::ForeignPtrRef; +use reborrow_foreign_private::ForeignPtrRef; +use std::marker::{CoerceShared, PhantomData, Reborrow}; +use std::ptr; -struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>); +struct LocalPtrMut<'a>((*const i32, PhantomData<&'a ()>)); impl<'a> Reborrow for LocalPtrMut<'a> {} impl<'a> CoerceShared> for LocalPtrMut<'a> {} +//~^ ERROR -fn main() {} +fn main() { + let local = LocalPtrMut((ptr::null(), PhantomData)); + let foreign: ForeignPtrRef<'_> = local; + let _ = foreign.to_ref(); +} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr new file mode 100644 index 0000000000000..6d9317e38a2de --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr @@ -0,0 +1,7 @@ +error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl + --> $DIR/coerce-shared-foreign-private-tuple-field.rs:17:1 + | +LL | impl<'a> CoerceShared> for LocalPtrMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs index 90bb4be4c1894..3ca3a315704a9 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs @@ -1,8 +1,5 @@ #![feature(reborrow)] -// The impl is accepted, but using it to coerce a local marker into a `'static` -// target still requires the local borrow to live for `'static`. - use std::marker::{CoerceShared, PhantomData, Reborrow}; struct CustomMarker<'a>(PhantomData<&'a ()>); @@ -13,6 +10,7 @@ impl<'a> Reborrow for CustomMarker<'a> {} struct StaticMarkerRef<'a>(PhantomData<&'a ()>); impl<'a> CoerceShared> for CustomMarker<'a> {} +//~^ ERROR fn method(_a: StaticMarkerRef<'static>) {} diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr index 337e4b6938944..8073e71e56319 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -1,5 +1,11 @@ +error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument + --> $DIR/coerce-shared-lifetime-mismatch.rs:12:10 + | +LL | impl<'a> CoerceShared> for CustomMarker<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0597]: `a` does not live long enough - --> $DIR/coerce-shared-lifetime-mismatch.rs:21:12 + --> $DIR/coerce-shared-lifetime-mismatch.rs:19:12 | LL | let a = CustomMarker(PhantomData); | - binding `a` declared here @@ -12,6 +18,6 @@ LL | LL | } | - `a` dropped here while still borrowed -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs index c528fb85340ac..131f69ae5bbff 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.rs +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -12,9 +14,9 @@ impl<'a, T> Reborrow for MissingSourceMut<'a, T> {} struct MissingSourceRef<'a, T> { value: &'a T, len: usize, + //~^ ERROR } impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} -//~^ ERROR fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr index 15146341bc56c..d3a45cad31072 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr @@ -1,8 +1,7 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-missing-target-field.rs:17:13 +error: implementing `CoerceShared` requires every target field to have a corresponding source field + --> $DIR/coerce-shared-missing-target-field.rs:16:5 | -LL | impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | len: usize, + | ^^^^^^^^^^ error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs index 9d87ff0bb938a..8e0629fe6cd69 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -25,7 +27,6 @@ struct AliasRef<'a> { } impl<'a> CoerceShared> for AliasMut<'a> {} -//~^ ERROR struct InnerLifetimeMut<'a> { value: &'a mut &'static (), @@ -36,6 +37,7 @@ impl Reborrow for InnerLifetimeMut<'_> {} #[derive(Copy, Clone)] struct InnerLifetimeRef<'a> { value: &'a &'a (), + //~^ ERROR } impl<'a> CoerceShared> for InnerLifetimeMut<'a> {} diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr index 63a96d65e176d..95333bdd446a5 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr @@ -1,9 +1,10 @@ -error[E0277]: the trait bound `&'a mut u32: CoerceShared<&'a u32>` is not satisfied - --> $DIR/coerce-shared-mut-ref-field-validation.rs:27:1 +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-mut-ref-field-validation.rs:39:5 | -LL | impl<'a> CoerceShared> for AliasMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut u32` +LL | value: &'a mut &'static (), + | -------------------------- source field `value` has type `&'a mut &'static ()` +... +LL | value: &'a &'a (), + | ^^^^^^^^^^^^^^^^^ target field `value` has type `&'a &'a ()` error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs index 2dc818a7015b2..a7cfd74d137c7 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.rs +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "\n\n\z" -> "\n" + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; @@ -12,10 +14,10 @@ impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {} #[derive(Clone, Copy)] struct GenericRef<'a, T, U> { value: &'a U, + //~^ ERROR marker: PhantomData, } impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} -//~^ ERROR fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr index d0031a7c8a99e..6f5595e09e632 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -1,9 +1,10 @@ -error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied - --> $DIR/coerce-shared-wrong-generic.rs:18:1 +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-wrong-generic.rs:16:5 | -LL | impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a U>` is not implemented for `&'a mut T` +LL | value: &'a mut T, + | ---------------- source field `value` has type `&'a mut T` +... +LL | value: &'a U, + | ^^^^^^^^^^^^ target field `value` has type `&'a U` error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs b/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs new file mode 100644 index 0000000000000..4d68327d22bfe --- /dev/null +++ b/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs @@ -0,0 +1,25 @@ +//@ run-pass +//@ edition: 2024 + +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); + +#[derive(Clone, Copy, Debug)] +struct CustomMarkerRef<'a>(PhantomData<&'a ()>); + +impl<'a> Reborrow for CustomMarker<'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); +} From 7ca3d1c5f79b8b2a0d024fae7005cc900977c97f Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 8 Jun 2026 14:31:14 +0200 Subject: [PATCH 2/7] Address CoerceShared WF check review --- .../src/coherence/builtin.rs | 96 ++++++++++--------- .../auxiliary/reborrow_foreign_private.rs | 22 ----- .../coerce-shared-associated-type-field.rs | 31 ------ .../coerce-shared-decl-macro-hygiene.rs | 27 ------ .../coerce-shared-field-lifetime-swap.rs | 23 ----- .../coerce-shared-field-lifetime-swap.stderr | 10 -- .../reborrow/coerce-shared-field-relations.rs | 54 ----------- .../coerce-shared-field-relations.stderr | 16 ---- .../coerce-shared-foreign-private-field.rs | 21 ---- ...coerce-shared-foreign-private-field.stderr | 7 -- ...erce-shared-foreign-private-tuple-field.rs | 24 ----- ...-shared-foreign-private-tuple-field.stderr | 7 -- .../coerce-shared-lifetime-mismatch.rs | 21 ---- .../coerce-shared-lifetime-mismatch.stderr | 23 ----- .../coerce-shared-missing-target-field.rs | 22 ----- .../coerce-shared-missing-target-field.stderr | 7 -- .../coerce-shared-mut-ref-field-validation.rs | 45 --------- ...rce-shared-mut-ref-field-validation.stderr | 10 -- .../reborrow/coerce-shared-wrong-generic.rs | 23 ----- .../coerce-shared-wrong-generic.stderr | 10 -- ...er-coerce-shared-corrected-issue-156309.rs | 25 ----- 21 files changed, 50 insertions(+), 474 deletions(-) delete mode 100644 tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs delete mode 100644 tests/ui/reborrow/coerce-shared-associated-type-field.rs delete mode 100644 tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs delete mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs delete mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-field-relations.rs delete mode 100644 tests/ui/reborrow/coerce-shared-field-relations.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.rs delete mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs delete mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs delete mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.rs delete mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs delete mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.rs delete mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.stderr delete mode 100644 tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3c97fae66ab22..3503e23cd1a7e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -506,7 +506,6 @@ pub(crate) fn reborrow_info<'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"; @@ -536,7 +535,7 @@ pub(crate) fn reborrow_info<'tcx>( }; let lifetimes_count = generic_lifetime_params_count(args); - let data_fields = wf_data_fields(tcx, def, args); + let data_fields = collect_reborrow_data_fields(tcx, def, args); if lifetimes_count != 1 { let item = tcx.hir_expect_item(impl_did); @@ -557,7 +556,6 @@ pub(crate) fn reborrow_info<'tcx>( for field in data_fields { if assert_field_type_is_reborrow( tcx, - &infcx, reborrow_trait, impl_did, param_env, @@ -579,7 +577,6 @@ pub(crate) fn reborrow_info<'tcx>( fn assert_field_type_is_reborrow<'tcx>( tcx: TyCtxt<'tcx>, - infcx: &InferCtxt<'tcx>, reborrow_trait: DefId, impl_did: LocalDefId, param_env: ty::ParamEnv<'tcx>, @@ -590,7 +587,8 @@ fn assert_field_type_is_reborrow<'tcx>( // Mutable references are Reborrow but not really. return Ok(()); } - let ocx = ObligationCtxt::new_with_diagnostics(infcx); + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + 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])); @@ -707,24 +705,24 @@ fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize { args.iter().filter(|arg| arg.as_region().is_some()).count() } -#[derive(Clone, Copy, Debug)] -struct WfField<'tcx> { +#[derive(Clone, Copy)] +struct ReborrowDataField<'tcx> { ident: Ident, name: Symbol, ty: Ty<'tcx>, span: Span, } -#[derive(Clone, Copy, Debug)] -struct WfFieldPair<'tcx> { - source: WfField<'tcx>, - target: WfField<'tcx>, +#[derive(Clone, Copy)] +struct CoerceSharedFieldPair<'tcx> { + source: ReborrowDataField<'tcx>, + target: ReborrowDataField<'tcx>, } -#[derive(Clone, Copy, Debug)] -enum WfFieldPairError<'tcx> { +#[derive(Clone, Copy)] +enum CoerceSharedFieldPairError<'tcx> { FieldStyleMismatch, - MissingSourceField { target: WfField<'tcx> }, + MissingSourceField { target: ReborrowDataField<'tcx> }, } fn single_region_arg<'tcx>(args: ty::GenericArgsRef<'tcx>) -> Option> { @@ -733,17 +731,17 @@ fn single_region_arg<'tcx>(args: ty::GenericArgsRef<'tcx>) -> Option( +fn collect_reborrow_data_fields<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, args: ty::GenericArgsRef<'tcx>, -) -> Vec> { +) -> Vec> { def.non_enum_variant() .fields .iter() .filter_map(|field| { let ty = field.ty(tcx, args).skip_norm_wip(); - (!ty.is_phantom_data()).then_some(WfField { + (!ty.is_phantom_data()).then_some(ReborrowDataField { ident: field.ident(tcx), name: field.name, ty, @@ -753,33 +751,34 @@ fn wf_data_fields<'tcx>( .collect() } -// This is a coherence/WF check only. It verifies that the impl describes a -// structurally valid field relation; later runtime lowering still starts from -// the single reborrow adjustment and is not modeled here. -fn coerce_shared_wf_field_pairs<'tcx>( +// This is a coherence/WF check only. It verifies that the CoerceShared impl +// describes a structurally valid field-wise relation. Runtime lowering of the +// operation is not modeled here. +fn collect_coerce_shared_field_pairs<'tcx>( tcx: TyCtxt<'tcx>, source_def: ty::AdtDef<'tcx>, source_args: ty::GenericArgsRef<'tcx>, target_def: ty::AdtDef<'tcx>, target_args: ty::GenericArgsRef<'tcx>, -) -> Result>, WfFieldPairError<'tcx>> { +) -> Result>, CoerceSharedFieldPairError<'tcx>> { let source_variant = source_def.non_enum_variant(); let target_variant = target_def.non_enum_variant(); if source_variant.ctor_kind() != target_variant.ctor_kind() { - return Err(WfFieldPairError::FieldStyleMismatch); + return Err(CoerceSharedFieldPairError::FieldStyleMismatch); } let by_position = matches!(target_variant.ctor_kind(), Some(CtorKind::Fn)); - let source_fields = wf_data_fields(tcx, source_def, source_args); - let target_fields = wf_data_fields(tcx, target_def, target_args); + let source_fields = collect_reborrow_data_fields(tcx, source_def, source_args); + let target_fields = collect_reborrow_data_fields(tcx, target_def, target_args); if by_position { target_fields .into_iter() .zip(source_fields.into_iter().map(Some).chain(std::iter::repeat(None))) .map(|(target, source)| { - let source = source.ok_or(WfFieldPairError::MissingSourceField { target })?; - Ok(WfFieldPair { source, target }) + let source = + source.ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?; + Ok(CoerceSharedFieldPair { source, target }) }) .collect() } else { @@ -792,9 +791,9 @@ fn coerce_shared_wf_field_pairs<'tcx>( .find(|source| { tcx.hygienic_eq(target.ident, source.ident, source_variant.def_id) }) - .ok_or(WfFieldPairError::MissingSourceField { target })?; + .ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?; - Ok(WfFieldPair { source, target }) + Ok(CoerceSharedFieldPair { source, target }) }) .collect() } @@ -838,21 +837,26 @@ fn validate_coerce_shared_fields<'tcx>( target_def: ty::AdtDef<'tcx>, target_args: ty::GenericArgsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let field_pairs = - match coerce_shared_wf_field_pairs(tcx, source_def, source_args, target_def, target_args) { - Ok(field_pairs) => field_pairs, - Err(WfFieldPairError::FieldStyleMismatch) => { - return Err(tcx - .dcx() - .emit_err(diagnostics::CoerceSharedFieldStyleMismatch { span, trait_name })); - } - Err(WfFieldPairError::MissingSourceField { target }) => { - return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMissingField { - span: target.span, - trait_name, - })); - } - }; + let field_pairs = match collect_coerce_shared_field_pairs( + tcx, + source_def, + source_args, + target_def, + target_args, + ) { + Ok(field_pairs) => field_pairs, + Err(CoerceSharedFieldPairError::FieldStyleMismatch) => { + return Err(tcx + .dcx() + .emit_err(diagnostics::CoerceSharedFieldStyleMismatch { span, trait_name })); + } + Err(CoerceSharedFieldPairError::MissingSourceField { target }) => { + return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMissingField { + span: target.span, + trait_name, + })); + } + }; for field_pair in field_pairs { validate_coerce_shared_field( @@ -877,8 +881,8 @@ fn validate_coerce_shared_field<'tcx>( coerce_shared_trait: DefId, trait_name: &'static str, span: Span, - source: WfField<'tcx>, - target: WfField<'tcx>, + source: ReborrowDataField<'tcx>, + target: ReborrowDataField<'tcx>, ) -> Result<(), ErrorGuaranteed> { if matches!( (source.ty.kind(), target.ty.kind()), diff --git a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs deleted file mode 100644 index 4f14ed56d07bc..0000000000000 --- a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![allow(dead_code)] - -use std::marker::PhantomData; - -#[derive(Clone, Copy)] -pub struct ForeignRef<'a> { - value: &'a i32, -} - -// SAFETY invariant: the pointer is valid as `&'a i32`. -#[derive(Clone, Copy)] -pub struct ForeignPtrRef<'a>((*const i32, PhantomData<&'a ()>)); - -impl<'a> ForeignPtrRef<'a> { - pub fn new(r: &'a i32) -> Self { - ForeignPtrRef((r, PhantomData)) - } - - pub fn to_ref(self) -> &'a i32 { - unsafe { &*self.0.0 } - } -} diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.rs b/tests/ui/reborrow/coerce-shared-associated-type-field.rs deleted file mode 100644 index e39f04f6ce2c0..0000000000000 --- a/tests/ui/reborrow/coerce-shared-associated-type-field.rs +++ /dev/null @@ -1,31 +0,0 @@ -//@ check-pass - -#![feature(reborrow)] -#![allow(dead_code)] - -use std::marker::{CoerceShared, Reborrow}; - -trait Trait { - type Assoc; -} - -impl Trait for i32 { - type Assoc = i64; -} - -struct MyMut<'a> { - x: &'a (), - y: i64, -} - -#[derive(Copy, Clone)] -struct MyRef<'a> { - x: &'a (), - y: ::Assoc, -} - -impl Reborrow for MyMut<'_> {} - -impl<'a> CoerceShared> for MyMut<'a> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs deleted file mode 100644 index b4246bfefb17d..0000000000000 --- a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ check-pass - -#![feature(reborrow, decl_macro)] -#![allow(incomplete_features)] - -use std::marker::{CoerceShared, Reborrow}; - -macro my_macro($field:ident) { - pub struct MyMut<'a> { - $field: &'a i32, - field: &'a i64, - } - - #[derive(Clone, Copy)] - pub struct MyRef<'a> { - $field: &'a i32, - field: &'a i64, - } - - impl Reborrow for MyMut<'_> {} - - impl<'a> CoerceShared> for MyMut<'a> {} -} - -my_macro!(field); - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs deleted file mode 100644 index 2fbd720da0ea9..0000000000000 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -#![feature(reborrow)] - -use std::marker::{CoerceShared, Reborrow}; - -struct MyMut<'a> { - x: &'static (), - y: &'a (), -} - -impl Reborrow for MyMut<'_> {} - -#[derive(Copy, Clone)] -struct MyRef<'a> { - x: &'a (), - //~^ ERROR - y: &'static (), -} - -impl<'a> CoerceShared> for MyMut<'a> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr deleted file mode 100644 index 48f3716d67a8a..0000000000000 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-field-lifetime-swap.rs:16:5 - | -LL | x: &'static (), - | -------------- source field `x` has type `&'static ()` -... -LL | x: &'a (), - | ^^^^^^^^^ target field `x` has type `&'a ()` - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs deleted file mode 100644 index f2856af355935..0000000000000 --- a/tests/ui/reborrow/coerce-shared-field-relations.rs +++ /dev/null @@ -1,54 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -#![feature(reborrow)] - -use std::marker::{CoerceShared, Reborrow}; - -struct CustomMut<'a, T> { - value: &'a mut T, -} - -impl<'a, T> Reborrow for CustomMut<'a, T> {} - -#[derive(Clone, Copy)] -struct CustomRef<'a, T> { - value: &'a T, -} - -impl<'a, T> CoerceShared> for CustomMut<'a, T> {} - -struct RenamedMut<'a, T> { - source: &'a mut T, -} - -impl<'a, T> Reborrow for RenamedMut<'a, T> {} - -#[derive(Clone, Copy)] -struct RenamedRef<'a, T> { - target: &'a T, - //~^ ERROR -} - -impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} - -struct BadMut<'a, T> { - value: &'a mut T, -} - -impl<'a, T> Reborrow for BadMut<'a, T> {} - -#[derive(Clone, Copy)] -struct BadRef<'a, T> { - value: &'a u32, - //~^ ERROR - _marker: std::marker::PhantomData, -} - -impl<'a, T> CoerceShared> for BadMut<'a, T> {} - -fn good(_value: CustomRef<'_, u32>) {} - -fn main() { - let mut value = 1; - good(CustomMut { value: &mut value }); -} diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr deleted file mode 100644 index 7be8b3c6c8208..0000000000000 --- a/tests/ui/reborrow/coerce-shared-field-relations.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: implementing `CoerceShared` requires every target field to have a corresponding source field - --> $DIR/coerce-shared-field-relations.rs:28:5 - | -LL | target: &'a T, - | ^^^^^^^^^^^^^ - -error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-field-relations.rs:42:5 - | -LL | value: &'a mut T, - | ---------------- source field `value` has type `&'a mut T` -... -LL | value: &'a u32, - | ^^^^^^^^^^^^^^ target field `value` has type `&'a u32` - -error: aborting due to 2 previous errors diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs deleted file mode 100644 index e01dcf5d95487..0000000000000 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -//@ aux-build: reborrow_foreign_private.rs - -#![feature(reborrow)] - -extern crate reborrow_foreign_private; - -use reborrow_foreign_private::ForeignRef; -use std::marker::{CoerceShared, Reborrow}; - -struct LocalMut<'a> { - value: &'a mut i32, -} - -impl<'a> Reborrow for LocalMut<'a> {} - -impl<'a> CoerceShared> for LocalMut<'a> {} -//~^ ERROR - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr deleted file mode 100644 index f03cc89782fff..0000000000000 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl - --> $DIR/coerce-shared-foreign-private-field.rs:18:1 - | -LL | impl<'a> CoerceShared> for LocalMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs deleted file mode 100644 index d45df376f5b8f..0000000000000 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -//@ aux-build: reborrow_foreign_private.rs - -#![feature(reborrow)] - -extern crate reborrow_foreign_private; - -use reborrow_foreign_private::ForeignPtrRef; -use std::marker::{CoerceShared, PhantomData, Reborrow}; -use std::ptr; - -struct LocalPtrMut<'a>((*const i32, PhantomData<&'a ()>)); - -impl<'a> Reborrow for LocalPtrMut<'a> {} - -impl<'a> CoerceShared> for LocalPtrMut<'a> {} -//~^ ERROR - -fn main() { - let local = LocalPtrMut((ptr::null(), PhantomData)); - let foreign: ForeignPtrRef<'_> = local; - let _ = foreign.to_ref(); -} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr deleted file mode 100644 index 6d9317e38a2de..0000000000000 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl - --> $DIR/coerce-shared-foreign-private-tuple-field.rs:17:1 - | -LL | impl<'a> CoerceShared> for LocalPtrMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs deleted file mode 100644 index 3ca3a315704a9..0000000000000 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(reborrow)] - -use std::marker::{CoerceShared, PhantomData, Reborrow}; - -struct CustomMarker<'a>(PhantomData<&'a ()>); - -impl<'a> Reborrow for CustomMarker<'a> {} - -#[derive(Clone, Copy)] -struct StaticMarkerRef<'a>(PhantomData<&'a ()>); - -impl<'a> CoerceShared> for CustomMarker<'a> {} -//~^ ERROR - -fn method(_a: StaticMarkerRef<'static>) {} - -fn main() { - let a = CustomMarker(PhantomData); - method(a); - //~^ ERROR -} diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr deleted file mode 100644 index 8073e71e56319..0000000000000 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument - --> $DIR/coerce-shared-lifetime-mismatch.rs:12:10 - | -LL | impl<'a> CoerceShared> for CustomMarker<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0597]: `a` does not live long enough - --> $DIR/coerce-shared-lifetime-mismatch.rs:19:12 - | -LL | let a = CustomMarker(PhantomData); - | - binding `a` declared here -LL | method(a); - | -------^- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` -LL | -LL | } - | - `a` dropped here while still borrowed - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs deleted file mode 100644 index 131f69ae5bbff..0000000000000 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -#![feature(reborrow)] - -use std::marker::{CoerceShared, Reborrow}; - -struct MissingSourceMut<'a, T> { - value: &'a mut T, -} - -impl<'a, T> Reborrow for MissingSourceMut<'a, T> {} - -#[derive(Clone, Copy)] -struct MissingSourceRef<'a, T> { - value: &'a T, - len: usize, - //~^ ERROR -} - -impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr deleted file mode 100644 index d3a45cad31072..0000000000000 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: implementing `CoerceShared` requires every target field to have a corresponding source field - --> $DIR/coerce-shared-missing-target-field.rs:16:5 - | -LL | len: usize, - | ^^^^^^^^^^ - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs deleted file mode 100644 index 8e0629fe6cd69..0000000000000 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs +++ /dev/null @@ -1,45 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -#![feature(reborrow)] - -use std::marker::{CoerceShared, Reborrow}; - -trait Trait { - type Assoc; -} - -impl Trait for i32 { - type Assoc = u32; -} - -type AliasMutField<'a> = &'a mut ::Assoc; -type AliasRefField<'a> = &'a u32; - -struct AliasMut<'a> { - value: AliasMutField<'a>, -} - -impl Reborrow for AliasMut<'_> {} - -#[derive(Copy, Clone)] -struct AliasRef<'a> { - value: AliasRefField<'a>, -} - -impl<'a> CoerceShared> for AliasMut<'a> {} - -struct InnerLifetimeMut<'a> { - value: &'a mut &'static (), -} - -impl Reborrow for InnerLifetimeMut<'_> {} - -#[derive(Copy, Clone)] -struct InnerLifetimeRef<'a> { - value: &'a &'a (), - //~^ ERROR -} - -impl<'a> CoerceShared> for InnerLifetimeMut<'a> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr deleted file mode 100644 index 95333bdd446a5..0000000000000 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-mut-ref-field-validation.rs:39:5 - | -LL | value: &'a mut &'static (), - | -------------------------- source field `value` has type `&'a mut &'static ()` -... -LL | value: &'a &'a (), - | ^^^^^^^^^^^^^^^^^ target field `value` has type `&'a &'a ()` - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs deleted file mode 100644 index a7cfd74d137c7..0000000000000 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ normalize-stderr: "\n\n\z" -> "\n" - -#![feature(reborrow)] - -use std::marker::{CoerceShared, PhantomData, Reborrow}; - -struct GenericMut<'a, T, U> { - value: &'a mut T, - marker: PhantomData, -} - -impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {} - -#[derive(Clone, Copy)] -struct GenericRef<'a, T, U> { - value: &'a U, - //~^ ERROR - marker: PhantomData, -} - -impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr deleted file mode 100644 index 6f5595e09e632..0000000000000 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-wrong-generic.rs:16:5 - | -LL | value: &'a mut T, - | ---------------- source field `value` has type `&'a mut T` -... -LL | value: &'a U, - | ^^^^^^^^^^^^ target field `value` has type `&'a U` - -error: aborting due to 1 previous error diff --git a/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs b/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs deleted file mode 100644 index 4d68327d22bfe..0000000000000 --- a/tests/ui/reborrow/marker-coerce-shared-corrected-issue-156309.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ run-pass -//@ edition: 2024 - -#![feature(reborrow)] - -use std::marker::{CoerceShared, PhantomData, Reborrow}; - -struct CustomMarker<'a>(PhantomData<&'a ()>); - -#[derive(Clone, Copy, Debug)] -struct CustomMarkerRef<'a>(PhantomData<&'a ()>); - -impl<'a> Reborrow for CustomMarker<'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); -} From 71b8d8620fa9f08e5192b268a353e6d542defee5 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 8 Jun 2026 15:27:40 +0200 Subject: [PATCH 3/7] Avoid copying CoerceShared source fields before lookup --- compiler/rustc_hir_analysis/src/coherence/builtin.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3503e23cd1a7e..53adb2bde435f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -787,13 +787,12 @@ fn collect_coerce_shared_field_pairs<'tcx>( .map(|target| { let source = source_fields .iter() - .copied() .find(|source| { tcx.hygienic_eq(target.ident, source.ident, source_variant.def_id) }) .ok_or(CoerceSharedFieldPairError::MissingSourceField { target })?; - Ok(CoerceSharedFieldPair { source, target }) + Ok(CoerceSharedFieldPair { source: *source, target }) }) .collect() } From 28638a7663c0ce9cde3448b55dd2ca126c508b63 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 12 Jun 2026 12:01:43 +0200 Subject: [PATCH 4/7] Address remaining CoerceShared WF review comments --- .../src/coherence/builtin.rs | 31 ++++++++++--------- .../coerce_shared_lifetime_mismatch.rs | 19 ++++++++++++ .../coerce_shared_lifetime_mismatch.stderr | 11 +++++++ 3 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs create mode 100644 tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 53adb2bde435f..d5e9e15caa024 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -506,6 +506,7 @@ pub(crate) fn reborrow_info<'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"; @@ -556,6 +557,7 @@ pub(crate) fn reborrow_info<'tcx>( for field in data_fields { if assert_field_type_is_reborrow( tcx, + &infcx, reborrow_trait, impl_did, param_env, @@ -569,7 +571,7 @@ pub(crate) fn reborrow_info<'tcx>( } // Field does not implement Reborrow: it must be Copy. - assert_field_type_is_copy(tcx, impl_did, param_env, field.ty, field.span)?; + assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field.ty, field.span)?; } Ok(()) @@ -577,6 +579,7 @@ pub(crate) fn reborrow_info<'tcx>( fn assert_field_type_is_reborrow<'tcx>( tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, reborrow_trait: DefId, impl_did: LocalDefId, param_env: ty::ParamEnv<'tcx>, @@ -587,8 +590,7 @@ fn assert_field_type_is_reborrow<'tcx>( // Mutable references are Reborrow but not really. return Ok(()); } - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + 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])); @@ -857,9 +859,11 @@ fn validate_coerce_shared_fields<'tcx>( } }; + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); for field_pair in field_pairs { validate_coerce_shared_field( tcx, + &infcx, impl_did, param_env, coerce_shared_trait, @@ -875,6 +879,7 @@ fn validate_coerce_shared_fields<'tcx>( fn validate_coerce_shared_field<'tcx>( tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, impl_did: LocalDefId, param_env: ty::ParamEnv<'tcx>, coerce_shared_trait: DefId, @@ -909,11 +914,10 @@ fn validate_coerce_shared_field<'tcx>( source.span, FieldRelation::Equal, ) { - return assert_field_type_is_copy(tcx, impl_did, param_env, source.ty, source.span); + return assert_field_type_is_copy(tcx, infcx, impl_did, param_env, source.ty, source.span); } - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let cause = traits::ObligationCause::misc(span, impl_did); ocx.register_obligation(Obligation::new( tcx, @@ -981,12 +985,9 @@ fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>( else { return false; }; - infcx.sub_regions( - SubregionOrigin::RelateObjectBound(span), - target_region, - source_region, - ty::VisibleForLeakCheck::Yes, - ); + if source_region != target_region { + return false; + } (source_referent_ty, target_referent_ty) } }; @@ -1008,14 +1009,14 @@ fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>( 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 infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let copy_trait = tcx.require_lang_item(LangItem::Copy, span); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + 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])); @@ -1025,7 +1026,7 @@ fn assert_field_type_is_copy<'tcx>( if !errors.is_empty() { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } else { - ocx.resolve_regions_and_report_errors(impl_did, param_env, []) + Ok(()) } } diff --git a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs new file mode 100644 index 0000000000000..79fd0d04370be --- /dev/null +++ b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs @@ -0,0 +1,19 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct Source<'a> { + data: &'static mut (), + marker: PhantomData<&'a ()>, +} + +impl<'a> Reborrow for Source<'a> {} + +struct Target<'a> { + data: &'a (), + //~^ ERROR implementing `CoerceShared` requires corresponding fields to match +} + +impl<'a> CoerceShared> for Source<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr new file mode 100644 index 0000000000000..689f48d474b7b --- /dev/null +++ b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr @@ -0,0 +1,11 @@ +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce_shared_lifetime_mismatch.rs:13:5 + | +LL | data: &'static mut (), + | --------------------- source field `data` has type `&'static mut ()` +... +LL | data: &'a (), + | ^^^^^^^^^^^^ target field `data` has type `&'a ()` + +error: aborting due to 1 previous error + From d6276e050f28494f92247c2cecb262a309ddb9d9 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 12 Jun 2026 13:11:29 +0200 Subject: [PATCH 5/7] tiny fix --- compiler/rustc_hir_analysis/src/coherence/builtin.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index d5e9e15caa024..ff1e504cdd5f1 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -676,6 +676,7 @@ pub(crate) fn coerce_shared_info<'tcx>( validate_coerce_shared_fields( tcx, + &infcx, impl_did, param_env, coerce_shared_trait, @@ -828,6 +829,7 @@ fn validate_reborrow_field_access( fn validate_coerce_shared_fields<'tcx>( tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, impl_did: LocalDefId, param_env: ty::ParamEnv<'tcx>, coerce_shared_trait: DefId, @@ -859,11 +861,10 @@ fn validate_coerce_shared_fields<'tcx>( } }; - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); for field_pair in field_pairs { validate_coerce_shared_field( tcx, - &infcx, + infcx, impl_did, param_env, coerce_shared_trait, From d5e890a16118f0caf3a14651ad1cf3dc26c5c38e Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 15 Jun 2026 11:25:29 +0200 Subject: [PATCH 6/7] Rebase + Test Fixes --- .../coerce-shared-associated-type-field.stderr | 8 -------- .../coerce-shared-decl-macro-hygiene.stderr | 13 ------------- tests/ui/reborrow/coerce-shared-generics.rs | 3 ++- tests/ui/reborrow/coerce-shared-generics.stderr | 8 -------- ...erce-shared-omitted-reborrow-field-after-dead.rs | 3 ++- ...-shared-omitted-reborrow-field-after-dead.stderr | 8 -------- .../coerce-shared-omitted-reborrow-field-locked.rs | 1 - ...erce-shared-omitted-reborrow-field-locked.stderr | 10 ++-------- .../coerce-shared-omitted-reborrow-field.rs | 3 ++- .../coerce-shared-omitted-reborrow-field.stderr | 8 -------- tests/ui/reborrow/coerce-shared-reordered-field.rs | 3 ++- .../reborrow/coerce-shared-reordered-field.stderr | 8 -------- 12 files changed, 10 insertions(+), 66 deletions(-) delete mode 100644 tests/ui/reborrow/coerce-shared-associated-type-field.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-generics.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr delete mode 100644 tests/ui/reborrow/coerce-shared-reordered-field.stderr diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.stderr b/tests/ui/reborrow/coerce-shared-associated-type-field.stderr deleted file mode 100644 index d533fa2eb8748..0000000000000 --- a/tests/ui/reborrow/coerce-shared-associated-type-field.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-associated-type-field.rs:27:10 - | -LL | impl<'a> CoerceShared> for MyMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr deleted file mode 100644 index 0b0b7bf048591..0000000000000 --- a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:14 - | -LL | impl<'a> CoerceShared> for MyMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | my_macro!(field); - | ---------------- in this macro invocation - | - = note: this error originates in the macro `my_macro` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-generics.rs b/tests/ui/reborrow/coerce-shared-generics.rs index 9edab02835761..cff41b373236d 100644 --- a/tests/ui/reborrow/coerce-shared-generics.rs +++ b/tests/ui/reborrow/coerce-shared-generics.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow)] #![allow(dead_code)] @@ -24,7 +26,6 @@ impl<'a, T, U: Copy, const N: usize> Clone for BufferRef<'a, T, U, N> { impl<'a, T, U: Copy, const N: usize> Copy for BufferRef<'a, T, U, N> {} impl<'a, T, U: Copy, const N: usize> CoerceShared> -//~^ ERROR for BufferMut<'a, T, U, N> { } diff --git a/tests/ui/reborrow/coerce-shared-generics.stderr b/tests/ui/reborrow/coerce-shared-generics.stderr deleted file mode 100644 index 72efdd475fc10..0000000000000 --- a/tests/ui/reborrow/coerce-shared-generics.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-generics.rs:26:38 - | -LL | impl<'a, T, U: Copy, const N: usize> CoerceShared> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs index 4f066079c749b..d74d1f3fa7be5 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow)] #![allow(dead_code)] @@ -29,7 +31,6 @@ impl<'a, T> Clone for OmitRef<'a, T> { impl<'a, T> Copy for OmitRef<'a, T> {} impl<'a, T> CoerceShared> for OmitMut<'a, T> {} -//~^ ERROR fn read(value: OmitRef<'_, i32>) { assert_eq!(*value.value, 1); diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr deleted file mode 100644 index 70a0db88319a7..0000000000000 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:31:13 - | -LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs index ade1890068d76..597b96ed500f3 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs @@ -28,7 +28,6 @@ impl<'a, T> Clone for OmitRef<'a, T> { impl<'a, T> Copy for OmitRef<'a, T> {} impl<'a, T> CoerceShared> for OmitMut<'a, T> {} -//~^ ERROR fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { value.value diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr index d718103c80e7f..a6d252ba1f5fb 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr @@ -1,11 +1,5 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:13 - | -LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0506]: cannot assign to `*wrapped.extra.value` because it is borrowed - --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:49:5 + --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:48:5 | LL | let shared = get(wrapped); | ------- `*wrapped.extra.value` is borrowed here @@ -16,6 +10,6 @@ LL | *wrapped.extra.value = 3; | `*wrapped.extra.value` is assigned to here but it was already borrowed | borrow later used here -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs index 55cc010c19af4..74d321667d7bf 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow)] #![allow(dead_code)] @@ -29,7 +31,6 @@ impl<'a, T> Clone for OmitRef<'a, T> { impl<'a, T> Copy for OmitRef<'a, T> {} impl<'a, T> CoerceShared> for OmitMut<'a, T> {} -//~^ ERROR fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { value.value diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr deleted file mode 100644 index c663576228f62..0000000000000 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:13 - | -LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.rs b/tests/ui/reborrow/coerce-shared-reordered-field.rs index f4630fe1f7d83..0aa1ffd6e4059 100644 --- a/tests/ui/reborrow/coerce-shared-reordered-field.rs +++ b/tests/ui/reborrow/coerce-shared-reordered-field.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(reborrow)] #![allow(dead_code)] @@ -17,7 +19,6 @@ struct ReorderRef<'a> { } impl<'a> CoerceShared> for ReorderMut<'a> {} -//~^ ERROR fn read(value: ReorderRef<'_>) -> (u16, u8) { (*value.b, *value.a) diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.stderr b/tests/ui/reborrow/coerce-shared-reordered-field.stderr deleted file mode 100644 index e3cd68547c428..0000000000000 --- a/tests/ui/reborrow/coerce-shared-reordered-field.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-reordered-field.rs:19:10 - | -LL | impl<'a> CoerceShared> for ReorderMut<'a> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - From 01c2eb46d1e2f4409cc2bb6d3c2f6787426f1373 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 15 Jun 2026 11:58:30 +0200 Subject: [PATCH 7/7] Rework reborrow test expectations --- .../auxiliary/reborrow_foreign_private.rs | 6 +++ .../coerce-shared-associated-type-field.rs | 31 +++++++++++ .../coerce-shared-decl-macro-hygiene.rs | 27 ++++++++++ .../coerce-shared-field-lifetime-swap.rs | 21 ++++++++ .../coerce-shared-field-lifetime-swap.stderr | 11 ++++ .../reborrow/coerce-shared-field-relations.rs | 52 +++++++++++++++++++ .../coerce-shared-field-relations.stderr | 17 ++++++ .../coerce-shared-foreign-private-field.rs | 19 +++++++ ...coerce-shared-foreign-private-field.stderr | 8 +++ ...erce-shared-foreign-private-tuple-field.rs | 21 ++++++++ ...-shared-foreign-private-tuple-field.stderr | 8 +++ .../coerce-shared-lifetime-mismatch.rs | 24 +++++++++ .../coerce-shared-lifetime-mismatch.stderr | 23 ++++++++ .../coerce-shared-missing-target-field.rs | 20 +++++++ .../coerce-shared-missing-target-field.stderr | 8 +++ .../coerce-shared-mut-ref-field-validation.rs | 43 +++++++++++++++ ...rce-shared-mut-ref-field-validation.stderr | 11 ++++ .../reborrow/coerce-shared-wrong-generic.rs | 21 ++++++++ .../coerce-shared-wrong-generic.stderr | 11 ++++ .../coerce_shared_lifetime_mismatch.rs | 19 ------- .../coerce_shared_lifetime_mismatch.stderr | 11 ---- 21 files changed, 382 insertions(+), 30 deletions(-) create mode 100644 tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs create mode 100644 tests/ui/reborrow/coerce-shared-associated-type-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr create mode 100644 tests/ui/reborrow/coerce-shared-field-relations.rs create mode 100644 tests/ui/reborrow/coerce-shared-field-relations.stderr create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs create mode 100644 tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr create mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.rs create mode 100644 tests/ui/reborrow/coerce-shared-missing-target-field.stderr create mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs create mode 100644 tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr create mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.rs create mode 100644 tests/ui/reborrow/coerce-shared-wrong-generic.stderr delete mode 100644 tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs delete mode 100644 tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr diff --git a/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs new file mode 100644 index 0000000000000..d9a1029211136 --- /dev/null +++ b/tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs @@ -0,0 +1,6 @@ +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct ForeignRef<'a> { + value: &'a i32, +} diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.rs b/tests/ui/reborrow/coerce-shared-associated-type-field.rs new file mode 100644 index 0000000000000..e39f04f6ce2c0 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.rs @@ -0,0 +1,31 @@ +//@ check-pass + +#![feature(reborrow)] +#![allow(dead_code)] + +use std::marker::{CoerceShared, Reborrow}; + +trait Trait { + type Assoc; +} + +impl Trait for i32 { + type Assoc = i64; +} + +struct MyMut<'a> { + x: &'a (), + y: i64, +} + +#[derive(Copy, Clone)] +struct MyRef<'a> { + x: &'a (), + y: ::Assoc, +} + +impl Reborrow for MyMut<'_> {} + +impl<'a> CoerceShared> for MyMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs new file mode 100644 index 0000000000000..b4246bfefb17d --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![feature(reborrow, decl_macro)] +#![allow(incomplete_features)] + +use std::marker::{CoerceShared, Reborrow}; + +macro my_macro($field:ident) { + pub struct MyMut<'a> { + $field: &'a i32, + field: &'a i64, + } + + #[derive(Clone, Copy)] + pub struct MyRef<'a> { + $field: &'a i32, + field: &'a i64, + } + + impl Reborrow for MyMut<'_> {} + + impl<'a> CoerceShared> for MyMut<'a> {} +} + +my_macro!(field); + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs new file mode 100644 index 0000000000000..9d102238467e6 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct MyMut<'a> { + x: &'static (), + y: &'a (), +} + +impl Reborrow for MyMut<'_> {} + +#[derive(Copy, Clone)] +struct MyRef<'a> { + x: &'a (), + //~^ ERROR + y: &'static (), +} + +impl<'a> CoerceShared> for MyMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr new file mode 100644 index 0000000000000..3d8f8bb6f3706 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr @@ -0,0 +1,11 @@ +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-field-lifetime-swap.rs:14:5 + | +LL | x: &'static (), + | -------------- source field `x` has type `&'static ()` +... +LL | x: &'a (), + | ^^^^^^^^^ target field `x` has type `&'a ()` + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs new file mode 100644 index 0000000000000..3920f3eba7cb5 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-relations.rs @@ -0,0 +1,52 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct CustomMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for CustomMut<'a, T> {} + +#[derive(Clone, Copy)] +struct CustomRef<'a, T> { + value: &'a T, +} + +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +struct RenamedMut<'a, T> { + source: &'a mut T, +} + +impl<'a, T> Reborrow for RenamedMut<'a, T> {} + +#[derive(Clone, Copy)] +struct RenamedRef<'a, T> { + target: &'a T, + //~^ ERROR +} + +impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} + +struct BadMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for BadMut<'a, T> {} + +#[derive(Clone, Copy)] +struct BadRef<'a, T> { + value: &'a u32, + //~^ ERROR + _marker: std::marker::PhantomData, +} + +impl<'a, T> CoerceShared> for BadMut<'a, T> {} + +fn good(_value: CustomRef<'_, u32>) {} + +fn main() { + let mut value = 1; + good(CustomMut { value: &mut value }); +} diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr new file mode 100644 index 0000000000000..c361fc208f206 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -0,0 +1,17 @@ +error: implementing `CoerceShared` requires every target field to have a corresponding source field + --> $DIR/coerce-shared-field-relations.rs:26:5 + | +LL | target: &'a T, + | ^^^^^^^^^^^^^ + +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-field-relations.rs:40:5 + | +LL | value: &'a mut T, + | ---------------- source field `value` has type `&'a mut T` +... +LL | value: &'a u32, + | ^^^^^^^^^^^^^^ target field `value` has type `&'a u32` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs new file mode 100644 index 0000000000000..66c8ece3bd43e --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs @@ -0,0 +1,19 @@ +//@ aux-build: reborrow_foreign_private.rs + +#![feature(reborrow)] + +extern crate reborrow_foreign_private; + +use reborrow_foreign_private::ForeignRef; +use std::marker::{CoerceShared, Reborrow}; + +struct LocalMut<'a> { + value: &'a mut i32, +} + +impl<'a> Reborrow for LocalMut<'a> {} + +impl<'a> CoerceShared> for LocalMut<'a> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr new file mode 100644 index 0000000000000..fb06a6f993098 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl + --> $DIR/coerce-shared-foreign-private-field.rs:16:1 + | +LL | impl<'a> CoerceShared> for LocalMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs new file mode 100644 index 0000000000000..a2b88af04eb50 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +mod foreign_ptr { + use std::marker::PhantomData; + + #[derive(Clone, Copy)] + pub struct ForeignPtrRef<'a>(*const i32, PhantomData<&'a ()>); +} + +use foreign_ptr::ForeignPtrRef; + +struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>); + +impl<'a> Reborrow for LocalPtrMut<'a> {} + +impl<'a> CoerceShared> for LocalPtrMut<'a> {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr new file mode 100644 index 0000000000000..ad7d23716a280 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl + --> $DIR/coerce-shared-foreign-private-tuple-field.rs:18:1 + | +LL | impl<'a> CoerceShared> for LocalPtrMut<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs new file mode 100644 index 0000000000000..b6b5471adb0fc --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs @@ -0,0 +1,24 @@ +#![feature(reborrow)] + +// The impl is accepted, but using it to coerce a local marker into a `'static` +// target still requires the local borrow to live for `'static`. + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); + +impl<'a> Reborrow for CustomMarker<'a> {} + +#[derive(Clone, Copy)] +struct StaticMarkerRef<'a>(PhantomData<&'a ()>); + +impl<'a> CoerceShared> for CustomMarker<'a> {} +//~^ ERROR + +fn method(_a: StaticMarkerRef<'static>) {} + +fn main() { + let a = CustomMarker(PhantomData); + method(a); + //~^ ERROR +} diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr new file mode 100644 index 0000000000000..9282384ead833 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -0,0 +1,23 @@ +error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument + --> $DIR/coerce-shared-lifetime-mismatch.rs:15:10 + | +LL | impl<'a> CoerceShared> for CustomMarker<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0597]: `a` does not live long enough + --> $DIR/coerce-shared-lifetime-mismatch.rs:22:12 + | +LL | let a = CustomMarker(PhantomData); + | - binding `a` declared here +LL | method(a); + | -------^- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` +LL | +LL | } + | - `a` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs new file mode 100644 index 0000000000000..edd843b041fa8 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.rs @@ -0,0 +1,20 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct MissingSourceMut<'a, T> { + value: &'a mut T, +} + +impl<'a, T> Reborrow for MissingSourceMut<'a, T> {} + +#[derive(Clone, Copy)] +struct MissingSourceRef<'a, T> { + value: &'a T, + len: usize, + //~^ ERROR +} + +impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr new file mode 100644 index 0000000000000..8286024cc8022 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr @@ -0,0 +1,8 @@ +error: implementing `CoerceShared` requires every target field to have a corresponding source field + --> $DIR/coerce-shared-missing-target-field.rs:14:5 + | +LL | len: usize, + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs new file mode 100644 index 0000000000000..cbe397bbed552 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -0,0 +1,43 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +trait Trait { + type Assoc; +} + +impl Trait for i32 { + type Assoc = u32; +} + +type AliasMutField<'a> = &'a mut ::Assoc; +type AliasRefField<'a> = &'a u32; + +struct AliasMut<'a> { + value: AliasMutField<'a>, +} + +impl Reborrow for AliasMut<'_> {} + +#[derive(Copy, Clone)] +struct AliasRef<'a> { + value: AliasRefField<'a>, +} + +impl<'a> CoerceShared> for AliasMut<'a> {} + +struct InnerLifetimeMut<'a> { + value: &'a mut &'static (), +} + +impl Reborrow for InnerLifetimeMut<'_> {} + +#[derive(Copy, Clone)] +struct InnerLifetimeRef<'a> { + value: &'a &'a (), + //~^ ERROR +} + +impl<'a> CoerceShared> for InnerLifetimeMut<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr new file mode 100644 index 0000000000000..d07ae4f120e19 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr @@ -0,0 +1,11 @@ +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-mut-ref-field-validation.rs:37:5 + | +LL | value: &'a mut &'static (), + | -------------------------- source field `value` has type `&'a mut &'static ()` +... +LL | value: &'a &'a (), + | ^^^^^^^^^^^^^^^^^ target field `value` has type `&'a &'a ()` + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs new file mode 100644 index 0000000000000..bbd9cfebcd9e8 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.rs @@ -0,0 +1,21 @@ +#![feature(reborrow)] + +use std::marker::{CoerceShared, PhantomData, Reborrow}; + +struct GenericMut<'a, T, U> { + value: &'a mut T, + marker: PhantomData, +} + +impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {} + +#[derive(Clone, Copy)] +struct GenericRef<'a, T, U> { + value: &'a U, + //~^ ERROR + marker: PhantomData, +} + +impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} + +fn main() {} diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr new file mode 100644 index 0000000000000..05bca7d9b2988 --- /dev/null +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -0,0 +1,11 @@ +error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field + --> $DIR/coerce-shared-wrong-generic.rs:14:5 + | +LL | value: &'a mut T, + | ---------------- source field `value` has type `&'a mut T` +... +LL | value: &'a U, + | ^^^^^^^^^^^^ target field `value` has type `&'a U` + +error: aborting due to 1 previous error + diff --git a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs deleted file mode 100644 index 79fd0d04370be..0000000000000 --- a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(reborrow)] - -use std::marker::{CoerceShared, PhantomData, Reborrow}; - -struct Source<'a> { - data: &'static mut (), - marker: PhantomData<&'a ()>, -} - -impl<'a> Reborrow for Source<'a> {} - -struct Target<'a> { - data: &'a (), - //~^ ERROR implementing `CoerceShared` requires corresponding fields to match -} - -impl<'a> CoerceShared> for Source<'a> {} - -fn main() {} diff --git a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr b/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr deleted file mode 100644 index 689f48d474b7b..0000000000000 --- a/tests/ui/reborrow/coerce_shared_lifetime_mismatch.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce_shared_lifetime_mismatch.rs:13:5 - | -LL | data: &'static mut (), - | --------------------- source field `data` has type `&'static mut ()` -... -LL | data: &'a (), - | ^^^^^^^^^^^^ target field `data` has type `&'a ()` - -error: aborting due to 1 previous error -