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