compiler/rustc_hir_analysis/src/coherence/builtin.rs RUST 1,177 lines View on github.com → Search inside
1//! Check properties that are required by built-in traits and set2//! up data structures required by type-checking/codegen.34use std::collections::BTreeMap;56use rustc_data_structures::fx::FxHashSet;7use rustc_errors::{ErrorGuaranteed, MultiSpan};8use rustc_hir as hir;9use rustc_hir::ItemKind;10use rustc_hir::def_id::{DefId, LocalDefId};11use rustc_hir::lang_items::LangItem;12use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt};13use rustc_infer::traits::{Obligation, PredicateObligations};14use rustc_middle::ty::adjustment::CoerceUnsizedInfo;15use rustc_middle::ty::print::PrintTraitRefExt as _;16use rustc_middle::ty::relate::solver_relating::RelateExt;17use rustc_middle::ty::{18    self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, suggest_constraining_type_params,19};20use rustc_span::{DUMMY_SP, Span, sym};21use rustc_trait_selection::error_reporting::InferCtxtErrorExt;22use rustc_trait_selection::traits::misc::{23    ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,24    type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,25};26use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCause, ObligationCtxt};27use tracing::debug;2829use crate::errors;3031pub(super) fn check_trait<'tcx>(32    tcx: TyCtxt<'tcx>,33    trait_def_id: DefId,34    impl_def_id: LocalDefId,35    impl_header: ty::ImplTraitHeader<'tcx>,36) -> Result<(), ErrorGuaranteed> {37    let lang_items = tcx.lang_items();38    let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };39    checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;40    checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?;41    checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;42    checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?;43    checker.check(lang_items.const_param_ty_trait(), |checker| {44        visit_implementation_of_const_param_ty(checker)45    })?;46    checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?;47    checker.check(lang_items.reborrow(), visit_implementation_of_reborrow)?;48    checker.check(lang_items.coerce_shared(), visit_implementation_of_coerce_shared)?;49    checker50        .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;51    checker.check(52        lang_items.coerce_pointee_validated_trait(),53        visit_implementation_of_coerce_pointee_validity,54    )?;55    Ok(())56}5758struct Checker<'tcx> {59    tcx: TyCtxt<'tcx>,60    trait_def_id: DefId,61    impl_def_id: LocalDefId,62    impl_header: ty::ImplTraitHeader<'tcx>,63}6465impl<'tcx> Checker<'tcx> {66    fn check(67        &self,68        trait_def_id: Option<DefId>,69        f: impl FnOnce(&Self) -> Result<(), ErrorGuaranteed>,70    ) -> Result<(), ErrorGuaranteed> {71        if Some(self.trait_def_id) == trait_def_id { f(self) } else { Ok(()) }72    }73}7475fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {76    let tcx = checker.tcx;77    let impl_did = checker.impl_def_id;78    // Destructors only work on local ADT types.79    match checker.impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty().kind() {80        ty::Adt(def, _) if def.did().is_local() => return Ok(()),81        ty::Error(_) => return Ok(()),82        _ => {}83    }8485    let impl_ = tcx.hir_expect_item(impl_did).expect_impl();8687    Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem {88        span: impl_.self_ty.span,89        trait_: tcx.item_name(checker.impl_header.trait_ref.skip_binder().def_id),90    }))91}9293fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {94    let tcx = checker.tcx;95    let impl_header = checker.impl_header;96    let impl_did = checker.impl_def_id;97    debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);9899    let self_type = impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty();100    debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);101102    let param_env = tcx.param_env(impl_did);103    assert!(!self_type.has_escaping_bound_vars());104105    debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);106107    if let ty::ImplPolarity::Negative = impl_header.polarity {108        return Ok(());109    }110111    let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);112    match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) {113        Ok(()) => Ok(()),114        Err(CopyImplementationError::InfringingFields(fields)) => {115            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;116            Err(infringing_fields_error(117                tcx,118                fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),119                LangItem::Copy,120                impl_did,121                span,122            ))123        }124        Err(CopyImplementationError::NotAnAdt) => {125            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;126            Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))127        }128        Err(CopyImplementationError::HasDestructor(did)) => {129            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;130            let impl_ = tcx.def_span(did);131            Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span, impl_ }))132        }133        Err(CopyImplementationError::HasUnsafeFields) => {134            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;135            Err(tcx136                .dcx()137                .span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type)))138        }139    }140}141142fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {143    let tcx = checker.tcx;144    let impl_header = checker.impl_header;145    let impl_did = checker.impl_def_id;146    debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did);147148    let self_type = impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty();149    debug!("visit_implementation_of_unpin: self_type={:?}", self_type);150151    let span = tcx.def_span(impl_did);152153    if tcx.features().pin_ergonomics() {154        match self_type.kind() {155            // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project156            // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`).157            // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait,158            // which cannot carry safety properties), then `&mut U` could be obtained from159            // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of160            // `Pin<&mut U>` for `U: !Unpin`.161            ty::Adt(adt, _) if adt.is_pin_project() => {162                return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType {163                    span,164                    adt_span: tcx.def_span(adt.did()),165                    adt_name: tcx.item_name(adt.did()),166                }));167            }168            ty::Adt(_, _) => {}169            _ => {170                return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type"));171            }172        };173    }174    Ok(())175}176177fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {178    let tcx = checker.tcx;179    let header = checker.impl_header;180    let impl_did = checker.impl_def_id;181    let self_type = header.trait_ref.instantiate_identity().skip_norm_wip().self_ty();182    assert!(!self_type.has_escaping_bound_vars());183184    let param_env = tcx.param_env(impl_did);185186    if let ty::ImplPolarity::Negative | ty::ImplPolarity::Reservation = header.polarity {187        return Ok(());188    }189190    if tcx.features().const_param_ty_unchecked() {191        return Ok(());192    }193194    if !tcx.features().adt_const_params() {195        match *self_type.kind() {196            ty::Adt(adt, _) if adt.is_struct() => {197                let struct_vis = tcx.visibility(adt.did());198                for variant in adt.variants() {199                    for field in &variant.fields {200                        if struct_vis.greater_than(field.vis, tcx) {201                            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;202                            return Err(tcx203                                .dcx()204                                .emit_err(errors::ConstParamTyFieldVisMismatch { span }));205                        }206                    }207                }208            }209210            _ => {}211        }212    }213214    let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);215    match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) {216        Ok(()) => Ok(()),217        Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {218            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;219            Err(infringing_fields_error(220                tcx,221                fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),222                LangItem::ConstParamTy,223                impl_did,224                span,225            ))226        }227        Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {228            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;229            Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }))230        }231        Err(ConstParamTyImplementationError::NonExhaustive(attr_span)) => {232            let defn_span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;233            Err(tcx234                .dcx()235                .emit_err(errors::ConstParamTyImplOnNonExhaustive { defn_span, attr_span }))236        }237        Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => {238            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;239            Err(infringing_fields_error(240                tcx,241                infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)),242                LangItem::ConstParamTy,243                impl_did,244                span,245            ))246        }247        Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => {248            let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;249            Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnUnsized { span }))250        }251    }252}253254fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {255    let tcx = checker.tcx;256    let impl_did = checker.impl_def_id;257    debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);258259    // Just compute this for the side-effects, in particular reporting260    // errors; other parts of the code may demand it for the info of261    // course.262    tcx.ensure_result().coerce_unsized_info(impl_did)263}264265fn visit_implementation_of_reborrow(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {266    let tcx = checker.tcx;267    let impl_did = checker.impl_def_id;268    debug!("visit_implementation_of_reborrow: impl_did={:?}", impl_did);269270    // Just compute this for the side-effects, in particular reporting271    // errors; other parts of the code may demand it for the info of272    // course.273    reborrow_info(tcx, impl_did)274}275276fn visit_implementation_of_coerce_shared(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {277    let tcx = checker.tcx;278    let impl_did = checker.impl_def_id;279    debug!("visit_implementation_of_coerce_shared: impl_did={:?}", impl_did);280281    // Just compute this for the side-effects, in particular reporting282    // errors; other parts of the code may demand it for the info of283    // course.284    coerce_shared_info(tcx, impl_did)285}286287fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool {288    span.ctxt()289        .outer_expn_data()290        .macro_def_id291        .is_some_and(|def_id| tcx.is_diagnostic_item(sym::CoercePointee, def_id))292}293294fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {295    let tcx = checker.tcx;296    let impl_did = checker.impl_def_id;297    let trait_ref = checker.impl_header.trait_ref.instantiate_identity().skip_norm_wip();298    debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);299300    let span = tcx.def_span(impl_did);301    let trait_name = "DispatchFromDyn";302303    let source = trait_ref.self_ty();304    let target = {305        assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn));306307        trait_ref.args.type_at(1)308    };309310    // Check `CoercePointee` impl is WF -- if not, then there's no reason to report311    // redundant errors for `DispatchFromDyn`. This is best effort, though.312    let mut res = Ok(());313    tcx.for_each_relevant_impl(314        tcx.require_lang_item(LangItem::CoerceUnsized, span),315        source,316        |impl_def_id| {317            res = res.and(tcx.ensure_result().coerce_unsized_info(impl_def_id));318        },319    );320    res?;321322    debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);323324    let param_env = tcx.param_env(impl_did);325326    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());327    let cause = ObligationCause::misc(span, impl_did);328329    // Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw330    // pointers. This is enforced here: we only allow impls for references, raw pointers, and things331    // that are effectively repr(transparent) newtypes around types that already hav a332    // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those types since some333    // of them support an allocator, but we ensure that for the cases where the type implements this334    // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else335    // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)336    // even if they do not carry that attribute.337    match (source.kind(), target.kind()) {338        (&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {339            if pat_a != pat_b {340                return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {341                    span,342                    trait_name,343                    pat_a: pat_a.to_string(),344                    pat_b: pat_b.to_string(),345                }));346            }347            Ok(())348        }349350        (&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))351            if r_a == *r_b && mutbl_a == *mutbl_b =>352        {353            Ok(())354        }355        (&ty::RawPtr(_, a_mutbl), &ty::RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()),356        (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))357            if def_a.is_struct() && def_b.is_struct() =>358        {359            if def_a != def_b {360                let source_path = tcx.def_path_str(def_a.did());361                let target_path = tcx.def_path_str(def_b.did());362                return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {363                    span,364                    trait_name,365                    note: true,366                    source_path,367                    target_path,368                }));369            }370371            if def_a.repr().c() || def_a.repr().packed() {372                return Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));373            }374375            let fields = &def_a.non_enum_variant().fields;376377            let mut res = Ok(());378            let coerced_fields = fields379                .iter_enumerated()380                .filter_map(|(i, field)| {381                    // Ignore PhantomData fields382                    let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();383                    if tcx384                        .try_normalize_erasing_regions(385                            ty::TypingEnv::non_body_analysis(tcx, def_a.did()),386                            unnormalized_ty,387                        )388                        .unwrap_or(unnormalized_ty.skip_norm_wip())389                        .is_phantom_data()390                    {391                        return None;392                    }393394                    let ty_a = field.ty(tcx, args_a);395                    let ty_b = field.ty(tcx, args_b);396397                    // FIXME: We could do normalization here, but is it really worth it?398                    if ty_a == ty_b {399                        // Allow 1-ZSTs that don't mention type params.400                        //401                        // Allowing type params here would allow us to possibly transmute402                        // between ZSTs, which may be used to create library unsoundness.403                        if let Ok(layout) =404                            tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a))405                            && layout.is_1zst()406                            && !ty_a.has_non_region_param()407                        {408                            // ignore 1-ZST fields409                            return None;410                        }411412                        res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {413                            span,414                            name: field.ident(tcx),415                            ty: ty_a,416                        }));417418                        None419                    } else {420                        Some((i, ty_a, ty_b, tcx.def_span(field.did)))421                    }422                })423                .collect::<Vec<_>>();424            res?;425426            if coerced_fields.is_empty() {427                return Err(tcx.dcx().emit_err(errors::CoerceNoField {428                    span,429                    trait_name,430                    note: true,431                }));432            } else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {433                let ocx = ObligationCtxt::new_with_diagnostics(&infcx);434                ocx.register_obligation(Obligation::new(435                    tcx,436                    cause.clone(),437                    param_env,438                    ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]),439                ));440                let errors = ocx.evaluate_obligations_error_on_ambiguity();441                if !errors.is_empty() {442                    if is_from_coerce_pointee_derive(tcx, span) {443                        return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {444                            span,445                            trait_name,446                            ty: trait_ref.self_ty(),447                            field_span,448                            field_ty: ty_a,449                        }));450                    } else {451                        return Err(infcx.err_ctxt().report_fulfillment_errors(errors));452                    }453                }454455                // Finally, resolve all regions.456                ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;457458                Ok(())459            } else {460                return Err(tcx.dcx().emit_err(errors::CoerceMulti {461                    span,462                    trait_name,463                    number: coerced_fields.len(),464                    fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),465                }));466            }467        }468        _ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),469    }470}471472fn structurally_normalize_ty<'tcx>(473    tcx: TyCtxt<'tcx>,474    infcx: &InferCtxt<'tcx>,475    impl_did: LocalDefId,476    span: Span,477    ty: Unnormalized<'tcx, Ty<'tcx>>,478) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {479    let ocx = ObligationCtxt::new(infcx);480    let Ok(normalized_ty) = ocx.structurally_normalize_ty(481        &traits::ObligationCause::misc(span, impl_did),482        tcx.param_env(impl_did),483        ty,484    ) else {485        // We shouldn't have errors here in the old solver, except for486        // evaluate/fulfill mismatches, but that's not a reason for an ICE.487        return None;488    };489    let errors = ocx.try_evaluate_obligations();490    if !errors.is_empty() {491        if infcx.next_trait_solver() {492            unreachable!();493        }494        // We shouldn't have errors here in the old solver, except for495        // evaluate/fulfill mismatches, but that's not a reason for an ICE.496        debug!(?errors, "encountered errors while fulfilling");497        return None;498    }499500    Some((normalized_ty, ocx.into_pending_obligations()))501}502503pub(crate) fn reborrow_info<'tcx>(504    tcx: TyCtxt<'tcx>,505    impl_did: LocalDefId,506) -> Result<(), ErrorGuaranteed> {507    debug!("compute_reborrow_info(impl_did={:?})", impl_did);508    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());509    let span = tcx.def_span(impl_did);510    let trait_name = "Reborrow";511512    let reborrow_trait = tcx.require_lang_item(LangItem::Reborrow, span);513514    let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip();515    let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip();516517    if trait_impl_lifetime_params_count(tcx, impl_did) != 1 {518        return Err(tcx519            .dcx()520            .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name }));521    }522523    assert_eq!(trait_ref.def_id, reborrow_trait);524    let param_env = tcx.param_env(impl_did);525    assert!(!source.has_escaping_bound_vars());526527    let (def, args) = match source.kind() {528        &ty::Adt(def, args) if def.is_struct() => (def, args),529        _ => {530            // Note: reusing error here as it takes trait_name as argument.531            return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));532        }533    };534535    let lifetimes_count = generic_lifetime_params_count(args);536    let data_fields = collect_struct_data_fields(tcx, def, args);537538    if lifetimes_count != 1 {539        let item = tcx.hir_expect_item(impl_did);540        let _span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = &item.kind {541            of_trait.trait_ref.path.span542        } else {543            tcx.def_span(impl_did)544        };545546        return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name }));547    }548549    if data_fields.is_empty() {550        return Ok(());551    }552553    // We've found some data fields. They must all be either be Copy or Reborrow.554    for (field, span) in data_fields {555        if assert_field_type_is_reborrow(556            tcx,557            &infcx,558            reborrow_trait,559            impl_did,560            param_env,561            field,562            span,563        )564        .is_ok()565        {566            // Field implements Reborrow.567            return Ok(());568        }569570        // Field does not implement Reborrow: it must be Copy.571        assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, field, span)?;572    }573574    Ok(())575}576577fn assert_field_type_is_reborrow<'tcx>(578    tcx: TyCtxt<'tcx>,579    infcx: &InferCtxt<'tcx>,580    reborrow_trait: DefId,581    impl_did: LocalDefId,582    param_env: ty::ParamEnv<'tcx>,583    ty: Ty<'tcx>,584    span: Span,585) -> Result<(), Vec<FulfillmentError<'tcx>>> {586    if ty.ref_mutability() == Some(ty::Mutability::Mut) {587        // Mutable references are Reborrow but not really.588        return Ok(());589    }590    let ocx = ObligationCtxt::new_with_diagnostics(infcx);591    let cause = traits::ObligationCause::misc(span, impl_did);592    let obligation =593        Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, reborrow_trait, [ty]));594    ocx.register_obligation(obligation);595    let errors = ocx.evaluate_obligations_error_on_ambiguity();596597    if !errors.is_empty() { Err(errors) } else { Ok(()) }598}599600pub(crate) fn coerce_shared_info<'tcx>(601    tcx: TyCtxt<'tcx>,602    impl_did: LocalDefId,603) -> Result<(), ErrorGuaranteed> {604    debug!("compute_coerce_shared_info(impl_did={:?})", impl_did);605    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());606    let span = tcx.def_span(impl_did);607    let trait_name = "CoerceShared";608609    let coerce_shared_trait = tcx.require_lang_item(LangItem::CoerceShared, span);610611    let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip();612    let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip();613614    if trait_impl_lifetime_params_count(tcx, impl_did) != 1 {615        return Err(tcx616            .dcx()617            .emit_err(errors::CoerceSharedNotSingleLifetimeParam { span, trait_name }));618    }619620    assert_eq!(trait_ref.def_id, coerce_shared_trait);621    let Some((target, _obligations)) = structurally_normalize_ty(622        tcx,623        &infcx,624        impl_did,625        span,626        Unnormalized::new_wip(trait_ref.args.type_at(1)),627    ) else {628        todo!("something went wrong with structurally_normalize_ty");629    };630631    let param_env = tcx.param_env(impl_did);632    assert!(!source.has_escaping_bound_vars());633634    let data = match (source.kind(), target.kind()) {635        (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))636            if def_a.is_struct() && def_b.is_struct() =>637        {638            // Check that both A and B have exactly one lifetime argument, and that they have the639            // same number of data fields that is not more than 1. The eventual intention is to640            // support multiple lifetime arguments (with the reborrowed lifetimes inferred from641            // usage one way or another) and multiple data fields with B allowed to leave out fields642            // from A. The current state is just the simplest choice.643            let a_lifetimes_count = generic_lifetime_params_count(args_a);644            let a_data_fields = collect_struct_data_fields(tcx, def_a, args_a);645            let b_lifetimes_count = generic_lifetime_params_count(args_b);646            let b_data_fields = collect_struct_data_fields(tcx, def_b, args_b);647648            if a_lifetimes_count != 1649                || b_lifetimes_count != 1650                || a_data_fields.len() > 1651                || b_data_fields.len() > 1652                || a_data_fields.len() != b_data_fields.len()653            {654                let item = tcx.hir_expect_item(impl_did);655                let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) =656                    &item.kind657                {658                    of_trait.trait_ref.path.span659                } else {660                    tcx.def_span(impl_did)661                };662663                return Err(tcx.dcx().emit_err(errors::CoerceSharedMulti { span, trait_name }));664            }665666            if a_data_fields.len() == 1 {667                // We found one data field for both: we'll attempt to perform CoerceShared between668                // them below.669                let (a, span_a) = a_data_fields[0];670                let (b, span_b) = b_data_fields[0];671672                Some((a, b, coerce_shared_trait, span_a, span_b))673            } else {674                // We found no data fields in either: this is a reborrowable marker type being675                // coerced into a shared marker. That is fine too.676                None677            }678        }679680        _ => {681            // Note: reusing CoerceUnsizedNonStruct error as it takes trait_name as argument.682            return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));683        }684    };685686    // We've proven that we have two types with one lifetime each and 0 or 1 data fields each.687    if let Some((source, target, trait_def_id, source_field_span, _target_field_span)) = data {688        // struct Source(SourceData);689        // struct Target(TargetData);690        //691        // 1 data field each; they must be the same type and Copy, or relate to one another using692        // CoerceShared.693        if source.ref_mutability() == Some(ty::Mutability::Mut)694            && target.ref_mutability() == Some(ty::Mutability::Not)695            && infcx696                .eq_structurally_relating_aliases(697                    param_env,698                    source.peel_refs(),699                    target.peel_refs(),700                    source_field_span,701                )702                .is_ok()703        {704            // &mut T implements CoerceShared to &T, except not really.705            return Ok(());706        }707        if infcx708            .eq_structurally_relating_aliases(param_env, source, target, source_field_span)709            .is_err()710        {711            // The two data fields don't agree on a common type; this means712            // that they must be `A: CoerceShared<B>`. Register an obligation713            // for that.714            let ocx = ObligationCtxt::new_with_diagnostics(&infcx);715            let cause = traits::ObligationCause::misc(span, impl_did);716            let obligation = Obligation::new(717                tcx,718                cause,719                param_env,720                ty::TraitRef::new(tcx, trait_def_id, [source, target]),721            );722            ocx.register_obligation(obligation);723            let errors = ocx.evaluate_obligations_error_on_ambiguity();724725            if !errors.is_empty() {726                return Err(infcx.err_ctxt().report_fulfillment_errors(errors));727            }728            // Finally, resolve all regions.729            ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;730        } else {731            // Types match: check that it is Copy.732            assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?;733        }734    }735736    Ok(())737}738739fn trait_impl_lifetime_params_count(tcx: TyCtxt<'_>, did: LocalDefId) -> usize {740    tcx.generics_of(did)741        .own_params742        .iter()743        .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))744        .count()745}746747fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize {748    args.iter().filter(|arg| arg.as_region().is_some()).count()749}750751fn collect_struct_data_fields<'tcx>(752    tcx: TyCtxt<'tcx>,753    def: ty::AdtDef<'tcx>,754    args: ty::GenericArgsRef<'tcx>,755) -> Vec<(Ty<'tcx>, Span)> {756    def.non_enum_variant()757        .fields758        .iter()759        .filter_map(|f| {760            // Ignore PhantomData fields761            let ty = f.ty(tcx, args);762            if ty.is_phantom_data() {763                return None;764            }765            Some((ty, tcx.def_span(f.did)))766        })767        .collect()768}769770fn assert_field_type_is_copy<'tcx>(771    tcx: TyCtxt<'tcx>,772    infcx: &InferCtxt<'tcx>,773    impl_did: LocalDefId,774    param_env: ty::ParamEnv<'tcx>,775    ty: Ty<'tcx>,776    span: Span,777) -> Result<(), ErrorGuaranteed> {778    let copy_trait = tcx.require_lang_item(LangItem::Copy, span);779    let ocx = ObligationCtxt::new_with_diagnostics(infcx);780    let cause = traits::ObligationCause::misc(span, impl_did);781    let obligation =782        Obligation::new(tcx, cause, param_env, ty::TraitRef::new(tcx, copy_trait, [ty]));783    ocx.register_obligation(obligation);784    let errors = ocx.evaluate_obligations_error_on_ambiguity();785786    if !errors.is_empty() {787        Err(infcx.err_ctxt().report_fulfillment_errors(errors))788    } else {789        Ok(())790    }791}792793pub(crate) fn coerce_unsized_info<'tcx>(794    tcx: TyCtxt<'tcx>,795    impl_did: LocalDefId,796) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> {797    debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);798    let span = tcx.def_span(impl_did);799    let trait_name = "CoerceUnsized";800801    let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, span);802    let unsize_trait = tcx.require_lang_item(LangItem::Unsize, span);803804    let source = tcx.type_of(impl_did).instantiate_identity().skip_norm_wip();805    let trait_ref = tcx.impl_trait_ref(impl_did).instantiate_identity().skip_norm_wip();806807    assert_eq!(trait_ref.def_id, coerce_unsized_trait);808    let target = trait_ref.args.type_at(1);809    debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);810811    let param_env = tcx.param_env(impl_did);812    assert!(!source.has_escaping_bound_vars());813814    debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);815816    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());817    let cause = ObligationCause::misc(span, impl_did);818    let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,819                       mt_b: ty::TypeAndMut<'tcx>,820                       mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {821        if mt_a.mutbl < mt_b.mutbl {822            infcx823                .err_ctxt()824                .report_mismatched_types(825                    &cause,826                    param_env,827                    mk_ptr(mt_b.ty),828                    target,829                    ty::error::TypeError::Mutability,830                )831                .emit();832        }833        (mt_a.ty, mt_b.ty, unsize_trait, None, span)834    };835    let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {836        (&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {837            if pat_a != pat_b {838                return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {839                    span,840                    trait_name,841                    pat_a: pat_a.to_string(),842                    pat_b: pat_b.to_string(),843                }));844            }845            (ty_a, ty_b, coerce_unsized_trait, None, span)846        }847848        (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {849            infcx.sub_regions(850                SubregionOrigin::RelateObjectBound(span),851                r_b,852                r_a,853                ty::VisibleForLeakCheck::Yes,854            );855            let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };856            let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };857            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty))858        }859860        (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b))861        | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => {862            let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };863            let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };864            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))865        }866867        (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))868            if def_a.is_struct() && def_b.is_struct() =>869        {870            if def_a != def_b {871                let source_path = tcx.def_path_str(def_a.did());872                let target_path = tcx.def_path_str(def_b.did());873                return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {874                    span,875                    trait_name,876                    note: true,877                    source_path,878                    target_path,879                }));880            }881882            // Here we are considering a case of converting883            // `S<P0...Pn>` to `S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,884            // which acts like a pointer to `U`, but carries along some extra data of type `T`:885            //886            //     struct Foo<T, U> {887            //         extra: T,888            //         ptr: *mut U,889            //     }890            //891            // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized892            // to `Foo<T, [i32]>`. That impl would look like:893            //894            //   impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}895            //896            // Here `U = [i32; 3]` and `V = [i32]`. At runtime,897            // when this coercion occurs, we would be changing the898            // field `ptr` from a thin pointer of type `*mut [i32;899            // 3]` to a wide pointer of type `*mut [i32]` (with900            // extra data `3`). **The purpose of this check is to901            // make sure that we know how to do this conversion.**902            //903            // To check if this impl is legal, we would walk down904            // the fields of `Foo` and consider their types with905            // both generic parameters. We are looking to find that906            // exactly one (non-phantom) field has changed its907            // type, which we will expect to be the pointer that908            // is becoming fat (we could probably generalize this909            // to multiple thin pointers of the same type becoming910            // fat, but we don't). In this case:911            //912            // - `extra` has type `T` before and type `T` after913            // - `ptr` has type `*mut U` before and type `*mut V` after914            //915            // Since just one field changed, we would then check916            // that `*mut U: CoerceUnsized<*mut V>` is implemented917            // (in other words, that we know how to do this918            // conversion). This will work out because `U:919            // Unsize<V>`, and we have a builtin rule that `*mut920            // U` can be coerced to `*mut V` if `U: Unsize<V>`.921            let fields = &def_a.non_enum_variant().fields;922            let diff_fields = fields923                .iter_enumerated()924                .filter_map(|(i, f)| {925                    let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b));926927                    // Ignore PhantomData fields928                    let unnormalized_ty = tcx.type_of(f.did).instantiate_identity();929                    if tcx930                        .try_normalize_erasing_regions(931                            ty::TypingEnv::non_body_analysis(tcx, def_a.did()),932                            unnormalized_ty,933                        )934                        .unwrap_or(unnormalized_ty.skip_norm_wip())935                        .is_phantom_data()936                    {937                        return None;938                    }939940                    // Ignore fields that aren't changed; it may941                    // be that we could get away with subtyping or942                    // something more accepting, but we use943                    // equality because we want to be able to944                    // perform this check without computing945                    // variance or constraining opaque types' hidden types.946                    // (This is because we may have to evaluate constraint947                    // expressions in the course of execution.)948                    // See e.g., #41936.949                    if a == b {950                        return None;951                    }952953                    // Collect up all fields that were significantly changed954                    // i.e., those that contain T in coerce_unsized T -> U955                    Some((i, a, b, tcx.def_span(f.did)))956                })957                .collect::<Vec<_>>();958959            if diff_fields.is_empty() {960                return Err(tcx.dcx().emit_err(errors::CoerceNoField {961                    span,962                    trait_name,963                    note: true,964                }));965            } else if diff_fields.len() > 1 {966                let item = tcx.hir_expect_item(impl_did);967                let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) =968                    &item.kind969                {970                    of_trait.trait_ref.path.span971                } else {972                    tcx.def_span(impl_did)973                };974975                return Err(tcx.dcx().emit_err(errors::CoerceMulti {976                    span,977                    trait_name,978                    number: diff_fields.len(),979                    fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),980                }));981            }982983            let (i, a, b, field_span) = diff_fields[0];984            let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);985            (a, b, coerce_unsized_trait, Some(kind), field_span)986        }987988        _ => {989            return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));990        }991    };992993    // Register an obligation for `A: Trait<B>`.994    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);995    let cause = traits::ObligationCause::misc(span, impl_did);996    let obligation = Obligation::new(997        tcx,998        cause,999        param_env,1000        ty::TraitRef::new(tcx, trait_def_id, [source, target]),1001    );1002    ocx.register_obligation(obligation);1003    let errors = ocx.evaluate_obligations_error_on_ambiguity();10041005    if !errors.is_empty() {1006        if is_from_coerce_pointee_derive(tcx, span) {1007            return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {1008                span,1009                trait_name,1010                ty: trait_ref.self_ty(),1011                field_span,1012                field_ty: source,1013            }));1014        } else {1015            return Err(infcx.err_ctxt().report_fulfillment_errors(errors));1016        }1017    }10181019    // Finally, resolve all regions.1020    ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;10211022    Ok(CoerceUnsizedInfo { custom_kind: kind })1023}10241025fn infringing_fields_error<'tcx>(1026    tcx: TyCtxt<'tcx>,1027    infringing_tys: impl Iterator<Item = (Span, Ty<'tcx>, InfringingFieldsReason<'tcx>)>,1028    lang_item: LangItem,1029    impl_did: LocalDefId,1030    impl_span: Span,1031) -> ErrorGuaranteed {1032    let trait_did = tcx.require_lang_item(lang_item, impl_span);10331034    let trait_name = tcx.def_path_str(trait_did);10351036    // We'll try to suggest constraining type parameters to fulfill the requirements of1037    // their `Copy` implementation.1038    let mut errors: BTreeMap<_, Vec<_>> = Default::default();1039    let mut bounds = vec![];10401041    let mut seen_tys = FxHashSet::default();10421043    let mut label_spans = Vec::new();10441045    for (span, ty, reason) in infringing_tys {1046        // Only report an error once per type.1047        if !seen_tys.insert(ty) {1048            continue;1049        }10501051        label_spans.push(span);10521053        match reason {1054            InfringingFieldsReason::Fulfill(fulfillment_errors) => {1055                for error in fulfillment_errors {1056                    let error_predicate = error.obligation.predicate;1057                    // Only note if it's not the root obligation, otherwise it's trivial and1058                    // should be self-explanatory (i.e. a field literally doesn't implement Copy).10591060                    // FIXME: This error could be more descriptive, especially if the error_predicate1061                    // contains a foreign type or if it's a deeply nested type...1062                    if error_predicate != error.root_obligation.predicate {1063                        errors1064                            .entry((ty.to_string(), error_predicate.to_string()))1065                            .or_default()1066                            .push(error.obligation.cause.span);1067                    }1068                    if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {1069                        trait_ref,1070                        polarity: ty::PredicatePolarity::Positive,1071                        ..1072                    })) = error_predicate.kind().skip_binder()1073                    {1074                        let ty = trait_ref.self_ty();1075                        if let ty::Param(_) = ty.kind() {1076                            bounds.push((1077                                format!("{ty}"),1078                                trait_ref.print_trait_sugared().to_string(),1079                                Some(trait_ref.def_id),1080                            ));1081                        }1082                    }1083                }1084            }1085            InfringingFieldsReason::Regions(region_errors) => {1086                for error in region_errors {1087                    let ty = ty.to_string();1088                    match error {1089                        RegionResolutionError::ConcreteFailure(origin, a, b) => {1090                            let predicate = format!("{b}: {a}");1091                            errors1092                                .entry((ty.clone(), predicate.clone()))1093                                .or_default()1094                                .push(origin.span());1095                            if let ty::RegionKind::ReEarlyParam(ebr) = b.kind()1096                                && ebr.is_named()1097                            {1098                                bounds.push((b.to_string(), a.to_string(), None));1099                            }1100                        }1101                        RegionResolutionError::GenericBoundFailure(origin, a, b) => {1102                            let predicate = format!("{a}: {b}");1103                            errors1104                                .entry((ty.clone(), predicate.clone()))1105                                .or_default()1106                                .push(origin.span());1107                            if let infer::region_constraints::GenericKind::Param(_) = a {1108                                bounds.push((a.to_string(), b.to_string(), None));1109                            }1110                        }1111                        _ => continue,1112                    }1113                }1114            }1115        }1116    }1117    let mut notes = Vec::new();1118    for ((ty, error_predicate), spans) in errors {1119        let span: MultiSpan = spans.into();1120        notes.push(errors::ImplForTyRequires {1121            span,1122            error_predicate,1123            trait_name: trait_name.clone(),1124            ty,1125        });1126    }11271128    let mut err = tcx.dcx().create_err(errors::TraitCannotImplForTy {1129        span: impl_span,1130        trait_name,1131        label_spans,1132        notes,1133    });11341135    suggest_constraining_type_params(1136        tcx,1137        tcx.hir_get_generics(impl_did).expect("impls always have generics"),1138        &mut err,1139        bounds1140            .iter()1141            .map(|(param, constraint, def_id)| (param.as_str(), constraint.as_str(), *def_id)),1142        None,1143    );11441145    err.emit()1146}11471148fn visit_implementation_of_coerce_pointee_validity(1149    checker: &Checker<'_>,1150) -> Result<(), ErrorGuaranteed> {1151    let tcx = checker.tcx;1152    let self_ty =1153        tcx.impl_trait_ref(checker.impl_def_id).instantiate_identity().skip_norm_wip().self_ty();1154    let span = tcx.def_span(checker.impl_def_id);1155    if !tcx.is_builtin_derived(checker.impl_def_id.into()) {1156        return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span }));1157    }1158    let ty::Adt(def, _args) = self_ty.kind() else {1159        return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType { span }));1160    };1161    let did = def.did();1162    // Now get a more precise span of the `struct`.1163    let span = tcx.def_span(did);1164    if !def.is_struct() {1165        return Err(tcx1166            .dcx()1167            .emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() }));1168    }1169    if !def.repr().transparent() {1170        return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span }));1171    }1172    if def.all_fields().next().is_none() {1173        return Err(tcx.dcx().emit_err(errors::CoercePointeeNoField { span }));1174    }1175    Ok(())1176}

