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