Skip to content

Commit 042d855

Browse files
Fix too restrictive checks on Drop impls
1 parent 2bd28d9 commit 042d855

File tree

1 file changed

+128
-7
lines changed

1 file changed

+128
-7
lines changed

Diff for: src/librustc_typeck/check/dropck.rs

+128-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use crate::check::regionck::RegionCtxt;
22

33
use crate::hir;
44
use crate::hir::def_id::DefId;
5+
use crate::util::common::ErrorReported;
56
use rustc::infer::outlives::env::OutlivesEnvironment;
67
use rustc::infer::{InferOk, SuppressRegionErrors};
78
use rustc::middle::region;
89
use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt};
10+
use rustc::ty::error::TypeError;
11+
use rustc::ty::relate::{Relate, RelateResult, TypeRelation};
912
use rustc::ty::subst::{Subst, SubstsRef};
10-
use rustc::ty::{self, Ty, TyCtxt};
11-
use crate::util::common::ErrorReported;
13+
use rustc::ty::{self, Predicate, Ty, TyCtxt};
1214

1315
use syntax_pos::Span;
1416

@@ -192,6 +194,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
192194
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
193195
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
194196

197+
let self_param_env = tcx.param_env(self_type_did);
198+
195199
// An earlier version of this code attempted to do this checking
196200
// via the traits::fulfill machinery. However, it ran into trouble
197201
// since the fulfill machinery merely turns outlives-predicates
@@ -205,14 +209,35 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
205209
// to take on a structure that is roughly an alpha-renaming of
206210
// the generic parameters of the item definition.)
207211

208-
// This path now just checks *all* predicates via the direct
209-
// lookup, rather than using fulfill machinery.
212+
// This path now just checks *all* predicates via an instantiation of
213+
// the `SimpleEqRelation`, which simply forwards to the `relate` machinery
214+
// after taking care of anonymizing late bound regions.
210215
//
211216
// However, it may be more efficient in the future to batch
212-
// the analysis together via the fulfill , rather than the
213-
// repeated `contains` calls.
217+
// the analysis together via the fulfill (see comment above regarding
218+
// the usage of the fulfill machinery), rather than the
219+
// repeated `.iter().any(..)` calls.
220+
221+
// This closure is a more robust way to check `Predicate` equality
222+
// than simple `==` checks (which were the previous implementation).
223+
// It relies on `ty::relate` for `TraitPredicate` and `ProjectionPredicate`
224+
// (which implement the Relate trait), while delegating on simple equality
225+
// for the other `Predicate`.
226+
// This implementation solves (Issue #59497) and (Issue #58311).
227+
// It is unclear to me at the moment whether the approach based on `relate`
228+
// could be extended easily also to the other `Predicate`.
229+
let predicate_matches_closure = |p: &'_ Predicate<'tcx>| {
230+
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
231+
match (predicate, p) {
232+
(Predicate::Trait(a), Predicate::Trait(b)) => relator.relate(a, b).is_ok(),
233+
(Predicate::Projection(a), Predicate::Projection(b)) => {
234+
relator.relate(a, b).is_ok()
235+
}
236+
_ => predicate == p,
237+
}
238+
};
214239

215-
if !assumptions_in_impl_context.contains(&predicate) {
240+
if !assumptions_in_impl_context.iter().any(predicate_matches_closure) {
216241
let item_span = tcx.hir().span(self_type_hir_id);
217242
struct_span_err!(
218243
tcx.sess,
@@ -251,3 +276,99 @@ crate fn check_drop_obligations<'a, 'tcx>(
251276

252277
Ok(())
253278
}
279+
280+
// This is an implementation of the TypeRelation trait with the
281+
// aim of simply comparing for equality (without side-effects).
282+
// It is not intended to be used anywhere else other than here.
283+
crate struct SimpleEqRelation<'tcx> {
284+
tcx: TyCtxt<'tcx>,
285+
param_env: ty::ParamEnv<'tcx>,
286+
}
287+
288+
impl<'tcx> SimpleEqRelation<'tcx> {
289+
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> {
290+
SimpleEqRelation { tcx, param_env }
291+
}
292+
}
293+
294+
impl TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
295+
fn tcx(&self) -> TyCtxt<'tcx> {
296+
self.tcx
297+
}
298+
299+
fn param_env(&self) -> ty::ParamEnv<'tcx> {
300+
self.param_env
301+
}
302+
303+
fn tag(&self) -> &'static str {
304+
"dropck::SimpleEqRelation"
305+
}
306+
307+
fn a_is_expected(&self) -> bool {
308+
true
309+
}
310+
311+
fn relate_with_variance<T: Relate<'tcx>>(
312+
&mut self,
313+
_: ty::Variance,
314+
a: &T,
315+
b: &T,
316+
) -> RelateResult<'tcx, T> {
317+
// Here we ignore variance because we require drop impl's types
318+
// to be *exactly* the same as to the ones in the struct definition.
319+
self.relate(a, b)
320+
}
321+
322+
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
323+
debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b);
324+
ty::relate::super_relate_tys(self, a, b)
325+
}
326+
327+
fn regions(
328+
&mut self,
329+
a: ty::Region<'tcx>,
330+
b: ty::Region<'tcx>,
331+
) -> RelateResult<'tcx, ty::Region<'tcx>> {
332+
debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b);
333+
334+
// We can just equate the regions because LBRs have been
335+
// already anonymized.
336+
if a == b {
337+
Ok(a)
338+
} else {
339+
// I'm not sure is this `TypeError` is the right one, but
340+
// it should not matter as it won't be checked (the dropck
341+
// will emit its own, more informative and higher-level errors
342+
// in case anything goes wrong).
343+
Err(TypeError::RegionsPlaceholderMismatch)
344+
}
345+
}
346+
347+
fn consts(
348+
&mut self,
349+
a: &'tcx ty::Const<'tcx>,
350+
b: &'tcx ty::Const<'tcx>,
351+
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
352+
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
353+
ty::relate::super_relate_consts(self, a, b)
354+
}
355+
356+
fn binders<T>(
357+
&mut self,
358+
a: &ty::Binder<T>,
359+
b: &ty::Binder<T>,
360+
) -> RelateResult<'tcx, ty::Binder<T>>
361+
where
362+
T: Relate<'tcx>,
363+
{
364+
debug!("SimpleEqRelation::binders({:?}: {:?}", a, b);
365+
366+
// Anonymizing the LBRs is necessary to solve (Issue #59497).
367+
// After we do so, it should be totally fine to skip the binders.
368+
let anon_a = self.tcx.anonymize_late_bound_regions(a);
369+
let anon_b = self.tcx.anonymize_late_bound_regions(b);
370+
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
371+
372+
Ok(a.clone())
373+
}
374+
}

0 commit comments

Comments
 (0)