]>
Commit | Line | Data |
---|---|---|
2b03887a | 1 | // FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. |
064997fb FG |
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 | _ => { | |
6522a427 | 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()) { |
6522a427 EL |
186 | ( |
187 | ty::PredicateKind::Clause(ty::Clause::Trait(a)), | |
188 | ty::PredicateKind::Clause(ty::Clause::Trait(b)), | |
189 | ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), | |
190 | ( | |
191 | ty::PredicateKind::Clause(ty::Clause::Projection(a)), | |
192 | ty::PredicateKind::Clause(ty::Clause::Projection(b)), | |
193 | ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), | |
94222f64 XL |
194 | ( |
195 | ty::PredicateKind::ConstEvaluatable(a), | |
196 | ty::PredicateKind::ConstEvaluatable(b), | |
2b03887a | 197 | ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), |
c295e0f8 | 198 | ( |
6522a427 EL |
199 | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( |
200 | ty_a, | |
201 | lt_a, | |
202 | ))), | |
203 | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( | |
204 | ty_b, | |
205 | lt_b, | |
206 | ))), | |
c295e0f8 XL |
207 | ) => { |
208 | relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok() | |
209 | && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok() | |
94222f64 | 210 | } |
064997fb FG |
211 | (ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => { |
212 | relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok() | |
213 | } | |
dfeec247 XL |
214 | _ => predicate == p, |
215 | } | |
216 | }; | |
217 | ||
f9f354fc | 218 | if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) { |
5099ac24 | 219 | let item_span = tcx.def_span(self_type_did); |
f9f354fc | 220 | let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id()); |
5e7ed085 | 221 | let reported = struct_span_err!( |
0bf4aa26 | 222 | tcx.sess, |
f9f354fc | 223 | predicate_sp, |
0bf4aa26 | 224 | E0367, |
04454e1e | 225 | "`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not", |
0bf4aa26 | 226 | ) |
dfeec247 XL |
227 | .span_note(item_span, "the implementor must specify the same requirement") |
228 | .emit(); | |
5e7ed085 | 229 | result = Err(reported); |
c34b1796 AL |
230 | } |
231 | } | |
85aaf69f | 232 | |
8bb4bdeb | 233 | result |
c34b1796 | 234 | } |
85aaf69f | 235 | |
6522a427 EL |
236 | /// This is an implementation of the [`TypeRelation`] trait with the |
237 | /// aim of simply comparing for equality (without side-effects). | |
238 | /// | |
239 | /// It is not intended to be used anywhere else other than here. | |
923072b8 | 240 | pub(crate) struct SimpleEqRelation<'tcx> { |
dfeec247 XL |
241 | tcx: TyCtxt<'tcx>, |
242 | param_env: ty::ParamEnv<'tcx>, | |
243 | } | |
244 | ||
245 | impl<'tcx> SimpleEqRelation<'tcx> { | |
246 | fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> { | |
247 | SimpleEqRelation { tcx, param_env } | |
248 | } | |
249 | } | |
250 | ||
a2a8927a | 251 | impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { |
dfeec247 XL |
252 | fn tcx(&self) -> TyCtxt<'tcx> { |
253 | self.tcx | |
254 | } | |
255 | ||
6522a427 EL |
256 | fn intercrate(&self) -> bool { |
257 | false | |
258 | } | |
259 | ||
dfeec247 XL |
260 | fn param_env(&self) -> ty::ParamEnv<'tcx> { |
261 | self.param_env | |
262 | } | |
263 | ||
264 | fn tag(&self) -> &'static str { | |
265 | "dropck::SimpleEqRelation" | |
266 | } | |
267 | ||
268 | fn a_is_expected(&self) -> bool { | |
269 | true | |
270 | } | |
271 | ||
6522a427 EL |
272 | fn mark_ambiguous(&mut self) { |
273 | bug!() | |
274 | } | |
275 | ||
dfeec247 XL |
276 | fn relate_with_variance<T: Relate<'tcx>>( |
277 | &mut self, | |
278 | _: ty::Variance, | |
17df50a5 | 279 | _info: ty::VarianceDiagInfo<'tcx>, |
f035d41b XL |
280 | a: T, |
281 | b: T, | |
dfeec247 XL |
282 | ) -> RelateResult<'tcx, T> { |
283 | // Here we ignore variance because we require drop impl's types | |
284 | // to be *exactly* the same as to the ones in the struct definition. | |
285 | self.relate(a, b) | |
286 | } | |
287 | ||
288 | fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { | |
289 | debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b); | |
290 | ty::relate::super_relate_tys(self, a, b) | |
291 | } | |
292 | ||
293 | fn regions( | |
294 | &mut self, | |
295 | a: ty::Region<'tcx>, | |
296 | b: ty::Region<'tcx>, | |
297 | ) -> RelateResult<'tcx, ty::Region<'tcx>> { | |
298 | debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b); | |
299 | ||
300 | // We can just equate the regions because LBRs have been | |
301 | // already anonymized. | |
302 | if a == b { | |
303 | Ok(a) | |
304 | } else { | |
305 | // I'm not sure is this `TypeError` is the right one, but | |
306 | // it should not matter as it won't be checked (the dropck | |
307 | // will emit its own, more informative and higher-level errors | |
308 | // in case anything goes wrong). | |
309 | Err(TypeError::RegionsPlaceholderMismatch) | |
310 | } | |
311 | } | |
312 | ||
313 | fn consts( | |
314 | &mut self, | |
5099ac24 FG |
315 | a: ty::Const<'tcx>, |
316 | b: ty::Const<'tcx>, | |
317 | ) -> RelateResult<'tcx, ty::Const<'tcx>> { | |
dfeec247 XL |
318 | debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b); |
319 | ty::relate::super_relate_consts(self, a, b) | |
320 | } | |
321 | ||
322 | fn binders<T>( | |
323 | &mut self, | |
cdc7bbd5 XL |
324 | a: ty::Binder<'tcx, T>, |
325 | b: ty::Binder<'tcx, T>, | |
326 | ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> | |
dfeec247 XL |
327 | where |
328 | T: Relate<'tcx>, | |
329 | { | |
330 | debug!("SimpleEqRelation::binders({:?}: {:?}", a, b); | |
331 | ||
332 | // Anonymizing the LBRs is necessary to solve (Issue #59497). | |
333 | // After we do so, it should be totally fine to skip the binders. | |
064997fb FG |
334 | let anon_a = self.tcx.anonymize_bound_vars(a); |
335 | let anon_b = self.tcx.anonymize_bound_vars(b); | |
dfeec247 XL |
336 | self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; |
337 | ||
3dfed10e | 338 | Ok(a) |
dfeec247 XL |
339 | } |
340 | } |