]>
Commit | Line | Data |
---|---|---|
74b04a01 XL |
1 | //! Miscellaneous type-system utilities that are too small to deserve their own modules. |
2 | ||
9ffffee4 | 3 | use crate::traits::{self, ObligationCause, ObligationCtxt}; |
74b04a01 | 4 | |
49aad941 | 5 | use hir::LangItem; |
9c376795 | 6 | use rustc_data_structures::fx::FxIndexSet; |
74b04a01 | 7 | use rustc_hir as hir; |
9ffffee4 | 8 | use rustc_infer::infer::canonical::Canonical; |
9c376795 | 9 | use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; |
9ffffee4 | 10 | use rustc_infer::traits::query::NoSolution; |
9c376795 | 11 | use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError}; |
4b012472 | 12 | use rustc_middle::ty::{self, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt}; |
9ffffee4 | 13 | use rustc_span::DUMMY_SP; |
ba9703b0 | 14 | |
9c376795 | 15 | use super::outlives_bounds::InferCtxtExt; |
74b04a01 | 16 | |
74b04a01 | 17 | pub enum CopyImplementationError<'tcx> { |
49aad941 | 18 | InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), |
74b04a01 XL |
19 | NotAnAdt, |
20 | HasDestructor, | |
21 | } | |
22 | ||
49aad941 FG |
23 | pub enum ConstParamTyImplementationError<'tcx> { |
24 | InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), | |
25 | NotAnAdtOrBuiltinAllowed, | |
26 | } | |
27 | ||
9c376795 FG |
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 | |
49aad941 FG |
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)`. | |
9c376795 | 40 | pub fn type_allowed_to_implement_copy<'tcx>( |
74b04a01 XL |
41 | tcx: TyCtxt<'tcx>, |
42 | param_env: ty::ParamEnv<'tcx>, | |
43 | self_type: Ty<'tcx>, | |
923072b8 | 44 | parent_cause: ObligationCause<'tcx>, |
74b04a01 | 45 | ) -> Result<(), CopyImplementationError<'tcx>> { |
add651ee | 46 | let (adt, args) = match self_type.kind() { |
2b03887a FG |
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(()), | |
74b04a01 | 58 | |
add651ee | 59 | &ty::Adt(adt, args) => (adt, args), |
74b04a01 | 60 | |
2b03887a FG |
61 | _ => return Err(CopyImplementationError::NotAnAdt), |
62 | }; | |
74b04a01 | 63 | |
49aad941 FG |
64 | all_fields_implement_trait( |
65 | tcx, | |
66 | param_env, | |
67 | self_type, | |
68 | adt, | |
add651ee | 69 | args, |
49aad941 FG |
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)`. | |
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>> { | |
add651ee | 94 | let (adt, args) = match self_type.kind() { |
49aad941 FG |
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(_) | |
fe692bf9 FG |
103 | | ty::Ref(.., hir::Mutability::Not) |
104 | | ty::Tuple(_) => return Ok(()), | |
49aad941 | 105 | |
add651ee | 106 | &ty::Adt(adt, args) => (adt, args), |
49aad941 FG |
107 | |
108 | _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), | |
109 | }; | |
110 | ||
111 | all_fields_implement_trait( | |
112 | tcx, | |
113 | param_env, | |
114 | self_type, | |
115 | adt, | |
add651ee | 116 | args, |
49aad941 FG |
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. | |
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>, | |
add651ee | 131 | args: &'tcx List<GenericArg<'tcx>>, |
49aad941 FG |
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)); | |
9c376795 | 136 | |
2b03887a FG |
137 | let mut infringing = Vec::new(); |
138 | for variant in adt.variants() { | |
139 | for field in &variant.fields { | |
9c376795 FG |
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 | ||
add651ee | 144 | let unnormalized_ty = field.ty(tcx, args); |
9c376795 | 145 | if unnormalized_ty.references_error() { |
2b03887a | 146 | continue; |
74b04a01 | 147 | } |
9c376795 FG |
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 | ||
2b03887a FG |
155 | // FIXME(compiler-errors): This gives us better spans for bad |
156 | // projection types like in issue-50480. | |
add651ee | 157 | // If the ADT has args, point to the cause we are given. |
2b03887a FG |
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. | |
9c376795 | 160 | let normalization_cause = if field |
add651ee | 161 | .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did())) |
2b03887a FG |
162 | .has_non_region_param() |
163 | { | |
164 | parent_cause.clone() | |
165 | } else { | |
9c376795 | 166 | ObligationCause::dummy_with_span(field_ty_span) |
2b03887a | 167 | }; |
9c376795 FG |
168 | let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty); |
169 | let normalization_errors = ocx.select_where_possible(); | |
353b0b11 FG |
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() { | |
4b012472 | 176 | tcx.sess.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))); |
9c376795 FG |
177 | continue; |
178 | } | |
179 | ||
180 | ocx.register_bound( | |
181 | ObligationCause::dummy_with_span(field_ty_span), | |
182 | param_env, | |
183 | ty, | |
49aad941 | 184 | trait_def_id, |
9c376795 FG |
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, | |
9c376795 FG |
194 | infcx.implied_bounds_tys( |
195 | param_env, | |
196 | parent_cause.body_id, | |
197 | FxIndexSet::from_iter([self_type]), | |
198 | ), | |
199 | ); | |
9c376795 FG |
200 | let errors = infcx.resolve_regions(&outlives_env); |
201 | if !errors.is_empty() { | |
202 | infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); | |
203 | } | |
74b04a01 | 204 | } |
2b03887a | 205 | } |
9c376795 | 206 | |
49aad941 | 207 | if infringing.is_empty() { Ok(()) } else { Err(infringing) } |
74b04a01 | 208 | } |
9ffffee4 FG |
209 | |
210 | pub fn check_tys_might_be_eq<'tcx>( | |
211 | tcx: TyCtxt<'tcx>, | |
4b012472 | 212 | canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, |
9ffffee4 | 213 | ) -> Result<(), NoSolution> { |
4b012472 FG |
214 | let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); |
215 | let (param_env, (ty_a, ty_b)) = key.into_parts(); | |
9ffffee4 FG |
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 | } |