| //! Check properties that are required by built-in traits and set | |
| //! up data structures required by type-checking/codegen. | |
| use std::collections::BTreeMap; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_errors::{ErrorGuaranteed, MultiSpan}; | |
| use rustc_hir as hir; | |
| use rustc_hir::ItemKind; | |
| use rustc_hir::def_id::{DefId, LocalDefId}; | |
| use rustc_hir::lang_items::LangItem; | |
| use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt}; | |
| use rustc_infer::traits::{Obligation, PredicateObligations}; | |
| use rustc_middle::ty::adjustment::CoerceUnsizedInfo; | |
| use rustc_middle::ty::print::PrintTraitRefExt as _; | |
| use rustc_middle::ty::relate::solver_relating::RelateExt; | |
| use rustc_middle::ty::{ | |
| self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, suggest_constraining_type_params, | |
| }; | |
| use rustc_span::{DUMMY_SP, Span, sym}; | |
| use rustc_trait_selection::error_reporting::InferCtxtErrorExt; | |
| use rustc_trait_selection::traits::misc::{ | |
| ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, | |
| type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, | |
| }; | |
| use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCause, ObligationCtxt}; | |
| use tracing::debug; | |
| use crate::diagnostics; | |
| pub(super) fn check_trait<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| trait_def_id: DefId, | |
| impl_def_id: LocalDefId, | |
| impl_header: ty::ImplTraitHeader<'tcx>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let lang_items = tcx.lang_items(); | |
| let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header }; | |
| checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; | |
| checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; | |
| checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; | |
| checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?; | |
| checker.check(lang_items.const_param_ty_trait(), |checker| { | |
| visit_implementation_of_const_param_ty(checker) | |
| })?; | |
| checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; | |
| checker.check(lang_items.reborrow(), visit_implementation_of_reborrow)?; | |
| checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?; | |
| checker | |
| .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; | |
| checker.check( | |
| lang_items.coerce_pointee_validated_trait(), | |
| visit_implementation_of_coerce_pointee_validity, | |
| )?; | |
| Ok(()) | |
| } | |
| struct Checker<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| trait_def_id: DefId, | |
| impl_def_id: LocalDefId, | |
| impl_header: ty::ImplTraitHeader<'tcx>, | |
| } | |
| impl<'tcx> Checker<'tcx> { | |
| fn check( | |
| &self, | |
| trait_def_id: Option<DefId>, | |
| f: impl FnOnce(&Self) -> Result<(), ErrorGuaranteed>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| if Some(self.trait_def_id) == trait_def_id { f(self) } else { Ok(()) } | |
| } | |
| } | |
| fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_did = checker.impl_def_id; | |
| // Destructors only work on local ADT types. | |
| match checker.impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty().kind() { | |
| ty::Adt(def, _) if def.did().is_local() => return Ok(()), | |
| ty::Error(_) => return Ok(()), | |
| _ => {} | |
| } | |
| let impl_ = tcx.hir_expect_item(impl_did).expect_impl(); | |
| Err(tcx.dcx().emit_err(diagnostics::DropImplOnWrongItem { | |
| span: impl_.self_ty.span, | |
| trait_: tcx.item_name(checker.impl_header.trait_ref.skip_binder().def_id), | |
| })) | |
| } | |
| fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_header = checker.impl_header; | |
| let impl_did = checker.impl_def_id; | |
| debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); | |
| let self_type = impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty(); | |
| debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); | |
| let param_env = tcx.param_env(impl_did); | |
| assert!(!self_type.has_escaping_bound_vars()); | |
| debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); | |
| if let ty::ImplPolarity::Negative = impl_header.polarity { | |
| return Ok(()); | |
| } | |
| let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); | |
| match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) { | |
| Ok(()) => Ok(()), | |
| Err(CopyImplementationError::InfringingFields(fields)) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(infringing_fields_error( | |
| tcx, | |
| fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), | |
| LangItem::Copy, | |
| impl_did, | |
| span, | |
| )) | |
| } | |
| Err(CopyImplementationError::NotAnAdt) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(tcx.dcx().emit_err(diagnostics::CopyImplOnNonAdt { span })) | |
| } | |
| Err(CopyImplementationError::HasDestructor(did)) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| let impl_ = tcx.def_span(did); | |
| Err(tcx.dcx().emit_err(diagnostics::CopyImplOnTypeWithDtor { span, impl_ })) | |
| } | |
| Err(CopyImplementationError::HasUnsafeFields) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(tcx | |
| .dcx() | |
| .span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type))) | |
| } | |
| } | |
| } | |
| fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_header = checker.impl_header; | |
| let impl_did = checker.impl_def_id; | |
| debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did); | |
| let self_type = impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty(); | |
| debug!("visit_implementation_of_unpin: self_type={:?}", self_type); | |
| let span = tcx.def_span(impl_did); | |
| if tcx.features().pin_ergonomics() { | |
| match self_type.kind() { | |
| // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project | |
| // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`). | |
| // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait, | |
| // which cannot carry safety properties), then `&mut U` could be obtained from | |
| // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of | |
| // `Pin<&mut U>` for `U: !Unpin`. | |
| ty::Adt(adt, _) if adt.is_pin_project() => { | |
| return Err(tcx.dcx().emit_err(crate::diagnostics::ImplUnpinForPinProjectedType { | |
| span, | |
| adt_span: tcx.def_span(adt.did()), | |
| adt_name: tcx.item_name(adt.did()), | |
| })); | |
| } | |
| ty::Adt(_, _) => {} | |
| _ => { | |
| return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type")); | |
| } | |
| }; | |
| } | |
| Ok(()) | |
| } | |
| fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let header = checker.impl_header; | |
| let impl_did = checker.impl_def_id; | |
| let self_type = header.trait_ref.instantiate_identity().skip_norm_wip().self_ty(); | |
| assert!(!self_type.has_escaping_bound_vars()); | |
| let param_env = tcx.param_env(impl_did); | |
| if let ty::ImplPolarity::Negative | ty::ImplPolarity::Reservation = header.polarity { | |
| return Ok(()); | |
| } | |
| if tcx.features().const_param_ty_unchecked() { | |
| return Ok(()); | |
| } | |
| if !tcx.features().adt_const_params() { | |
| match *self_type.kind() { | |
| ty::Adt(adt, _) if adt.is_struct() => { | |
| let struct_vis = tcx.visibility(adt.did()); | |
| for variant in adt.variants() { | |
| for field in &variant.fields { | |
| if struct_vis.greater_than(field.vis, tcx) { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::ConstParamTyFieldVisMismatch { span })); | |
| } | |
| } | |
| } | |
| } | |
| _ => {} | |
| } | |
| } | |
| let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); | |
| match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { | |
| Ok(()) => Ok(()), | |
| Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(infringing_fields_error( | |
| tcx, | |
| fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), | |
| LangItem::ConstParamTy, | |
| impl_did, | |
| span, | |
| )) | |
| } | |
| Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(tcx.dcx().emit_err(diagnostics::ConstParamTyImplOnNonAdt { span })) | |
| } | |
| Err(ConstParamTyImplementationError::NonExhaustive(attr_span)) => { | |
| let defn_span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::ConstParamTyImplOnNonExhaustive { defn_span, attr_span })) | |
| } | |
| Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(infringing_fields_error( | |
| tcx, | |
| infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)), | |
| LangItem::ConstParamTy, | |
| impl_did, | |
| span, | |
| )) | |
| } | |
| Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => { | |
| let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; | |
| Err(tcx.dcx().emit_err(diagnostics::ConstParamTyImplOnUnsized { span })) | |
| } | |
| } | |
| } | |
| fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_did = checker.impl_def_id; | |
| debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); | |
| // Just compute this for the side-effects, in particular reporting | |
| // errors; other parts of the code may demand it for the info of | |
| // course. | |
| tcx.ensure_result().coerce_unsized_info(impl_did) | |
| } | |
| fn visit_implementation_of_reborrow(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_did = checker.impl_def_id; | |
| debug!("visit_implementation_of_reborrow: impl_did={:?}", impl_did); | |
| // Just compute this for the side-effects, in particular reporting | |
| // errors; other parts of the code may demand it for the info of | |
| // course. | |
| reborrow_info(tcx, impl_did) | |
| } | |
| fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_did = checker.impl_def_id; | |
| debug!("visit_implementation_of_coerce_shared: impl_did={:?}", impl_did); | |
| // Just compute this for the side-effects, in particular reporting | |
| // errors; other parts of the code may demand it for the info of | |
| // course. | |
| coerce_shared_info(tcx, impl_did) | |
| } | |
| fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool { | |
| span.ctxt() | |
| .outer_expn_data() | |
| .macro_def_id | |
| .is_some_and(|def_id| tcx.is_diagnostic_item(sym::CoercePointee, def_id)) | |
| } | |
| fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let impl_did = checker.impl_def_id; | |
| let trait_ref = checker.impl_header.trait_ref.instantiate_identity().skip_norm_wip(); | |
| debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); | |
| let span = tcx.def_span(impl_did); | |
| let trait_name = "DispatchFromDyn"; | |
| let source = trait_ref.self_ty(); | |
| let target = { | |
| assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn)); | |
| trait_ref.args.type_at(1) | |
| }; | |
| // Check `CoercePointee` impl is WF -- if not, then there's no reason to report | |
| // redundant errors for `DispatchFromDyn`. This is best effort, though. | |
| let mut res = Ok(()); | |
| tcx.for_each_relevant_impl( | |
| tcx.require_lang_item(LangItem::CoerceUnsized, span), | |
| source, | |
| |impl_def_id| { | |
| res = res.and(tcx.ensure_result().coerce_unsized_info(impl_def_id)); | |
| }, | |
| ); | |
| res?; | |
| debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); | |
| let param_env = tcx.param_env(impl_did); | |
| let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); | |
| let cause = ObligationCause::misc(span, impl_did); | |
| // Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw | |
| // pointers. This is enforced here: we only allow impls for references, raw pointers, and things | |
| // that are effectively repr(transparent) newtypes around types that already hav a | |
| // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those types since some | |
| // of them support an allocator, but we ensure that for the cases where the type implements this | |
| // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else | |
| // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) | |
| // even if they do not carry that attribute. | |
| match (source.kind(), target.kind()) { | |
| (&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => { | |
| if pat_a != pat_b { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceSamePatKind { | |
| span, | |
| trait_name, | |
| pat_a: pat_a.to_string(), | |
| pat_b: pat_b.to_string(), | |
| })); | |
| } | |
| Ok(()) | |
| } | |
| (&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b)) | |
| if r_a == *r_b && mutbl_a == *mutbl_b => | |
| { | |
| Ok(()) | |
| } | |
| (&ty::RawPtr(_, a_mutbl), &ty::RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()), | |
| (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) | |
| if def_a.is_struct() && def_b.is_struct() => | |
| { | |
| if def_a != def_b { | |
| let source_path = tcx.def_path_str(def_a.did()); | |
| let target_path = tcx.def_path_str(def_b.did()); | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceSameStruct { | |
| span, | |
| trait_name, | |
| note: true, | |
| source_path, | |
| target_path, | |
| })); | |
| } | |
| if def_a.repr().c() || def_a.repr().packed() { | |
| return Err(tcx.dcx().emit_err(diagnostics::DispatchFromDynRepr { span })); | |
| } | |
| let fields = &def_a.non_enum_variant().fields; | |
| let mut res = Ok(()); | |
| let coerced_fields = fields | |
| .iter_enumerated() | |
| .filter_map(|(i, field)| { | |
| // Ignore PhantomData fields | |
| let unnormalized_ty = tcx.type_of(field.did).instantiate_identity(); | |
| if tcx | |
| .try_normalize_erasing_regions( | |
| ty::TypingEnv::non_body_analysis(tcx, def_a.did()), | |
| unnormalized_ty, | |
| ) | |
| .unwrap_or(unnormalized_ty.skip_norm_wip()) | |
| .is_phantom_data() | |
| { | |
| return None; | |
| } | |
| let ty_a = field.ty(tcx, args_a).skip_norm_wip(); | |
| let ty_b = field.ty(tcx, args_b).skip_norm_wip(); | |
| // FIXME: We could do normalization here, but is it really worth it? | |
| if ty_a == ty_b { | |
| // Allow 1-ZSTs that don't mention type params. | |
| // | |
| // Allowing type params here would allow us to possibly transmute | |
| // between ZSTs, which may be used to create library unsoundness. | |
| if let Ok(layout) = | |
| tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a)) | |
| && layout.is_1zst() | |
| && !ty_a.has_non_region_param() | |
| { | |
| // ignore 1-ZST fields | |
| return None; | |
| } | |
| res = Err(tcx.dcx().emit_err(diagnostics::DispatchFromDynZST { | |
| span, | |
| name: field.ident(tcx), | |
| ty: ty_a, | |
| })); | |
| None | |
| } else { | |
| Some((i, ty_a, ty_b, tcx.def_span(field.did))) | |
| } | |
| }) | |
| .collect::<Vec<_>>(); | |
| res?; | |
| if coerced_fields.is_empty() { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceNoField { | |
| span, | |
| trait_name, | |
| note: true, | |
| })); | |
| } else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] { | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| ocx.register_obligation(Obligation::new( | |
| tcx, | |
| cause.clone(), | |
| param_env, | |
| ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]), | |
| )); | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { | |
| if is_from_coerce_pointee_derive(tcx, span) { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceFieldValidity { | |
| span, | |
| trait_name, | |
| ty: trait_ref.self_ty(), | |
| field_span, | |
| field_ty: ty_a, | |
| })); | |
| } else { | |
| return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); | |
| } | |
| } | |
| // Finally, resolve all regions. | |
| ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; | |
| Ok(()) | |
| } else { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceMulti { | |
| span, | |
| trait_name, | |
| number: coerced_fields.len(), | |
| fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(), | |
| })); | |
| } | |
| } | |
| _ => Err(tcx.dcx().emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })), | |
| } | |
| } | |
| fn structurally_normalize_ty<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| infcx: &InferCtxt<'tcx>, | |
| impl_did: LocalDefId, | |
| span: Span, | |
| ty: Unnormalized<'tcx, Ty<'tcx>>, | |
| ) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { | |
| let ocx = ObligationCtxt::new(infcx); | |
| let Ok(normalized_ty) = ocx.structurally_normalize_ty( | |
| &traits::ObligationCause::misc(span, impl_did), | |
| tcx.param_env(impl_did), | |
| ty, | |
| ) else { | |
| // We shouldn't have errors here in the old solver, except for | |
| // evaluate/fulfill mismatches, but that's not a reason for an ICE. | |
| return None; | |
| }; | |
| let errors = ocx.try_evaluate_obligations(); | |
| if !errors.is_empty() { | |
| if infcx.next_trait_solver() { | |
| unreachable!(); | |
| } | |
| // We shouldn't have errors here in the old solver, except for | |
| // evaluate/fulfill mismatches, but that's not a reason for an ICE. | |
| debug!(?errors, "encountered errors while fulfilling"); | |
| return None; | |
| } | |
| Some((normalized_ty, ocx.into_pending_obligations())) | |
| } | |
| pub(crate) fn reborrow_info<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| impl_did: LocalDefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| debug!("compute_reborrow_info(impl_did={:?})", impl_did); | |
| let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); | |
| let span = tcx.def_span(impl_did); | |
| let trait_name = "Reborrow"; | |
| let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span); | |
| let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); | |
| let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); | |
| if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoerceSharedNotSingleLifetimeParam { span, trait_name })); | |
| } | |
| assert_eq!(trait_ref.def_id, reborrow_trait); | |
| let param_env = tcx.param_env(impl_did); | |
| assert!(!source.has_escaping_bound_vars()); | |
| let (def, args) = match source.kind() { | |
| &ty::Adt(def, args) if def.is_struct() => (def, args), | |
| _ => { | |
| // Note: reusing error here as it takes trait_name as argument. | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })); | |
| } | |
| }; | |
| let lifetimes_count = generic_lifetime_params_count(args); | |
| let data_fields = collect_struct_data_fields(tcx, def, args); | |
| if lifetimes_count != 1 { | |
| let item = tcx.hir_expect_item(impl_did); | |
| let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind { | |
| of_trait.trait_ref.path.span | |
| } else { | |
| tcx.def_span(impl_did) | |
| }; | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceSharedMulti { span, trait_name })); | |
| } | |
| if data_fields.is_empty() { | |
| return Ok(()); | |
| } | |
| // We've found some data fields. They must all be either be Copy or Reborrow. | |
| for (field, span) in data_fields { | |
| if assert_field_type_is_reborrow( | |
| tcx, | |
| &infcx, | |
| reborrow_trait, | |
| impl_did, | |
| param_env, | |
| field, | |
| span, | |
| ) | |
| .is_ok() | |
| { | |
| // Field implements Reborrow, check remaining fields. | |
| continue; | |
| } | |
| // Field does not implement Reborrow: it must be Copy. | |
| assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?; | |
| } | |
| Ok(()) | |
| } | |
| fn assert_field_type_is_reborrow<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| infcx: &InferCtxt<'tcx>, | |
| reborrow_trait: DefId, | |
| impl_did: LocalDefId, | |
| param_env: ty::ParamEnv<'tcx>, | |
| ty: Ty<'tcx>, | |
| span: Span, | |
| ) -> Result<(), Vec<FulfillmentError<'tcx>>> { | |
| if ty.ref_mutability() == Some(ty::Mutability::Mut) { | |
| // Mutable references are Reborrow but not really. | |
| return Ok(()); | |
| } | |
| let ocx = ObligationCtxt::new_with_diagnostics(infcx); | |
| let cause = traits::ObligationCause::misc(span, impl_did); | |
| let obligation = | |
| Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, reborrow_trait, [ty])); | |
| ocx.register_obligation(obligation); | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { Err(errors) } else { Ok(()) } | |
| } | |
| pub(crate) fn coerce_shared_info<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| impl_did: LocalDefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| debug!("compute_coerce_shared_info(impl_did={:?})", impl_did); | |
| let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); | |
| let span = tcx.def_span(impl_did); | |
| let trait_name = "CoerceShared"; | |
| let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span); | |
| let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); | |
| let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); | |
| if trait_impl_lifetime_params_count(tcx, impl_did) != 1 { | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoerceSharedNotSingleLifetimeParam { span, trait_name })); | |
| } | |
| assert_eq!(trait_ref.def_id, coerce_shared_trait); | |
| let Some((target, _obligations)) = structurally_normalize_ty( | |
| tcx, | |
| &infcx, | |
| impl_did, | |
| span, | |
| Unnormalized::new_wip(trait_ref.args.type_at(1)), | |
| ) else { | |
| todo!("something went wrong with structurally_normalize_ty"); | |
| }; | |
| let param_env = tcx.param_env(impl_did); | |
| assert!(!source.has_escaping_bound_vars()); | |
| let data = match (source.kind(), target.kind()) { | |
| (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) | |
| if def_a.is_struct() && def_b.is_struct() => | |
| { | |
| // Check that both A and B have exactly one lifetime argument, and that they have the | |
| // same number of data fields that is not more than 1. The eventual intention is to | |
| // support multiple lifetime arguments (with the reborrowed lifetimes inferred from | |
| // usage one way or another) and multiple data fields with B allowed to leave out fields | |
| // from A. The current state is just the simplest choice. | |
| let a_lifetimes_count = 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 item = tcx.hir_expect_item(impl_did); | |
| let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = | |
| &item.kind | |
| { | |
| of_trait.trait_ref.path.span | |
| } else { | |
| tcx.def_span(impl_did) | |
| }; | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(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]; | |
| 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 | |
| } | |
| } | |
| _ => { | |
| // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument. | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })); | |
| } | |
| }; | |
| // We've proven that we have two types with one lifetime each and 0 or 1 data fields each. | |
| if let Some((source, target, trait_def_id, 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<B>`. Register an obligation | |
| // for that. | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| let cause = traits::ObligationCause::misc(span, impl_did); | |
| let obligation = Obligation::new( | |
| tcx, | |
| cause, | |
| param_env, | |
| ty::TraitRef::new(tcx, trait_def_id, [source, target]), | |
| ); | |
| ocx.register_obligation(obligation); | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { | |
| return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); | |
| } | |
| // Finally, resolve all regions. | |
| ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; | |
| } else { | |
| // Types match: check that it is Copy. | |
| assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?; | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn trait_impl_lifetime_params_count(tcx: TyCtxt<'_>, did: LocalDefId) -> usize { | |
| tcx.generics_of(did) | |
| .own_params | |
| .iter() | |
| .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) | |
| .count() | |
| } | |
| 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>( | |
| tcx: TyCtxt<'tcx>, | |
| def: ty::AdtDef<'tcx>, | |
| args: ty::GenericArgsRef<'tcx>, | |
| ) -> Vec<(Ty<'tcx>, Span)> { | |
| 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))) | |
| }) | |
| .collect() | |
| } | |
| fn assert_field_type_is_copy<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| infcx: &InferCtxt<'tcx>, | |
| impl_did: LocalDefId, | |
| param_env: ty::ParamEnv<'tcx>, | |
| ty: Ty<'tcx>, | |
| span: Span, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let copy_trait = tcx.require_lang_item(LangItem::Copy, span); | |
| let ocx = ObligationCtxt::new_with_diagnostics(infcx); | |
| let cause = traits::ObligationCause::misc(span, impl_did); | |
| let obligation = | |
| Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, copy_trait, [ty])); | |
| ocx.register_obligation(obligation); | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { | |
| Err(infcx.err_ctxt().report_fulfillment_errors(errors)) | |
| } else { | |
| Ok(()) | |
| } | |
| } | |
| pub(crate) fn coerce_unsized_info<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| impl_did: LocalDefId, | |
| ) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> { | |
| debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); | |
| let span = tcx.def_span(impl_did); | |
| let trait_name = "CoerceUnsized"; | |
| let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, span); | |
| let unsize_trait = tcx.require_lang_item(LangItem::Unsize, span); | |
| let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip(); | |
| let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip(); | |
| assert_eq!(trait_ref.def_id, coerce_unsized_trait); | |
| let target = trait_ref.args.type_at(1); | |
| debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); | |
| let param_env = tcx.param_env(impl_did); | |
| assert!(!source.has_escaping_bound_vars()); | |
| debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); | |
| let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); | |
| let cause = ObligationCause::misc(span, impl_did); | |
| let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, | |
| mt_b: ty::TypeAndMut<'tcx>, | |
| mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { | |
| if mt_a.mutbl < mt_b.mutbl { | |
| infcx | |
| .err_ctxt() | |
| .report_mismatched_types( | |
| &cause, | |
| param_env, | |
| mk_ptr(mt_b.ty), | |
| target, | |
| ty::error::TypeError::Mutability, | |
| ) | |
| .emit(); | |
| } | |
| (mt_a.ty, mt_b.ty, unsize_trait, None, span) | |
| }; | |
| let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) { | |
| (&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => { | |
| if pat_a != pat_b { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceSamePatKind { | |
| span, | |
| trait_name, | |
| pat_a: pat_a.to_string(), | |
| pat_b: pat_b.to_string(), | |
| })); | |
| } | |
| (ty_a, ty_b, coerce_unsized_trait, None, span) | |
| } | |
| (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { | |
| infcx.sub_regions( | |
| SubregionOrigin::RelateObjectBound(span), | |
| r_b, | |
| r_a, | |
| ty::VisibleForLeakCheck::Yes, | |
| ); | |
| let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; | |
| let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; | |
| check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) | |
| } | |
| (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) | |
| | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { | |
| let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; | |
| let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; | |
| check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) | |
| } | |
| (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) | |
| if def_a.is_struct() && def_b.is_struct() => | |
| { | |
| if def_a != def_b { | |
| let source_path = tcx.def_path_str(def_a.did()); | |
| let target_path = tcx.def_path_str(def_b.did()); | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceSameStruct { | |
| span, | |
| trait_name, | |
| note: true, | |
| source_path, | |
| target_path, | |
| })); | |
| } | |
| // Here we are considering a case of converting | |
| // `S<P0...Pn>` to `S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, | |
| // which acts like a pointer to `U`, but carries along some extra data of type `T`: | |
| // | |
| // struct Foo<T, U> { | |
| // extra: T, | |
| // ptr: *mut U, | |
| // } | |
| // | |
| // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized | |
| // to `Foo<T, [i32]>`. That impl would look like: | |
| // | |
| // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {} | |
| // | |
| // Here `U = [i32; 3]` and `V = [i32]`. At runtime, | |
| // when this coercion occurs, we would be changing the | |
| // field `ptr` from a thin pointer of type `*mut [i32; | |
| // 3]` to a wide pointer of type `*mut [i32]` (with | |
| // extra data `3`). **The purpose of this check is to | |
| // make sure that we know how to do this conversion.** | |
| // | |
| // To check if this impl is legal, we would walk down | |
| // the fields of `Foo` and consider their types with | |
| // both generic parameters. We are looking to find that | |
| // exactly one (non-phantom) field has changed its | |
| // type, which we will expect to be the pointer that | |
| // is becoming fat (we could probably generalize this | |
| // to multiple thin pointers of the same type becoming | |
| // fat, but we don't). In this case: | |
| // | |
| // - `extra` has type `T` before and type `T` after | |
| // - `ptr` has type `*mut U` before and type `*mut V` after | |
| // | |
| // Since just one field changed, we would then check | |
| // that `*mut U: CoerceUnsized<*mut V>` is implemented | |
| // (in other words, that we know how to do this | |
| // conversion). This will work out because `U: | |
| // Unsize<V>`, and we have a builtin rule that `*mut | |
| // U` can be coerced to `*mut V` if `U: Unsize<V>`. | |
| let fields = &def_a.non_enum_variant().fields; | |
| let diff_fields = fields | |
| .iter_enumerated() | |
| .filter_map(|(i, f)| { | |
| let (a, b) = | |
| (f.ty(tcx, args_a).skip_norm_wip(), f.ty(tcx, args_b).skip_norm_wip()); | |
| // Ignore PhantomData fields | |
| let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); | |
| if tcx | |
| .try_normalize_erasing_regions( | |
| ty::TypingEnv::non_body_analysis(tcx, def_a.did()), | |
| unnormalized_ty, | |
| ) | |
| .unwrap_or(unnormalized_ty.skip_norm_wip()) | |
| .is_phantom_data() | |
| { | |
| return None; | |
| } | |
| // Ignore fields that aren't changed; it may | |
| // be that we could get away with subtyping or | |
| // something more accepting, but we use | |
| // equality because we want to be able to | |
| // perform this check without computing | |
| // variance or constraining opaque types' hidden types. | |
| // (This is because we may have to evaluate constraint | |
| // expressions in the course of execution.) | |
| // See e.g., #41936. | |
| if a == b { | |
| return None; | |
| } | |
| // Collect up all fields that were significantly changed | |
| // i.e., those that contain T in coerce_unsized T -> U | |
| Some((i, a, b, tcx.def_span(f.did))) | |
| }) | |
| .collect::<Vec<_>>(); | |
| if diff_fields.is_empty() { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceNoField { | |
| span, | |
| trait_name, | |
| note: true, | |
| })); | |
| } else if diff_fields.len() > 1 { | |
| let item = tcx.hir_expect_item(impl_did); | |
| let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = | |
| &item.kind | |
| { | |
| of_trait.trait_ref.path.span | |
| } else { | |
| tcx.def_span(impl_did) | |
| }; | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceMulti { | |
| span, | |
| trait_name, | |
| number: diff_fields.len(), | |
| fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(), | |
| })); | |
| } | |
| let (i, a, b, field_span) = diff_fields[0]; | |
| let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); | |
| (a, b, coerce_unsized_trait, Some(kind), field_span) | |
| } | |
| _ => { | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoerceUnsizedNonStruct { span, trait_name })); | |
| } | |
| }; | |
| // Register an obligation for `A: Trait<B>`. | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| let cause = traits::ObligationCause::misc(span, impl_did); | |
| let obligation = Obligation::new( | |
| tcx, | |
| cause, | |
| param_env, | |
| ty::TraitRef::new(tcx, trait_def_id, [source, target]), | |
| ); | |
| ocx.register_obligation(obligation); | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { | |
| if is_from_coerce_pointee_derive(tcx, span) { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoerceFieldValidity { | |
| span, | |
| trait_name, | |
| ty: trait_ref.self_ty(), | |
| field_span, | |
| field_ty: source, | |
| })); | |
| } else { | |
| return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); | |
| } | |
| } | |
| // Finally, resolve all regions. | |
| ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?; | |
| Ok(CoerceUnsizedInfo { custom_kind: kind }) | |
| } | |
| fn infringing_fields_error<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| infringing_tys: impl Iterator<Item = (Span, Ty<'tcx>, InfringingFieldsReason<'tcx>)>, | |
| lang_item: LangItem, | |
| impl_did: LocalDefId, | |
| impl_span: Span, | |
| ) -> ErrorGuaranteed { | |
| let trait_did = tcx.require_lang_item(lang_item, impl_span); | |
| let trait_name = tcx.def_path_str(trait_did); | |
| // We'll try to suggest constraining type parameters to fulfill the requirements of | |
| // their `Copy` implementation. | |
| let mut errors: BTreeMap<_, Vec<_>> = Default::default(); | |
| let mut bounds = vec![]; | |
| let mut seen_tys = FxHashSet::default(); | |
| let mut label_spans = Vec::new(); | |
| for (span, ty, reason) in infringing_tys { | |
| // Only report an error once per type. | |
| if !seen_tys.insert(ty) { | |
| continue; | |
| } | |
| label_spans.push(span); | |
| match reason { | |
| InfringingFieldsReason::Fulfill(fulfillment_errors) => { | |
| for error in fulfillment_errors { | |
| let error_predicate = error.obligation.predicate; | |
| // Only note if it's not the root obligation, otherwise it's trivial and | |
| // should be self-explanatory (i.e. a field literally doesn't implement Copy). | |
| // FIXME: This error could be more descriptive, especially if the error_predicate | |
| // contains a foreign type or if it's a deeply nested type... | |
| if error_predicate != error.root_obligation.predicate { | |
| errors | |
| .entry((ty.to_string(), error_predicate.to_string())) | |
| .or_default() | |
| .push(error.obligation.cause.span); | |
| } | |
| if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { | |
| trait_ref, | |
| polarity: ty::PredicatePolarity::Positive, | |
| .. | |
| })) = error_predicate.kind().skip_binder() | |
| { | |
| let ty = trait_ref.self_ty(); | |
| if let ty::Param(_) = ty.kind() { | |
| bounds.push(( | |
| format!("{ty}"), | |
| trait_ref.print_trait_sugared().to_string(), | |
| Some(trait_ref.def_id), | |
| )); | |
| } | |
| } | |
| } | |
| } | |
| InfringingFieldsReason::Regions(region_errors) => { | |
| for error in region_errors { | |
| let ty = ty.to_string(); | |
| match error { | |
| RegionResolutionError::ConcreteFailure(origin, a, b) => { | |
| let predicate = format!("{b}: {a}"); | |
| errors | |
| .entry((ty.clone(), predicate.clone())) | |
| .or_default() | |
| .push(origin.span()); | |
| if let ty::RegionKind::ReEarlyParam(ebr) = b.kind() | |
| && ebr.is_named() | |
| { | |
| bounds.push((b.to_string(), a.to_string(), None)); | |
| } | |
| } | |
| RegionResolutionError::GenericBoundFailure(origin, a, b) => { | |
| let predicate = format!("{a}: {b}"); | |
| errors | |
| .entry((ty.clone(), predicate.clone())) | |
| .or_default() | |
| .push(origin.span()); | |
| if let infer::region_constraints::GenericKind::Param(_) = a { | |
| bounds.push((a.to_string(), b.to_string(), None)); | |
| } | |
| } | |
| _ => continue, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| let mut notes = Vec::new(); | |
| for ((ty, error_predicate), spans) in errors { | |
| let span: MultiSpan = spans.into(); | |
| notes.push(diagnostics::ImplForTyRequires { | |
| span, | |
| error_predicate, | |
| trait_name: trait_name.clone(), | |
| ty, | |
| }); | |
| } | |
| let mut err = tcx.dcx().create_err(diagnostics::TraitCannotImplForTy { | |
| span: impl_span, | |
| trait_name, | |
| label_spans, | |
| notes, | |
| }); | |
| suggest_constraining_type_params( | |
| tcx, | |
| tcx.hir_get_generics(impl_did).expect("impls always have generics"), | |
| &mut err, | |
| bounds | |
| .iter() | |
| .map(|(param, constraint, def_id)| (param.as_str(), constraint.as_str(), *def_id)), | |
| None, | |
| ); | |
| err.emit() | |
| } | |
| fn visit_implementation_of_coerce_pointee_validity( | |
| checker: &Checker<'_>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let tcx = checker.tcx; | |
| let self_ty = | |
| tcx.impl_trait_ref(checker.impl_def_id).instantiate_identity().skip_norm_wip().self_ty(); | |
| let span = tcx.def_span(checker.impl_def_id); | |
| if !tcx.is_builtin_derived(checker.impl_def_id.into()) { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoercePointeeNoUserValidityAssertion { span })); | |
| } | |
| let ty::Adt(def, _args) = self_ty.kind() else { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoercePointeeNotConcreteType { span })); | |
| }; | |
| let did = def.did(); | |
| // Now get a more precise span of the `struct`. | |
| let span = tcx.def_span(did); | |
| if !def.is_struct() { | |
| return Err(tcx | |
| .dcx() | |
| .emit_err(diagnostics::CoercePointeeNotStruct { span, kind: def.descr().into() })); | |
| } | |
| if !def.repr().transparent() { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoercePointeeNotTransparent { span })); | |
| } | |
| if def.all_fields().next().is_none() { | |
| return Err(tcx.dcx().emit_err(diagnostics::CoercePointeeNoField { span })); | |
| } | |
| Ok(()) | |
| } |