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