]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | // FIXME(@lcnr): Move this module out of `rustc_typeck`. |
2 | // | |
3 | // We don't do any drop checking during hir typeck. | |
f9f354fc | 4 | use crate::hir::def_id::{DefId, LocalDefId}; |
5e7ed085 | 5 | use rustc_errors::{struct_span_err, ErrorGuaranteed}; |
ba9703b0 XL |
6 | use rustc_middle::ty::error::TypeError; |
7 | use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; | |
923072b8 FG |
8 | use rustc_middle::ty::subst::SubstsRef; |
9 | use rustc_middle::ty::util::IgnoreRegions; | |
10 | use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; | |
60c5eb7d | 11 | |
9fa01778 | 12 | /// This function confirms that the `Drop` implementation identified by |
c34b1796 AL |
13 | /// `drop_impl_did` is not any more specialized than the type it is |
14 | /// attached to (Issue #8142). | |
15 | /// | |
16 | /// This means: | |
17 | /// | |
18 | /// 1. The self type must be nominal (this is already checked during | |
19 | /// coherence), | |
20 | /// | |
9fa01778 | 21 | /// 2. The generic region/type parameters of the impl's self type must |
0731742a | 22 | /// all be parameters of the Drop impl itself (i.e., no |
c34b1796 AL |
23 | /// specialization like `impl Drop for Foo<i32>`), and, |
24 | /// | |
25 | /// 3. Any bounds on the generic parameters must be reflected in the | |
26 | /// struct/enum definition for the nominal type itself (i.e. | |
27 | /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`). | |
28 | /// | |
5e7ed085 | 29 | pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> { |
7cac9316 XL |
30 | let dtor_self_type = tcx.type_of(drop_impl_did); |
31 | let dtor_predicates = tcx.predicates_of(drop_impl_did); | |
1b1a35ee | 32 | match dtor_self_type.kind() { |
b7449926 | 33 | ty::Adt(adt_def, self_to_impl_substs) => { |
0bf4aa26 XL |
34 | ensure_drop_params_and_item_params_correspond( |
35 | tcx, | |
f9f354fc | 36 | drop_impl_did.expect_local(), |
5e7ed085 | 37 | adt_def.did(), |
923072b8 | 38 | self_to_impl_substs, |
0bf4aa26 | 39 | )?; |
c34b1796 | 40 | |
0bf4aa26 XL |
41 | ensure_drop_predicates_are_implied_by_item_defn( |
42 | tcx, | |
e74abb32 | 43 | dtor_predicates, |
5e7ed085 | 44 | adt_def.did().expect_local(), |
0bf4aa26 XL |
45 | self_to_impl_substs, |
46 | ) | |
c34b1796 AL |
47 | } |
48 | _ => { | |
49 | // Destructors only work on nominal types. This was | |
2c00a5a8 XL |
50 | // already checked by coherence, but compilation may |
51 | // not have been terminated. | |
8bb4bdeb | 52 | let span = tcx.def_span(drop_impl_did); |
5e7ed085 | 53 | let reported = tcx.sess.delay_span_bug( |
dfeec247 | 54 | span, |
04454e1e | 55 | &format!("should have been rejected by coherence check: {dtor_self_type}"), |
dfeec247 | 56 | ); |
5e7ed085 | 57 | Err(reported) |
c34b1796 AL |
58 | } |
59 | } | |
60 | } | |
61 | ||
dc9dc135 XL |
62 | fn ensure_drop_params_and_item_params_correspond<'tcx>( |
63 | tcx: TyCtxt<'tcx>, | |
f9f354fc | 64 | drop_impl_did: LocalDefId, |
0bf4aa26 | 65 | self_type_did: DefId, |
923072b8 | 66 | drop_impl_substs: SubstsRef<'tcx>, |
5e7ed085 | 67 | ) -> Result<(), ErrorGuaranteed> { |
923072b8 FG |
68 | let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else { |
69 | return Ok(()) | |
70 | }; | |
e9174d1e | 71 | |
923072b8 FG |
72 | let drop_impl_span = tcx.def_span(drop_impl_did); |
73 | let item_span = tcx.def_span(self_type_did); | |
74 | let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); | |
75 | let mut err = | |
76 | struct_span_err!(tcx.sess, drop_impl_span, E0366, "`Drop` impls cannot be specialized"); | |
77 | match arg { | |
78 | ty::util::NotUniqueParam::DuplicateParam(arg) => { | |
79 | err.note(&format!("`{arg}` is mentioned multiple times")) | |
a7813a04 | 80 | } |
923072b8 FG |
81 | ty::util::NotUniqueParam::NotParam(arg) => { |
82 | err.note(&format!("`{arg}` is not a generic parameter")) | |
a7813a04 | 83 | } |
923072b8 FG |
84 | }; |
85 | err.span_note( | |
86 | item_span, | |
87 | &format!( | |
88 | "use the same sequence of generic lifetime, type and const parameters \ | |
89 | as the {self_descr} definition", | |
90 | ), | |
91 | ); | |
92 | Err(err.emit()) | |
c34b1796 AL |
93 | } |
94 | ||
95 | /// Confirms that every predicate imposed by dtor_predicates is | |
96 | /// implied by assuming the predicates attached to self_type_did. | |
dc9dc135 XL |
97 | fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( |
98 | tcx: TyCtxt<'tcx>, | |
e74abb32 | 99 | dtor_predicates: ty::GenericPredicates<'tcx>, |
f9f354fc | 100 | self_type_did: LocalDefId, |
532ac7d7 | 101 | self_to_impl_substs: SubstsRef<'tcx>, |
5e7ed085 | 102 | ) -> Result<(), ErrorGuaranteed> { |
8bb4bdeb | 103 | let mut result = Ok(()); |
c34b1796 AL |
104 | |
105 | // Here is an example, analogous to that from | |
106 | // `compare_impl_method`. | |
107 | // | |
108 | // Consider a struct type: | |
109 | // | |
110 | // struct Type<'c, 'b:'c, 'a> { | |
111 | // x: &'a Contents // (contents are irrelevant; | |
112 | // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) | |
113 | // } | |
114 | // | |
115 | // and a Drop impl: | |
116 | // | |
117 | // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { | |
118 | // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) | |
119 | // } | |
120 | // | |
121 | // We start out with self_to_impl_substs, that maps the generic | |
122 | // parameters of Type to that of the Drop impl. | |
123 | // | |
124 | // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} | |
125 | // | |
0731742a | 126 | // Applying this to the predicates (i.e., assumptions) provided by the item |
c34b1796 AL |
127 | // definition yields the instantiated assumptions: |
128 | // | |
129 | // ['y : 'z] | |
130 | // | |
131 | // We then check all of the predicates of the Drop impl: | |
132 | // | |
133 | // ['y:'z, 'x:'y] | |
134 | // | |
135 | // and ensure each is in the list of instantiated | |
136 | // assumptions. Here, `'y:'z` is present, but `'x:'y` is | |
137 | // absent. So we report an error that the Drop impl injected a | |
138 | // predicate that is not present on the struct definition. | |
139 | ||
c34b1796 AL |
140 | // We can assume the predicates attached to struct/enum definition |
141 | // hold. | |
7cac9316 | 142 | let generic_assumptions = tcx.predicates_of(self_type_did); |
c34b1796 AL |
143 | |
144 | let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); | |
9e0c209e | 145 | let assumptions_in_impl_context = assumptions_in_impl_context.predicates; |
c34b1796 | 146 | |
f2b60f7d FG |
147 | debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates); |
148 | ||
dfeec247 XL |
149 | let self_param_env = tcx.param_env(self_type_did); |
150 | ||
c34b1796 AL |
151 | // An earlier version of this code attempted to do this checking |
152 | // via the traits::fulfill machinery. However, it ran into trouble | |
153 | // since the fulfill machinery merely turns outlives-predicates | |
154 | // 'a:'b and T:'b into region inference constraints. It is simpler | |
155 | // just to look for all the predicates directly. | |
156 | ||
9e0c209e | 157 | assert_eq!(dtor_predicates.parent, None); |
f9f354fc | 158 | for &(predicate, predicate_sp) in dtor_predicates.predicates { |
c34b1796 AL |
159 | // (We do not need to worry about deep analysis of type |
160 | // expressions etc because the Drop impls are already forced | |
b039eaaf | 161 | // to take on a structure that is roughly an alpha-renaming of |
c34b1796 AL |
162 | // the generic parameters of the item definition.) |
163 | ||
dfeec247 XL |
164 | // This path now just checks *all* predicates via an instantiation of |
165 | // the `SimpleEqRelation`, which simply forwards to the `relate` machinery | |
166 | // after taking care of anonymizing late bound regions. | |
c34b1796 AL |
167 | // |
168 | // However, it may be more efficient in the future to batch | |
dfeec247 XL |
169 | // the analysis together via the fulfill (see comment above regarding |
170 | // the usage of the fulfill machinery), rather than the | |
171 | // repeated `.iter().any(..)` calls. | |
c34b1796 | 172 | |
dfeec247 XL |
173 | // This closure is a more robust way to check `Predicate` equality |
174 | // than simple `==` checks (which were the previous implementation). | |
94222f64 XL |
175 | // It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`, |
176 | // `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait), | |
177 | // while delegating on simple equality for the other `Predicate`. | |
dfeec247 XL |
178 | // This implementation solves (Issue #59497) and (Issue #58311). |
179 | // It is unclear to me at the moment whether the approach based on `relate` | |
180 | // could be extended easily also to the other `Predicate`. | |
f9f354fc | 181 | let predicate_matches_closure = |p: Predicate<'tcx>| { |
dfeec247 | 182 | let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); |
5869c6ff XL |
183 | let predicate = predicate.kind(); |
184 | let p = p.kind(); | |
29967ef6 | 185 | match (predicate.skip_binder(), p.skip_binder()) { |
94222f64 | 186 | (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { |
5099ac24 FG |
187 | // Since struct predicates cannot have ~const, project the impl predicate |
188 | // onto one that ignores the constness. This is equivalent to saying that | |
189 | // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` | |
190 | // in the impl. | |
191 | let non_const_a = | |
192 | ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; | |
193 | relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() | |
f9f354fc | 194 | } |
5869c6ff | 195 | (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { |
29967ef6 | 196 | relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() |
dfeec247 | 197 | } |
94222f64 XL |
198 | ( |
199 | ty::PredicateKind::ConstEvaluatable(a), | |
200 | ty::PredicateKind::ConstEvaluatable(b), | |
5e7ed085 | 201 | ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), |
c295e0f8 XL |
202 | ( |
203 | ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), | |
204 | ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), | |
205 | ) => { | |
206 | relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok() | |
207 | && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok() | |
94222f64 | 208 | } |
064997fb FG |
209 | (ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => { |
210 | relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok() | |
211 | } | |
dfeec247 XL |
212 | _ => predicate == p, |
213 | } | |
214 | }; | |
215 | ||
f9f354fc | 216 | if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) { |
5099ac24 | 217 | let item_span = tcx.def_span(self_type_did); |
f9f354fc | 218 | let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id()); |
5e7ed085 | 219 | let reported = struct_span_err!( |
0bf4aa26 | 220 | tcx.sess, |
f9f354fc | 221 | predicate_sp, |
0bf4aa26 | 222 | E0367, |
04454e1e | 223 | "`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not", |
0bf4aa26 | 224 | ) |
dfeec247 XL |
225 | .span_note(item_span, "the implementor must specify the same requirement") |
226 | .emit(); | |
5e7ed085 | 227 | result = Err(reported); |
c34b1796 AL |
228 | } |
229 | } | |
85aaf69f | 230 | |
8bb4bdeb | 231 | result |
c34b1796 | 232 | } |
85aaf69f | 233 | |
dfeec247 XL |
234 | // This is an implementation of the TypeRelation trait with the |
235 | // aim of simply comparing for equality (without side-effects). | |
236 | // It is not intended to be used anywhere else other than here. | |
923072b8 | 237 | pub(crate) struct SimpleEqRelation<'tcx> { |
dfeec247 XL |
238 | tcx: TyCtxt<'tcx>, |
239 | param_env: ty::ParamEnv<'tcx>, | |
240 | } | |
241 | ||
242 | impl<'tcx> SimpleEqRelation<'tcx> { | |
243 | fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> { | |
244 | SimpleEqRelation { tcx, param_env } | |
245 | } | |
246 | } | |
247 | ||
a2a8927a | 248 | impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { |
dfeec247 XL |
249 | fn tcx(&self) -> TyCtxt<'tcx> { |
250 | self.tcx | |
251 | } | |
252 | ||
253 | fn param_env(&self) -> ty::ParamEnv<'tcx> { | |
254 | self.param_env | |
255 | } | |
256 | ||
257 | fn tag(&self) -> &'static str { | |
258 | "dropck::SimpleEqRelation" | |
259 | } | |
260 | ||
261 | fn a_is_expected(&self) -> bool { | |
262 | true | |
263 | } | |
264 | ||
265 | fn relate_with_variance<T: Relate<'tcx>>( | |
266 | &mut self, | |
267 | _: ty::Variance, | |
17df50a5 | 268 | _info: ty::VarianceDiagInfo<'tcx>, |
f035d41b XL |
269 | a: T, |
270 | b: T, | |
dfeec247 XL |
271 | ) -> RelateResult<'tcx, T> { |
272 | // Here we ignore variance because we require drop impl's types | |
273 | // to be *exactly* the same as to the ones in the struct definition. | |
274 | self.relate(a, b) | |
275 | } | |
276 | ||
277 | fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { | |
278 | debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b); | |
279 | ty::relate::super_relate_tys(self, a, b) | |
280 | } | |
281 | ||
282 | fn regions( | |
283 | &mut self, | |
284 | a: ty::Region<'tcx>, | |
285 | b: ty::Region<'tcx>, | |
286 | ) -> RelateResult<'tcx, ty::Region<'tcx>> { | |
287 | debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b); | |
288 | ||
289 | // We can just equate the regions because LBRs have been | |
290 | // already anonymized. | |
291 | if a == b { | |
292 | Ok(a) | |
293 | } else { | |
294 | // I'm not sure is this `TypeError` is the right one, but | |
295 | // it should not matter as it won't be checked (the dropck | |
296 | // will emit its own, more informative and higher-level errors | |
297 | // in case anything goes wrong). | |
298 | Err(TypeError::RegionsPlaceholderMismatch) | |
299 | } | |
300 | } | |
301 | ||
302 | fn consts( | |
303 | &mut self, | |
5099ac24 FG |
304 | a: ty::Const<'tcx>, |
305 | b: ty::Const<'tcx>, | |
306 | ) -> RelateResult<'tcx, ty::Const<'tcx>> { | |
dfeec247 XL |
307 | debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b); |
308 | ty::relate::super_relate_consts(self, a, b) | |
309 | } | |
310 | ||
311 | fn binders<T>( | |
312 | &mut self, | |
cdc7bbd5 XL |
313 | a: ty::Binder<'tcx, T>, |
314 | b: ty::Binder<'tcx, T>, | |
315 | ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> | |
dfeec247 XL |
316 | where |
317 | T: Relate<'tcx>, | |
318 | { | |
319 | debug!("SimpleEqRelation::binders({:?}: {:?}", a, b); | |
320 | ||
321 | // Anonymizing the LBRs is necessary to solve (Issue #59497). | |
322 | // After we do so, it should be totally fine to skip the binders. | |
064997fb FG |
323 | let anon_a = self.tcx.anonymize_bound_vars(a); |
324 | let anon_b = self.tcx.anonymize_bound_vars(b); | |
dfeec247 XL |
325 | self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; |
326 | ||
3dfed10e | 327 | Ok(a) |
dfeec247 XL |
328 | } |
329 | } |