Skip to main content

rustc_hir_analysis/coherence/builtin/
coerce_shared.rs

1use rustc_attr_ir::lang_items::LangItem;
2use rustc_errors::ErrorGuaranteed;
3use rustc_hir as hir;
4use rustc_hir::ItemKind;
5use rustc_hir::def_id::{DefId, LocalDefId};
6use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
7use rustc_infer::traits::{Obligation, TraitErrors};
8use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized};
9use rustc_span::Span;
10use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
11use rustc_trait_selection::traits::{self, ObligationCtxt};
12use tracing::debug;
13
14use super::{
15    ReborrowDataField, assert_field_type_is_copy, collect_reborrow_data_fields, field_type_is_copy,
16    field_type_is_reborrow, trait_impl_lifetime_params_count,
17};
18use crate::diagnostics;
19
20#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CoerceSharedDiagnosticContext { }
#[automatically_derived]
impl ::core::clone::Clone for CoerceSharedDiagnosticContext {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Span>;
        let _: ::core::clone::AssertParamIsClone<Option<Span>>;
        let _: ::core::clone::AssertParamIsClone<Option<Span>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CoerceSharedDiagnosticContext { }Copy)]
21struct CoerceSharedDiagnosticContext {
22    impl_span: Span,
23    trait_span: Span,
24    source_ty_span: Span,
25    target_ty_span: Span,
26    source_lifetime_span: Option<Span>,
27    target_lifetime_span: Option<Span>,
28}
29
30#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CoerceSharedTypeRole { }
#[automatically_derived]
impl ::core::clone::Clone for CoerceSharedTypeRole {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CoerceSharedTypeRole { }Copy)]
31enum CoerceSharedTypeRole {
32    Source,
33    Target,
34}
35
36impl CoerceSharedTypeRole {
37    fn as_str(self) -> &'static str {
38        match self {
39            CoerceSharedTypeRole::Source => "source",
40            CoerceSharedTypeRole::Target => "target",
41        }
42    }
43
44    fn type_span(self, diagnostic_context: CoerceSharedDiagnosticContext) -> Span {
45        match self {
46            CoerceSharedTypeRole::Source => diagnostic_context.source_ty_span,
47            CoerceSharedTypeRole::Target => diagnostic_context.target_ty_span,
48        }
49    }
50}
51
52fn coerce_shared_diagnostic_context(
53    tcx: TyCtxt<'_>,
54    impl_did: LocalDefId,
55) -> CoerceSharedDiagnosticContext {
56    let item = tcx.hir_expect_item(impl_did);
57    let fallback_span = tcx.def_span(impl_did);
58    let mut diagnostic_context = CoerceSharedDiagnosticContext {
59        impl_span: item.span,
60        trait_span: fallback_span,
61        source_ty_span: fallback_span,
62        target_ty_span: fallback_span,
63        source_lifetime_span: None,
64        target_lifetime_span: None,
65    };
66
67    let ItemKind::Impl(impl_) = &item.kind else {
68        return diagnostic_context;
69    };
70    let Some(of_trait) = impl_.of_trait else {
71        return diagnostic_context;
72    };
73
74    diagnostic_context.trait_span = of_trait.trait_ref.path.span;
75    diagnostic_context.source_ty_span = impl_.self_ty.span;
76    diagnostic_context.source_lifetime_span = first_explicit_lifetime_span_in_ty(impl_.self_ty)
77        .or_else(|| first_explicit_impl_lifetime_param_span(impl_.generics));
78
79    if let Some(target_ty) = coerce_shared_target_ty_from_path(of_trait.trait_ref.path) {
80        diagnostic_context.target_ty_span = target_ty.span;
81        diagnostic_context.target_lifetime_span =
82            first_explicit_lifetime_span_in_ambig_ty(target_ty);
83    } else {
84        diagnostic_context.target_ty_span = diagnostic_context.trait_span;
85    }
86
87    diagnostic_context
88}
89
90fn coerce_shared_target_ty_from_path<'hir>(
91    path: &'hir hir::Path<'hir>,
92) -> Option<&'hir hir::Ty<'hir, hir::AmbigArg>> {
93    path.segments.last()?.args().args.iter().find_map(|arg| match arg {
94        hir::GenericArg::Type(ty) => Some(*ty),
95        hir::GenericArg::Lifetime(_) | hir::GenericArg::Const(_) | hir::GenericArg::Infer(_) => {
96            None
97        }
98    })
99}
100
101fn first_explicit_impl_lifetime_param_span(generics: &hir::Generics<'_>) -> Option<Span> {
102    generics.params.iter().find_map(|param| match param.kind {
103        hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
104            Some(param.span)
105        }
106        hir::GenericParamKind::Lifetime { .. }
107        | hir::GenericParamKind::Type { .. }
108        | hir::GenericParamKind::Const { .. } => None,
109    })
110}
111
112fn first_explicit_lifetime_span(lifetime: &hir::Lifetime) -> Option<Span> {
113    match lifetime.kind {
114        hir::LifetimeKind::Param(_) | hir::LifetimeKind::Static
115            if !lifetime.ident.span.is_dummy() =>
116        {
117            Some(lifetime.ident.span)
118        }
119        hir::LifetimeKind::Param(_)
120        | hir::LifetimeKind::Static
121        | hir::LifetimeKind::ImplicitObjectLifetimeDefault
122        | hir::LifetimeKind::Error(_)
123        | hir::LifetimeKind::Infer => None,
124    }
125}
126
127fn first_explicit_lifetime_span_in_ambig_ty(ty: &hir::Ty<'_, hir::AmbigArg>) -> Option<Span> {
128    first_explicit_lifetime_span_in_ty(ty.as_unambig_ty())
129}
130
131fn first_explicit_lifetime_span_in_ty(ty: &hir::Ty<'_>) -> Option<Span> {
132    match ty.kind {
133        hir::TyKind::Ref(lifetime, mut_ty) => first_explicit_lifetime_span(lifetime)
134            .or_else(|| first_explicit_lifetime_span_in_ty(mut_ty.ty)),
135        hir::TyKind::Slice(ty)
136        | hir::TyKind::Array(ty, _)
137        | hir::TyKind::Pat(ty, _)
138        | hir::TyKind::FieldOf(ty, _)
139        | hir::TyKind::View(ty, _) => first_explicit_lifetime_span_in_ty(ty),
140        hir::TyKind::Ptr(mut_ty) => first_explicit_lifetime_span_in_ty(mut_ty.ty),
141        hir::TyKind::Tup(tys) => tys.iter().find_map(first_explicit_lifetime_span_in_ty),
142        hir::TyKind::Path(qpath) => first_explicit_lifetime_span_in_qpath(qpath),
143        hir::TyKind::TraitObject(bounds, lifetime) => bounds
144            .iter()
145            .find_map(|bound| first_explicit_lifetime_span_in_path(bound.trait_ref.path))
146            .or_else(|| first_explicit_lifetime_span(&lifetime)),
147        hir::TyKind::OpaqueDef(opaque) => first_explicit_lifetime_span_in_bounds(opaque.bounds),
148        hir::TyKind::TraitAscription(bounds) => first_explicit_lifetime_span_in_bounds(bounds),
149        hir::TyKind::FnPtr(fn_ptr) => {
150            fn_ptr.generic_params.iter().find_map(|param| match param.kind {
151                hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
152                    Some(param.span)
153                }
154                hir::GenericParamKind::Lifetime { .. }
155                | hir::GenericParamKind::Type { .. }
156                | hir::GenericParamKind::Const { .. } => None,
157            })
158        }
159        hir::TyKind::UnsafeBinder(binder) => binder
160            .generic_params
161            .iter()
162            .find_map(|param| match param.kind {
163                hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
164                    Some(param.span)
165                }
166                hir::GenericParamKind::Lifetime { .. }
167                | hir::GenericParamKind::Type { .. }
168                | hir::GenericParamKind::Const { .. } => None,
169            })
170            .or_else(|| first_explicit_lifetime_span_in_ty(binder.inner_ty)),
171        hir::TyKind::InferDelegation(_)
172        | hir::TyKind::Never
173        | hir::TyKind::Infer(())
174        | hir::TyKind::Err(_) => None,
175    }
176}
177
178fn first_explicit_lifetime_span_in_bounds(bounds: hir::GenericBounds<'_>) -> Option<Span> {
179    bounds.iter().find_map(|bound| match bound {
180        hir::GenericBound::Trait(poly_trait_ref) => {
181            first_explicit_lifetime_span_in_path(poly_trait_ref.trait_ref.path)
182        }
183        hir::GenericBound::Outlives(lifetime) => first_explicit_lifetime_span(lifetime),
184        hir::GenericBound::Use(args, _) => args.iter().find_map(|arg| match arg {
185            hir::PreciseCapturingArgKind::Lifetime(lifetime) => {
186                first_explicit_lifetime_span(lifetime)
187            }
188            hir::PreciseCapturingArgKind::Param(_) => None,
189        }),
190    })
191}
192
193fn first_explicit_lifetime_span_in_qpath(qpath: hir::QPath<'_>) -> Option<Span> {
194    match qpath {
195        hir::QPath::Resolved(qself, path) => qself
196            .and_then(first_explicit_lifetime_span_in_ty)
197            .or_else(|| first_explicit_lifetime_span_in_path(path)),
198        hir::QPath::TypeRelative(qself, segment) => first_explicit_lifetime_span_in_ty(qself)
199            .or_else(|| first_explicit_lifetime_span_in_path_segment(segment)),
200    }
201}
202
203fn first_explicit_lifetime_span_in_path(path: &hir::Path<'_>) -> Option<Span> {
204    path.segments.iter().find_map(first_explicit_lifetime_span_in_path_segment)
205}
206
207fn first_explicit_lifetime_span_in_path_segment(segment: &hir::PathSegment<'_>) -> Option<Span> {
208    first_explicit_lifetime_span_in_generic_args(segment.args())
209}
210
211fn first_explicit_lifetime_span_in_generic_args(args: &hir::GenericArgs<'_>) -> Option<Span> {
212    args.args
213        .iter()
214        .find_map(|arg| match arg {
215            hir::GenericArg::Lifetime(lifetime) => first_explicit_lifetime_span(lifetime),
216            hir::GenericArg::Type(ty) => first_explicit_lifetime_span_in_ambig_ty(ty),
217            hir::GenericArg::Const(_) | hir::GenericArg::Infer(_) => None,
218        })
219        .or_else(|| {
220            args.constraints.iter().find_map(|constraint| {
221                first_explicit_lifetime_span_in_generic_args(constraint.gen_args)
222                    .or_else(|| constraint.ty().and_then(first_explicit_lifetime_span_in_ty))
223            })
224        })
225}
226
227pub(super) fn coerce_shared_info<'tcx>(
228    tcx: TyCtxt<'tcx>,
229    impl_did: LocalDefId,
230) -> Result<(), ErrorGuaranteed> {
231    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/7bbda45cb1ec0a378d91959646a8fa904be3192a/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs:231",
                        "rustc_hir_analysis::coherence::builtin::coerce_shared",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/7bbda45cb1ec0a378d91959646a8fa904be3192a/compiler/rustc_hir_analysis/src/coherence/builtin/coerce_shared.rs"),
                        ::tracing_core::__macro_support::Option::Some(231u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_analysis::coherence::builtin::coerce_shared"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("compute_coerce_shared_info(impl_did={0:?})",
                                                    impl_did) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("compute_coerce_shared_info(impl_did={:?})", impl_did);
232    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
233    let span = tcx.def_span(impl_did);
234    let diagnostic_context = coerce_shared_diagnostic_context(tcx, impl_did);
235    let trait_name = "CoerceShared";
236
237    let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span);
238
239    let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip();
240    let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip();
241
242    if trait_impl_lifetime_params_count(tcx, impl_did) != 1 {
243        return Err(tcx
244            .dcx()
245            .emit_err(diagnostics::CoerceSharedNotSingleLifetimeParam { span, trait_name }));
246    }
247
248    {
    match (&trait_ref.def_id, &coerce_shared_trait) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(trait_ref.def_id, coerce_shared_trait);
249    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
250    let param_env = tcx.param_env(impl_did);
251    let (source, target) = ocx
252        .deeply_normalize(
253            &traits::ObligationCause::misc(span, impl_did),
254            param_env,
255            Unnormalized::new_wip((source, trait_ref.args.type_at(1))),
256        )
257        .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
258    let errors = ocx.evaluate_obligations_error_on_ambiguity();
259    if let TraitErrors::HasErrors(errors) = errors {
260        return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
261    }
262
263    if !!source.has_escaping_bound_vars() {
    ::core::panicking::panic("assertion failed: !source.has_escaping_bound_vars()")
};assert!(!source.has_escaping_bound_vars());
264
265    match (source.kind(), target.kind()) {
266        (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))
267            if def_a.is_struct() && def_b.is_struct() =>
268        {
269            let a_lifetime = single_region_arg(args_a);
270            let b_lifetime = single_region_arg(args_b);
271
272            if a_lifetime.is_none() || b_lifetime.is_none() {
273                return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMulti {
274                    span: diagnostic_context.trait_span,
275                    trait_name,
276                }));
277            }
278
279            if a_lifetime != b_lifetime {
280                return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedLifetimeMismatch {
281                    span: diagnostic_context.trait_span,
282                    source_lifetime_span: diagnostic_context.source_lifetime_span,
283                    target_lifetime_span: diagnostic_context.target_lifetime_span,
284                    trait_name,
285                }));
286            }
287
288            validate_reborrow_field_access(
289                tcx,
290                impl_did,
291                def_a,
292                trait_name,
293                diagnostic_context,
294                CoerceSharedTypeRole::Source,
295            )?;
296            validate_reborrow_field_access(
297                tcx,
298                impl_did,
299                def_b,
300                trait_name,
301                diagnostic_context,
302                CoerceSharedTypeRole::Target,
303            )?;
304
305            validate_coerce_shared_fields(
306                &infcx,
307                impl_did,
308                param_env,
309                coerce_shared_trait,
310                trait_name,
311                span,
312                diagnostic_context,
313                def_a,
314                args_a,
315                def_b,
316                args_b,
317            )
318        }
319
320        _ => {
321            // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument.
322            Err(tcx.dcx().emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name }))
323        }
324    }
325}
326
327#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl<'tcx> ::core::clone::TrivialClone for CoerceSharedFieldPair<'tcx>
    {
}
#[automatically_derived]
impl<'tcx> ::core::clone::Clone for CoerceSharedFieldPair<'tcx> {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<ReborrowDataField<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<ReborrowDataField<'tcx>>;
        *self
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::marker::Copy for CoerceSharedFieldPair<'tcx> { }Copy)]
328struct CoerceSharedFieldPair<'tcx> {
329    source: ReborrowDataField<'tcx>,
330    target: ReborrowDataField<'tcx>,
331}
332
333struct CoerceSharedFields<'tcx> {
334    pairs: Vec<CoerceSharedFieldPair<'tcx>>,
335    unpaired_sources: Vec<ReborrowDataField<'tcx>>,
336}
337
338#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl<'tcx> ::core::clone::TrivialClone for
    CoerceSharedFieldPairError<'tcx> {
}
#[automatically_derived]
impl<'tcx> ::core::clone::Clone for CoerceSharedFieldPairError<'tcx> {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<ReborrowDataField<'tcx>>;
        *self
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::marker::Copy for CoerceSharedFieldPairError<'tcx> { }Copy)]
339enum CoerceSharedFieldPairError<'tcx> {
340    FieldStyleMismatch,
341    MissingSourceField { target: ReborrowDataField<'tcx> },
342}
343
344fn single_region_arg<'tcx>(args: ty::GenericArgsRef<'tcx>) -> Option<ty::Region<'tcx>> {
345    let mut lifetimes = args.iter().filter_map(|arg| arg.as_region());
346    let lifetime = lifetimes.next()?;
347    lifetimes.next().is_none().then_some(lifetime)
348}
349
350// This is a coherence/WF check only. It verifies that the CoerceShared impl
351// describes a structurally valid field-wise relation. Runtime lowering of the
352// operation is not modeled here.
353fn collect_coerce_shared_field_pairs<'tcx>(
354    tcx: TyCtxt<'tcx>,
355    source_def: ty::AdtDef<'tcx>,
356    source_args: ty::GenericArgsRef<'tcx>,
357    target_def: ty::AdtDef<'tcx>,
358    target_args: ty::GenericArgsRef<'tcx>,
359) -> Result<CoerceSharedFields<'tcx>, CoerceSharedFieldPairError<'tcx>> {
360    let source_variant = source_def.non_enum_variant();
361    let target_variant = target_def.non_enum_variant();
362    if source_variant.ctor_kind() != target_variant.ctor_kind() {
363        return Err(CoerceSharedFieldPairError::FieldStyleMismatch);
364    }
365
366    let source_fields = collect_reborrow_data_fields(tcx, source_def, source_args);
367    let target_fields = collect_reborrow_data_fields(tcx, target_def, target_args);
368
369    let mut pairs = Vec::with_capacity(target_fields.len());
370
371    for target in &target_fields {
372        let source = source_fields
373            .iter()
374            .find(|source| tcx.hygienic_eq(target.ident, source.ident, source_variant.def_id))
375            .ok_or(CoerceSharedFieldPairError::MissingSourceField { target: *target })?;
376
377        pairs.push(CoerceSharedFieldPair { source: *source, target: *target });
378    }
379
380    let unpaired_sources = source_fields
381        .into_iter()
382        .filter(|source| {
383            !target_fields
384                .iter()
385                .any(|target| tcx.hygienic_eq(target.ident, source.ident, source_variant.def_id))
386        })
387        .collect();
388
389    Ok(CoerceSharedFields { pairs, unpaired_sources })
390}
391
392fn validate_reborrow_field_access(
393    tcx: TyCtxt<'_>,
394    impl_did: LocalDefId,
395    def: ty::AdtDef<'_>,
396    trait_name: &'static str,
397    diagnostic_context: CoerceSharedDiagnosticContext,
398    role: CoerceSharedTypeRole,
399) -> Result<(), ErrorGuaranteed> {
400    let module = tcx.parent_module_from_def_id(impl_did);
401    let variant = def.non_enum_variant();
402    if variant.field_list_has_applicable_non_exhaustive() {
403        return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedInaccessibleField {
404            span: diagnostic_context.impl_span,
405            type_span: role.type_span(diagnostic_context),
406            trait_name,
407            role: role.as_str(),
408            type_name: tcx.item_name(def.did()),
409        }));
410    }
411
412    for field in &variant.fields {
413        if !field.vis.is_accessible_from(module, tcx) {
414            return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedInaccessibleField {
415                span: diagnostic_context.impl_span,
416                type_span: role.type_span(diagnostic_context),
417                trait_name,
418                role: role.as_str(),
419                type_name: tcx.item_name(def.did()),
420            }));
421        }
422    }
423
424    Ok(())
425}
426
427fn validate_coerce_shared_fields<'tcx>(
428    infcx: &InferCtxt<'tcx>,
429    impl_did: LocalDefId,
430    param_env: ty::ParamEnv<'tcx>,
431    coerce_shared_trait: DefId,
432    trait_name: &'static str,
433    span: Span,
434    diagnostic_context: CoerceSharedDiagnosticContext,
435    source_def: ty::AdtDef<'tcx>,
436    source_args: ty::GenericArgsRef<'tcx>,
437    target_def: ty::AdtDef<'tcx>,
438    target_args: ty::GenericArgsRef<'tcx>,
439) -> Result<(), ErrorGuaranteed> {
440    let tcx = infcx.tcx;
441    let fields = match collect_coerce_shared_field_pairs(
442        tcx,
443        source_def,
444        source_args,
445        target_def,
446        target_args,
447    ) {
448        Ok(fields) => fields,
449        Err(CoerceSharedFieldPairError::FieldStyleMismatch) => {
450            return Err(tcx
451                .dcx()
452                .emit_err(diagnostics::CoerceSharedFieldStyleMismatch { span, trait_name }));
453        }
454        Err(CoerceSharedFieldPairError::MissingSourceField { target }) => {
455            return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMissingField {
456                span: target.span,
457                source_ty_span: diagnostic_context.source_ty_span,
458                trait_name,
459                source_ty_name: tcx.item_name(source_def.did()),
460                field_name: target.name,
461            }));
462        }
463    };
464
465    for field_pair in fields.pairs {
466        validate_coerce_shared_field(
467            infcx,
468            impl_did,
469            param_env,
470            coerce_shared_trait,
471            trait_name,
472            span,
473            diagnostic_context,
474            field_pair.source,
475            field_pair.target,
476        )?;
477    }
478
479    let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span);
480    for source in fields.unpaired_sources {
481        validate_coerce_shared_unpaired_source_field(
482            infcx,
483            impl_did,
484            param_env,
485            reborrow_trait,
486            trait_name,
487            diagnostic_context,
488            source,
489        )?;
490    }
491
492    // FIXME(reborrow): remove this temporary WF-side memcpy-ability guard once
493    // the downstream CoerceShared implementation can correctly handle source and
494    // target types that are not trivially memcpy-able. Refer to #157489
495    validate_coerce_shared_fields_are_memcpy_compatible(
496        infcx,
497        impl_did,
498        param_env,
499        coerce_shared_trait,
500        trait_name,
501        span,
502        diagnostic_context,
503        source_def,
504        source_args,
505        target_def,
506        target_args,
507    )?;
508
509    Ok(())
510}
511
512fn validate_coerce_shared_fields_are_memcpy_compatible<'tcx>(
513    infcx: &InferCtxt<'tcx>,
514    impl_did: LocalDefId,
515    param_env: ty::ParamEnv<'tcx>,
516    coerce_shared_trait: DefId,
517    trait_name: &'static str,
518    span: Span,
519    diagnostic_context: CoerceSharedDiagnosticContext,
520    source_def: ty::AdtDef<'tcx>,
521    source_args: ty::GenericArgsRef<'tcx>,
522    target_def: ty::AdtDef<'tcx>,
523    target_args: ty::GenericArgsRef<'tcx>,
524) -> Result<(), ErrorGuaranteed> {
525    let tcx = infcx.tcx;
526    let source_non_zst_fields =
527        non_zst_reborrow_data_fields(infcx, param_env, source_def, source_args);
528    let target_non_zst_fields =
529        non_zst_reborrow_data_fields(infcx, param_env, target_def, target_args);
530
531    match (&source_non_zst_fields[..], &target_non_zst_fields[..]) {
532        ([], []) => Ok(()),
533        ([source], [target]) => {
534            if field_tys_satisfy_relation_after_normalization_and_resolution(
535                tcx,
536                impl_did,
537                param_env,
538                source.ty,
539                target.ty,
540                source.span,
541                FieldRelation::Equal,
542            ) {
543                return Ok(());
544            }
545
546            if #[allow(non_exhaustive_omitted_patterns)] match (source.ty.kind(),
        target.ty.kind()) {
    (&ty::Ref(_, _, ty::Mutability::Mut), &ty::Ref(_, _, ty::Mutability::Not))
        | (&ty::Alias(..), _) | (_, &ty::Alias(..)) => true,
    _ => false,
}matches!(
547                (source.ty.kind(), target.ty.kind()),
548                (&ty::Ref(_, _, ty::Mutability::Mut), &ty::Ref(_, _, ty::Mutability::Not))
549                    | (&ty::Alias(..), _)
550                    | (_, &ty::Alias(..))
551            ) && field_tys_satisfy_relation_after_normalization_and_resolution(
552                tcx,
553                impl_did,
554                param_env,
555                source.ty,
556                target.ty,
557                source.span,
558                FieldRelation::MutRefToSharedRef,
559            ) {
560                return Ok(());
561            }
562
563            validate_field_tys_satisfy_coerce_shared_relation(
564                infcx,
565                impl_did,
566                param_env,
567                coerce_shared_trait,
568                trait_name,
569                span,
570                diagnostic_context,
571                *source,
572                *target,
573            )
574        }
575        _ => Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMultipleNonZstFields {
576            span: diagnostic_context.impl_span,
577            source_ty_span: diagnostic_context.source_ty_span,
578            target_ty_span: diagnostic_context.target_ty_span,
579            trait_name,
580            source_count: source_non_zst_fields.len(),
581            target_count: target_non_zst_fields.len(),
582        })),
583    }
584}
585
586fn non_zst_reborrow_data_fields<'tcx>(
587    infcx: &InferCtxt<'tcx>,
588    param_env: ty::ParamEnv<'tcx>,
589    def: ty::AdtDef<'tcx>,
590    args: ty::GenericArgsRef<'tcx>,
591) -> Vec<ReborrowDataField<'tcx>> {
592    let tcx = infcx.tcx;
593    collect_reborrow_data_fields(tcx, def, args)
594        .into_iter()
595        .filter(|field| {
596            !#[allow(non_exhaustive_omitted_patterns)] match tcx.layout_of(infcx.typing_env(param_env).as_query_input(field.ty))
    {
    Ok(layout) if layout.is_zst() => true,
    _ => false,
}matches!(
597                tcx.layout_of(infcx.typing_env(param_env).as_query_input(field.ty)),
598                Ok(layout) if layout.is_zst()
599            )
600        })
601        .collect()
602}
603
604fn validate_coerce_shared_field<'tcx>(
605    infcx: &InferCtxt<'tcx>,
606    impl_did: LocalDefId,
607    param_env: ty::ParamEnv<'tcx>,
608    coerce_shared_trait: DefId,
609    trait_name: &'static str,
610    span: Span,
611    diagnostic_context: CoerceSharedDiagnosticContext,
612    source: ReborrowDataField<'tcx>,
613    target: ReborrowDataField<'tcx>,
614) -> Result<(), ErrorGuaranteed> {
615    let tcx = infcx.tcx;
616    if #[allow(non_exhaustive_omitted_patterns)] match (source.ty.kind(),
        target.ty.kind()) {
    (&ty::Ref(_, _, ty::Mutability::Mut), &ty::Ref(_, _, ty::Mutability::Not))
        | (&ty::Alias(..), _) | (_, &ty::Alias(..)) => true,
    _ => false,
}matches!(
617        (source.ty.kind(), target.ty.kind()),
618        (&ty::Ref(_, _, ty::Mutability::Mut), &ty::Ref(_, _, ty::Mutability::Not))
619            | (&ty::Alias(..), _)
620            | (_, &ty::Alias(..))
621    ) && field_tys_satisfy_relation_after_normalization_and_resolution(
622        tcx,
623        impl_did,
624        param_env,
625        source.ty,
626        target.ty,
627        source.span,
628        FieldRelation::MutRefToSharedRef,
629    ) {
630        return Ok(());
631    }
632
633    if field_tys_satisfy_relation_after_normalization_and_resolution(
634        tcx,
635        impl_did,
636        param_env,
637        source.ty,
638        target.ty,
639        source.span,
640        FieldRelation::Equal,
641    ) {
642        return assert_field_type_is_copy(tcx, infcx, impl_did, param_env, source.ty, source.span);
643    }
644
645    validate_field_tys_satisfy_coerce_shared_relation(
646        infcx,
647        impl_did,
648        param_env,
649        coerce_shared_trait,
650        trait_name,
651        span,
652        diagnostic_context,
653        source,
654        target,
655    )
656}
657
658fn validate_coerce_shared_unpaired_source_field<'tcx>(
659    infcx: &InferCtxt<'tcx>,
660    impl_did: LocalDefId,
661    param_env: ty::ParamEnv<'tcx>,
662    reborrow_trait: DefId,
663    trait_name: &'static str,
664    diagnostic_context: CoerceSharedDiagnosticContext,
665    mut source: ReborrowDataField<'tcx>,
666) -> Result<(), ErrorGuaranteed> {
667    let tcx = infcx.tcx;
668    let ocx = ObligationCtxt::new_with_diagnostics(infcx);
669    source.ty = ocx
670        .deeply_normalize(
671            &traits::ObligationCause::misc(source.span, impl_did),
672            param_env,
673            Unnormalized::new_wip(source.ty),
674        )
675        .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
676
677    if field_type_is_reborrow(
678        tcx,
679        infcx,
680        reborrow_trait,
681        impl_did,
682        param_env,
683        source.ty,
684        source.span,
685    ) || field_type_is_copy(tcx, infcx, impl_did, param_env, source.ty, source.span)
686    {
687        return Ok(());
688    }
689
690    Err(tcx.dcx().emit_err(diagnostics::CoerceSharedOmittedSourceFieldNotCopyOrReborrow {
691        span: source.span,
692        impl_span: diagnostic_context.impl_span,
693        trait_name,
694        field_name: source.name,
695        field_ty: source.ty,
696    }))
697}
698
699fn validate_field_tys_satisfy_coerce_shared_relation<'tcx>(
700    infcx: &InferCtxt<'tcx>,
701    impl_did: LocalDefId,
702    param_env: ty::ParamEnv<'tcx>,
703    coerce_shared_trait: DefId,
704    trait_name: &'static str,
705    span: Span,
706    diagnostic_context: CoerceSharedDiagnosticContext,
707    source: ReborrowDataField<'tcx>,
708    target: ReborrowDataField<'tcx>,
709) -> Result<(), ErrorGuaranteed> {
710    let tcx = infcx.tcx;
711    let ocx = ObligationCtxt::new_with_diagnostics(infcx);
712    let cause = traits::ObligationCause::misc(span, impl_did);
713    ocx.register_obligation(Obligation::new(
714        tcx,
715        cause,
716        param_env,
717        ty::TraitRef::new(tcx, coerce_shared_trait, [source.ty, target.ty]),
718    ));
719    let errors = ocx.evaluate_obligations_error_on_ambiguity();
720
721    if errors.has_errors() {
722        return Err(emit_coerce_shared_field_mismatch(
723            tcx,
724            trait_name,
725            diagnostic_context,
726            source,
727            target,
728        ));
729    }
730
731    ocx.resolve_regions_and_report_errors(impl_did, param_env, [])
732}
733
734fn emit_coerce_shared_field_mismatch<'tcx>(
735    tcx: TyCtxt<'tcx>,
736    trait_name: &'static str,
737    diagnostic_context: CoerceSharedDiagnosticContext,
738    source: ReborrowDataField<'tcx>,
739    target: ReborrowDataField<'tcx>,
740) -> ErrorGuaranteed {
741    tcx.dcx().emit_err(diagnostics::CoerceSharedFieldMismatch {
742        span: target.span,
743        source_span: source.span,
744        impl_span: diagnostic_context.impl_span,
745        source_name: source.name,
746        source_ty: source.ty,
747        target_name: target.name,
748        target_ty: target.ty,
749        trait_name,
750    })
751}
752
753enum FieldRelation {
754    Equal,
755    MutRefToSharedRef,
756}
757
758// Normalizing the outer `CoerceShared` types does not normalize their fields:
759// instantiating a field can expose projections. Each candidate relation uses a
760// fresh inference context, so failed checks cannot affect the next one; this
761// intentionally normalizes the fields for each check.
762//
763// FIXME(field_projections): This function should take `Unnormalized<Ty<'tcx>>` for
764// `source_ty` and `target_ty`.
765fn field_tys_satisfy_relation_after_normalization_and_resolution<'tcx>(
766    tcx: TyCtxt<'tcx>,
767    impl_did: LocalDefId,
768    param_env: ty::ParamEnv<'tcx>,
769    source_ty: Ty<'tcx>,
770    target_ty: Ty<'tcx>,
771    span: Span,
772    relation: FieldRelation,
773) -> bool {
774    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
775    let cause = traits::ObligationCause::misc(span, impl_did);
776    let ocx = ObligationCtxt::new(&infcx);
777
778    let Ok((source_ty, target_ty)) =
779        ocx.deeply_normalize(&cause, param_env, Unnormalized::new_wip((source_ty, target_ty)))
780    else {
781        return false;
782    };
783
784    if ocx.evaluate_obligations_error_on_ambiguity().has_errors() {
785        return false;
786    }
787
788    match relation {
789        FieldRelation::Equal => {
790            if infcx
791                .at(&cause, param_env)
792                .relate(DefineOpaqueTypes::Yes, source_ty, ty::Variance::Invariant, target_ty)
793                .is_err()
794            {
795                return false;
796            }
797        }
798        FieldRelation::MutRefToSharedRef => {
799            let (
800                &ty::Ref(source_region, source_referent_ty, ty::Mutability::Mut),
801                &ty::Ref(target_region, target_referent_ty, ty::Mutability::Not),
802            ) = (source_ty.kind(), target_ty.kind())
803            else {
804                return false;
805            };
806            if source_region != target_region {
807                return false;
808            }
809            if ocx.sup(&cause, param_env, target_referent_ty, source_referent_ty).is_err() {
810                return false;
811            }
812        }
813    };
814
815    ocx.evaluate_obligations_error_on_ambiguity().no_errors()
816        && ocx.resolve_regions(impl_did, param_env, []).is_empty()
817}