Skip to main content

rustc_trait_selection/traits/
misc.rs

1//! Miscellaneous type-system utilities that are too small to deserve their own modules.
2
3use hir::LangItem;
4use rustc_ast::Mutability;
5use rustc_hir as hir;
6use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
7use rustc_infer::traits::TraitErrors;
8use rustc_middle::bug;
9use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
10use rustc_span::{Span, sym};
11use thin_vec::ThinVec;
12
13use crate::regions::InferCtxtRegionExt;
14use crate::traits::{self, FulfillmentError, Obligation, ObligationCause};
15
16pub enum CopyImplementationError<'tcx> {
17    InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
18    NotAnAdt,
19    HasDestructor(hir::def_id::DefId),
20    HasUnsafeFields,
21}
22
23pub enum ConstParamTyImplementationError<'tcx> {
24    UnsizedConstParamsFeatureRequired,
25    InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
26    InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
27    NotAnAdtOrBuiltinAllowed,
28    NonExhaustive(Span),
29}
30
31pub enum InfringingFieldsReason<'tcx> {
32    Fulfill(ThinVec<FulfillmentError<'tcx>>),
33    Regions(Vec<RegionResolutionError<'tcx>>),
34}
35
36/// Checks that the fields of the type (an ADT) all implement copy.
37///
38/// If fields don't implement copy, return an error containing a list of
39/// those violating fields.
40///
41/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
42/// a reference or an array returns `Err(NotAnAdt)`.
43///
44/// If the impl is `Safe`, `self_type` must not have unsafe fields. When used to
45/// generate suggestions in lints, `Safe` should be supplied so as to not
46/// suggest implementing `Copy` for types with unsafe fields.
47pub fn type_allowed_to_implement_copy<'tcx>(
48    tcx: TyCtxt<'tcx>,
49    param_env: ty::ParamEnv<'tcx>,
50    self_type: Ty<'tcx>,
51    parent_cause: ObligationCause<'tcx>,
52    impl_safety: hir::Safety,
53) -> Result<(), CopyImplementationError<'tcx>> {
54    let (adt, args) = match self_type.kind() {
55        // These types used to have a builtin impl.
56        // Now libcore provides that impl.
57        ty::Uint(_)
58        | ty::Int(_)
59        | ty::Bool
60        | ty::Float(_)
61        | ty::Char
62        | ty::RawPtr(..)
63        | ty::Never
64        | ty::Ref(_, _, hir::Mutability::Not)
65        | ty::Array(..) => return Ok(()),
66
67        &ty::Adt(adt, args) => (adt, args),
68
69        _ => return Err(CopyImplementationError::NotAnAdt),
70    };
71
72    all_fields_implement_trait(
73        tcx,
74        param_env,
75        self_type,
76        adt,
77        args,
78        parent_cause,
79        hir::LangItem::Copy,
80    )
81    .map_err(CopyImplementationError::InfringingFields)?;
82
83    if let Some(did) = adt.destructor(tcx).map(|dtor| dtor.did) {
84        return Err(CopyImplementationError::HasDestructor(did));
85    }
86
87    if impl_safety.is_safe() && self_type.has_unsafe_fields() {
88        return Err(CopyImplementationError::HasUnsafeFields);
89    }
90
91    Ok(())
92}
93
94/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`.
95///
96/// If fields don't implement `(Unsized?)ConstParamTy`, return an error containing a list of
97/// those violating fields.
98///
99/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
100pub fn type_allowed_to_implement_const_param_ty<'tcx>(
101    tcx: TyCtxt<'tcx>,
102    param_env: ty::ParamEnv<'tcx>,
103    self_type: Ty<'tcx>,
104    parent_cause: ObligationCause<'tcx>,
105) -> Result<(), ConstParamTyImplementationError<'tcx>> {
106    let mut need_unstable_feature_bound = false;
107
108    let inner_tys: Vec<_> = match *self_type.kind() {
109        // Trivially okay as these types are all:
110        // - Sized
111        // - Contain no nested types
112        // - Have structural equality
113        ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()),
114
115        // Handle types gated under `feature(unsized_const_params)`
116        // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references
117        ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => {
118            need_unstable_feature_bound = true;
119            ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [inner_ty]))vec![inner_ty]
120        }
121        ty::Str => {
122            need_unstable_feature_bound = true;
123            ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ty::new_slice(tcx, tcx.types.u8)]))vec![Ty::new_slice(tcx, tcx.types.u8)]
124        }
125        ty::Array(inner_ty, _) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [inner_ty]))vec![inner_ty],
126
127        // `str` morally acts like a newtype around `[u8]`
128        ty::Tuple(inner_tys) => inner_tys.into_iter().collect(),
129
130        ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => {
131            if !tcx.features().adt_const_params() {
132                for variant in adt.variants() {
133                    if variant.is_field_list_non_exhaustive() {
134                        let attr_span = match {
    {
        'done:
            {
            for i in
                ::rustc_hir::attrs::HasAttrs::get_attrs(variant.def_id, &tcx)
                {
                #[allow(unused_imports)]
                use ::rustc_hir::attrs::AttributeKind::*;
                let i: &::rustc_hir::Attribute = i;
                match i {
                    ::rustc_hir::Attribute::Parsed(hir::attrs::AttributeKind::NonExhaustive(span))
                        => {
                        break 'done Some(*span);
                    }
                    ::rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}hir::find_attr!(tcx, variant.def_id, hir::attrs::AttributeKind::NonExhaustive(span) => *span)
135                        {
136                            Some(sp) => sp,
137                            None => ::rustc_middle::util::bug::bug_fmt(format_args!("non_exhaustive variant missing NonExhaustive attribute"))bug!("non_exhaustive variant missing NonExhaustive attribute"),
138                        };
139                        return Err(ConstParamTyImplementationError::NonExhaustive(attr_span));
140                    }
141                }
142            }
143
144            all_fields_implement_trait(
145                tcx,
146                param_env,
147                self_type,
148                adt,
149                args,
150                parent_cause.clone(),
151                LangItem::ConstParamTy,
152            )
153            .map_err(ConstParamTyImplementationError::InfrigingFields)?;
154
155            ::alloc::vec::Vec::new()vec![]
156        }
157
158        _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
159    };
160
161    let mut infringing_inner_tys = ::alloc::vec::Vec::new()vec![];
162    for inner_ty in inner_tys {
163        // We use an ocx per inner ty for better diagnostics
164        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
165        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
166
167        // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)]
168        if need_unstable_feature_bound {
169            ocx.register_obligation(Obligation::new(
170                tcx,
171                parent_cause.clone(),
172                param_env,
173                ty::ClauseKind::UnstableFeature(sym::unsized_const_params),
174            ));
175
176            if !ocx.evaluate_obligations_error_on_ambiguity().no_errors() {
177                return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
178            }
179        }
180
181        ocx.register_bound(
182            parent_cause.clone(),
183            param_env,
184            inner_ty,
185            tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span),
186        );
187
188        let errors = ocx.evaluate_obligations_error_on_ambiguity();
189        if let TraitErrors::HasErrors(errors) = errors {
190            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors)));
191            continue;
192        }
193
194        // Check regions assuming the self type of the impl is WF
195        let errors = infcx.resolve_regions(parent_cause.body_def_id, param_env, [self_type]);
196        if !errors.is_empty() {
197            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
198            continue;
199        }
200    }
201
202    if !infringing_inner_tys.is_empty() {
203        return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(
204            infringing_inner_tys,
205        ));
206    }
207
208    Ok(())
209}
210
211/// Check that all fields of a given `adt` implement `lang_item` trait.
212pub fn all_fields_implement_trait<'tcx>(
213    tcx: TyCtxt<'tcx>,
214    param_env: ty::ParamEnv<'tcx>,
215    self_type: Ty<'tcx>,
216    adt: AdtDef<'tcx>,
217    args: ty::GenericArgsRef<'tcx>,
218    parent_cause: ObligationCause<'tcx>,
219    lang_item: LangItem,
220) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
221    let trait_def_id = tcx.require_lang_item(lang_item, parent_cause.span);
222
223    let mut infringing = Vec::new();
224    for variant in adt.variants() {
225        for field in &variant.fields {
226            // Do this per-field to get better error messages.
227            let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
228            let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
229
230            let unnormalized_ty = field.ty(tcx, args);
231            if unnormalized_ty.references_error() {
232                continue;
233            }
234
235            let field_span = tcx.def_span(field.did);
236            let field_ty_span = match tcx.hir_get_if_local(field.did) {
237                Some(hir::Node::Field(field_def)) => field_def.ty.span,
238                _ => field_span,
239            };
240
241            // FIXME(compiler-errors): This gives us better spans for bad
242            // projection types like in issue-50480.
243            // If the ADT has args, point to the cause we are given.
244            // If it does not, then this field probably doesn't normalize
245            // to begin with, and point to the bad field's span instead.
246            let normalization_cause = if field
247                .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did()))
248                .has_non_region_param()
249            {
250                parent_cause.clone()
251            } else {
252                ObligationCause::dummy_with_span(field_ty_span)
253            };
254            let ty: Ty<'_> = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
255            let normalization_errors = ocx.try_evaluate_obligations();
256
257            // NOTE: The post-normalization type may also reference errors,
258            // such as when we project to a missing type or we have a mismatch
259            // between expected and found const-generic types. Don't report an
260            // additional copy error here, since it's not typically useful.
261            if !normalization_errors.no_errors() || ty.references_error() {
262                tcx.dcx().span_delayed_bug(
263                    field_span,
264                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("couldn\'t normalize struct field `{1}` when checking {0} implementation",
                tcx.def_path_str(trait_def_id), ty))
    })format!(
265                        "couldn't normalize struct field `{ty}` when checking {tr} implementation",
266                        tr = tcx.def_path_str(trait_def_id)
267                    ),
268                );
269                continue;
270            }
271
272            ocx.register_bound(
273                ObligationCause::dummy_with_span(field_ty_span),
274                param_env,
275                ty,
276                trait_def_id,
277            );
278            let errors = ocx.evaluate_obligations_error_on_ambiguity();
279            if let TraitErrors::HasErrors(errors) = errors {
280                infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
281            }
282
283            // Check regions assuming the self type of the impl is WF
284            let errors = infcx.resolve_regions(parent_cause.body_def_id, param_env, [self_type]);
285            if !errors.is_empty() {
286                infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
287            }
288        }
289    }
290
291    if infringing.is_empty() { Ok(()) } else { Err(infringing) }
292}