Code quality findings 28

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
} else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let (a, span_a) = a_data_fields[0];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let (b, span_b) = b_data_fields[0];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// field `ptr` from a thin pointer of type `*mut [i32;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// 3]` to a wide pointer of type `*mut [i32]` (with
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let (i, a, b, field_span) = diff_fields[0];
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
tcx.hir_get_generics(impl_did).expect("impls always have generics"),
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match checker.impl_header.trait_ref.instantiate_identity().skip_norm_wip().self_ty().kind() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.emit_err(errors::ConstParamTyFieldVisMismatch { span }));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let (def, args) = match source.kind() {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!("something went wrong with structurally_normalize_ty");
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// ptr: *mut U,
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// field `ptr` from a thin pointer of type `*mut [i32;
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// 3]` to a wide pointer of type `*mut [i32]` (with
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// - `ptr` has type `*mut U` before and type `*mut V` after
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// that `*mut U: CoerceUnsized<*mut V>` is implemented
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// U` can be coerced to `*mut V` if `U: Unsize<V>`.
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
label_spans.push(span);
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
.entry((ty.to_string(), error_predicate.to_string()))
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
bounds.push((
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
trait_ref.print_trait_sugared().to_string(),
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
let ty = ty.to_string();
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
.entry((ty.clone(), predicate.clone()))
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
.push(origin.span());
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
bounds.push((a.to_string(), b.to_string(), None));
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
bounds.push((a.to_string(), b.to_string(), None));
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
notes.push(errors::ImplForTyRequires {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
trait_name: trait_name.clone(),

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.