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 rustc_ast::Mutability;
4use rustc_hir as hir;
5use rustc_hir::attrs::lang_items::LangItem;
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(tcx, param_env, self_type, adt, args, parent_cause, LangItem::Copy)
73        .map_err(CopyImplementationError::InfringingFields)?;
74
75    if let Some(did) = adt.destructor(tcx).map(|dtor| dtor.did) {
76        return Err(CopyImplementationError::HasDestructor(did));
77    }
78
79    if impl_safety.is_safe() && self_type.has_unsafe_fields() {
80        return Err(CopyImplementationError::HasUnsafeFields);
81    }
82
83    Ok(())
84}
85
86/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`.
87///
88/// If fields don't implement `(Unsized?)ConstParamTy`, return an error containing a list of
89/// those violating fields.
90///
91/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
92pub fn type_allowed_to_implement_const_param_ty<'tcx>(
93    tcx: TyCtxt<'tcx>,
94    param_env: ty::ParamEnv<'tcx>,
95    self_type: Ty<'tcx>,
96    parent_cause: ObligationCause<'tcx>,
97) -> Result<(), ConstParamTyImplementationError<'tcx>> {
98    let mut need_unstable_feature_bound = false;
99
100    let inner_tys: Vec<_> = match *self_type.kind() {
101        // Trivially okay as these types are all:
102        // - Sized
103        // - Contain no nested types
104        // - Have structural equality
105        ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()),
106
107        // Handle types gated under `feature(unsized_const_params)`
108        // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references
109        ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => {
110            need_unstable_feature_bound = true;
111            ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [inner_ty]))vec![inner_ty]
112        }
113        ty::Str => {
114            need_unstable_feature_bound = true;
115            ::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)]
116        }
117        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],
118
119        // `str` morally acts like a newtype around `[u8]`
120        ty::Tuple(inner_tys) => inner_tys.into_iter().collect(),
121
122        ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => {
123            if !tcx.features().adt_const_params() {
124                for variant in adt.variants() {
125                    if variant.is_field_list_non_exhaustive() {
126                        let attr_span = match {
    {
        'done:
            {
            for i in
                ::rustc_attr_ir::HasAttrs::get_attrs(variant.def_id, &tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(hir::attrs::AttributeKind::NonExhaustive(span))
                        => {
                        break 'done Some(*span);
                    }
                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}hir::find_attr!(tcx, variant.def_id, hir::attrs::AttributeKind::NonExhaustive(span) => *span)
127                        {
128                            Some(sp) => sp,
129                            None => ::rustc_middle::util::bug::bug_fmt(format_args!("non_exhaustive variant missing NonExhaustive attribute"))bug!("non_exhaustive variant missing NonExhaustive attribute"),
130                        };
131                        return Err(ConstParamTyImplementationError::NonExhaustive(attr_span));
132                    }
133                }
134            }
135
136            all_fields_implement_trait(
137                tcx,
138                param_env,
139                self_type,
140                adt,
141                args,
142                parent_cause.clone(),
143                LangItem::ConstParamTy,
144            )
145            .map_err(ConstParamTyImplementationError::InfrigingFields)?;
146
147            ::alloc::vec::Vec::new()vec![]
148        }
149
150        _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
151    };
152
153    let mut infringing_inner_tys = ::alloc::vec::Vec::new()vec![];
154    for inner_ty in inner_tys {
155        // We use an ocx per inner ty for better diagnostics
156        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
157        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
158
159        // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)]
160        if need_unstable_feature_bound {
161            ocx.register_obligation(Obligation::new(
162                tcx,
163                parent_cause.clone(),
164                param_env,
165                ty::ClauseKind::UnstableFeature(sym::unsized_const_params),
166            ));
167
168            if !ocx.evaluate_obligations_error_on_ambiguity().no_errors() {
169                return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
170            }
171        }
172
173        ocx.register_bound(
174            parent_cause.clone(),
175            param_env,
176            inner_ty,
177            tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span),
178        );
179
180        let errors = ocx.evaluate_obligations_error_on_ambiguity();
181        if let TraitErrors::HasErrors(errors) = errors {
182            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors)));
183            continue;
184        }
185
186        // Check regions assuming the self type of the impl is WF
187        let errors = infcx.resolve_regions(parent_cause.body_def_id, param_env, [self_type]);
188        if !errors.is_empty() {
189            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
190            continue;
191        }
192    }
193
194    if !infringing_inner_tys.is_empty() {
195        return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(
196            infringing_inner_tys,
197        ));
198    }
199
200    Ok(())
201}
202
203/// Check that all fields of a given `adt` implement `lang_item` trait.
204pub fn all_fields_implement_trait<'tcx>(
205    tcx: TyCtxt<'tcx>,
206    param_env: ty::ParamEnv<'tcx>,
207    self_type: Ty<'tcx>,
208    adt: AdtDef<'tcx>,
209    args: ty::GenericArgsRef<'tcx>,
210    parent_cause: ObligationCause<'tcx>,
211    lang_item: LangItem,
212) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
213    let trait_def_id = tcx.require_lang_item(lang_item, parent_cause.span);
214
215    let mut infringing = Vec::new();
216    for variant in adt.variants() {
217        for field in &variant.fields {
218            // Do this per-field to get better error messages.
219            let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
220            let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
221
222            let unnormalized_ty = field.ty(tcx, args);
223            if unnormalized_ty.references_error() {
224                continue;
225            }
226
227            let field_span = tcx.def_span(field.did);
228            let field_ty_span = match tcx.hir_get_if_local(field.did) {
229                Some(hir::Node::Field(field_def)) => field_def.ty.span,
230                _ => field_span,
231            };
232
233            // FIXME(compiler-errors): This gives us better spans for bad
234            // projection types like in issue-50480.
235            // If the ADT has args, point to the cause we are given.
236            // If it does not, then this field probably doesn't normalize
237            // to begin with, and point to the bad field's span instead.
238            let normalization_cause = if field
239                .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did()))
240                .has_non_region_param()
241            {
242                parent_cause.clone()
243            } else {
244                ObligationCause::dummy_with_span(field_ty_span)
245            };
246            let ty: Ty<'_> = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
247            let normalization_errors = ocx.try_evaluate_obligations();
248
249            // NOTE: The post-normalization type may also reference errors,
250            // such as when we project to a missing type or we have a mismatch
251            // between expected and found const-generic types. Don't report an
252            // additional copy error here, since it's not typically useful.
253            if !normalization_errors.no_errors() || ty.references_error() {
254                tcx.dcx().span_delayed_bug(
255                    field_span,
256                    ::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!(
257                        "couldn't normalize struct field `{ty}` when checking {tr} implementation",
258                        tr = tcx.def_path_str(trait_def_id)
259                    ),
260                );
261                continue;
262            }
263
264            ocx.register_bound(
265                ObligationCause::dummy_with_span(field_ty_span),
266                param_env,
267                ty,
268                trait_def_id,
269            );
270            let errors = ocx.evaluate_obligations_error_on_ambiguity();
271            if let TraitErrors::HasErrors(errors) = errors {
272                infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
273            }
274
275            // Check regions assuming the self type of the impl is WF
276            let errors = infcx.resolve_regions(parent_cause.body_def_id, param_env, [self_type]);
277            if !errors.is_empty() {
278                infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
279            }
280        }
281    }
282
283    if infringing.is_empty() { Ok(()) } else { Err(infringing) }
284}