• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
2 
3 use crate::traits::{self, ObligationCause, ObligationCtxt};
4 
5 use hir::LangItem;
6 use rustc_data_structures::fx::FxIndexSet;
7 use rustc_hir as hir;
8 use rustc_infer::infer::canonical::Canonical;
9 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
10 use rustc_infer::traits::query::NoSolution;
11 use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
12 use rustc_middle::ty::{self, AdtDef, GenericArg, List, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
13 use rustc_span::DUMMY_SP;
14 
15 use super::outlives_bounds::InferCtxtExt;
16 
17 pub enum CopyImplementationError<'tcx> {
18     InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
19     NotAnAdt,
20     HasDestructor,
21 }
22 
23 pub enum ConstParamTyImplementationError<'tcx> {
24     InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
25     NotAnAdtOrBuiltinAllowed,
26 }
27 
28 pub enum InfringingFieldsReason<'tcx> {
29     Fulfill(Vec<FulfillmentError<'tcx>>),
30     Regions(Vec<RegionResolutionError<'tcx>>),
31 }
32 
33 /// Checks that the fields of the type (an ADT) all implement copy.
34 ///
35 /// If fields don't implement copy, return an error containing a list of
36 /// those violating fields.
37 ///
38 /// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
39 /// a reference or an array returns `Err(NotAnAdt)`.
type_allowed_to_implement_copy<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, parent_cause: ObligationCause<'tcx>, ) -> Result<(), CopyImplementationError<'tcx>>40 pub fn type_allowed_to_implement_copy<'tcx>(
41     tcx: TyCtxt<'tcx>,
42     param_env: ty::ParamEnv<'tcx>,
43     self_type: Ty<'tcx>,
44     parent_cause: ObligationCause<'tcx>,
45 ) -> Result<(), CopyImplementationError<'tcx>> {
46     let (adt, substs) = match self_type.kind() {
47         // These types used to have a builtin impl.
48         // Now libcore provides that impl.
49         ty::Uint(_)
50         | ty::Int(_)
51         | ty::Bool
52         | ty::Float(_)
53         | ty::Char
54         | ty::RawPtr(..)
55         | ty::Never
56         | ty::Ref(_, _, hir::Mutability::Not)
57         | ty::Array(..) => return Ok(()),
58 
59         &ty::Adt(adt, substs) => (adt, substs),
60 
61         _ => return Err(CopyImplementationError::NotAnAdt),
62     };
63 
64     all_fields_implement_trait(
65         tcx,
66         param_env,
67         self_type,
68         adt,
69         substs,
70         parent_cause,
71         hir::LangItem::Copy,
72     )
73     .map_err(CopyImplementationError::InfringingFields)?;
74 
75     if adt.has_dtor(tcx) {
76         return Err(CopyImplementationError::HasDestructor);
77     }
78 
79     Ok(())
80 }
81 
82 /// Checks that the fields of the type (an ADT) all implement `ConstParamTy`.
83 ///
84 /// If fields don't implement `ConstParamTy`, return an error containing a list of
85 /// those violating fields.
86 ///
87 /// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>>88 pub fn type_allowed_to_implement_const_param_ty<'tcx>(
89     tcx: TyCtxt<'tcx>,
90     param_env: ty::ParamEnv<'tcx>,
91     self_type: Ty<'tcx>,
92     parent_cause: ObligationCause<'tcx>,
93 ) -> Result<(), ConstParamTyImplementationError<'tcx>> {
94     let (adt, substs) = match self_type.kind() {
95         // `core` provides these impls.
96         ty::Uint(_)
97         | ty::Int(_)
98         | ty::Bool
99         | ty::Char
100         | ty::Str
101         | ty::Array(..)
102         | ty::Slice(_)
103         | ty::Ref(.., hir::Mutability::Not)
104         | ty::Tuple(_) => return Ok(()),
105 
106         &ty::Adt(adt, substs) => (adt, substs),
107 
108         _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
109     };
110 
111     all_fields_implement_trait(
112         tcx,
113         param_env,
114         self_type,
115         adt,
116         substs,
117         parent_cause,
118         hir::LangItem::ConstParamTy,
119     )
120     .map_err(ConstParamTyImplementationError::InfrigingFields)?;
121 
122     Ok(())
123 }
124 
125 /// Check that all fields of a given `adt` implement `lang_item` trait.
all_fields_implement_trait<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, adt: AdtDef<'tcx>, substs: &'tcx List<GenericArg<'tcx>>, parent_cause: ObligationCause<'tcx>, lang_item: LangItem, ) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>>126 pub fn all_fields_implement_trait<'tcx>(
127     tcx: TyCtxt<'tcx>,
128     param_env: ty::ParamEnv<'tcx>,
129     self_type: Ty<'tcx>,
130     adt: AdtDef<'tcx>,
131     substs: &'tcx List<GenericArg<'tcx>>,
132     parent_cause: ObligationCause<'tcx>,
133     lang_item: LangItem,
134 ) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
135     let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span));
136 
137     let mut infringing = Vec::new();
138     for variant in adt.variants() {
139         for field in &variant.fields {
140             // Do this per-field to get better error messages.
141             let infcx = tcx.infer_ctxt().build();
142             let ocx = traits::ObligationCtxt::new(&infcx);
143 
144             let unnormalized_ty = field.ty(tcx, substs);
145             if unnormalized_ty.references_error() {
146                 continue;
147             }
148 
149             let field_span = tcx.def_span(field.did);
150             let field_ty_span = match tcx.hir().get_if_local(field.did) {
151                 Some(hir::Node::Field(field_def)) => field_def.ty.span,
152                 _ => field_span,
153             };
154 
155             // FIXME(compiler-errors): This gives us better spans for bad
156             // projection types like in issue-50480.
157             // If the ADT has substs, point to the cause we are given.
158             // If it does not, then this field probably doesn't normalize
159             // to begin with, and point to the bad field's span instead.
160             let normalization_cause = if field
161                 .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
162                 .has_non_region_param()
163             {
164                 parent_cause.clone()
165             } else {
166                 ObligationCause::dummy_with_span(field_ty_span)
167             };
168             let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
169             let normalization_errors = ocx.select_where_possible();
170 
171             // NOTE: The post-normalization type may also reference errors,
172             // such as when we project to a missing type or we have a mismatch
173             // between expected and found const-generic types. Don't report an
174             // additional copy error here, since it's not typically useful.
175             if !normalization_errors.is_empty() || ty.references_error() {
176                 tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id)));
177                 continue;
178             }
179 
180             ocx.register_bound(
181                 ObligationCause::dummy_with_span(field_ty_span),
182                 param_env,
183                 ty,
184                 trait_def_id,
185             );
186             let errors = ocx.select_all_or_error();
187             if !errors.is_empty() {
188                 infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
189             }
190 
191             // Check regions assuming the self type of the impl is WF
192             let outlives_env = OutlivesEnvironment::with_bounds(
193                 param_env,
194                 infcx.implied_bounds_tys(
195                     param_env,
196                     parent_cause.body_id,
197                     FxIndexSet::from_iter([self_type]),
198                 ),
199             );
200             let errors = infcx.resolve_regions(&outlives_env);
201             if !errors.is_empty() {
202                 infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
203             }
204         }
205     }
206 
207     if infringing.is_empty() { Ok(()) } else { Err(infringing) }
208 }
209 
check_tys_might_be_eq<'tcx>( tcx: TyCtxt<'tcx>, canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>, ) -> Result<(), NoSolution>210 pub fn check_tys_might_be_eq<'tcx>(
211     tcx: TyCtxt<'tcx>,
212     canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>,
213 ) -> Result<(), NoSolution> {
214     let (infcx, (param_env, ty_a, ty_b), _) =
215         tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
216     let ocx = ObligationCtxt::new(&infcx);
217 
218     let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);
219     // use `select_where_possible` instead of `select_all_or_error` so that
220     // we don't get errors from obligations being ambiguous.
221     let errors = ocx.select_where_possible();
222 
223     if errors.len() > 0 || result.is_err() { Err(NoSolution) } else { Ok(()) }
224 }
225