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