1 use crate::check
::regionck
::RegionCtxt
;
3 use crate::hir
::def_id
::{DefId, LocalDefId}
;
4 use rustc_errors
::{struct_span_err, ErrorGuaranteed}
;
5 use rustc_infer
::infer
::outlives
::env
::OutlivesEnvironment
;
6 use rustc_infer
::infer
::{InferOk, RegionckMode, TyCtxtInferExt}
;
7 use rustc_infer
::traits
::TraitEngineExt
as _
;
8 use rustc_middle
::ty
::error
::TypeError
;
9 use rustc_middle
::ty
::relate
::{Relate, RelateResult, TypeRelation}
;
10 use rustc_middle
::ty
::subst
::{Subst, SubstsRef}
;
11 use rustc_middle
::ty
::{self, EarlyBinder, Predicate, Ty, TyCtxt}
;
13 use rustc_trait_selection
::traits
::error_reporting
::InferCtxtExt
;
14 use rustc_trait_selection
::traits
::query
::dropck_outlives
::AtExt
;
15 use rustc_trait_selection
::traits
::{ObligationCause, TraitEngine, TraitEngineExt}
;
17 /// This function confirms that the `Drop` implementation identified by
18 /// `drop_impl_did` is not any more specialized than the type it is
19 /// attached to (Issue #8142).
23 /// 1. The self type must be nominal (this is already checked during
26 /// 2. The generic region/type parameters of the impl's self type must
27 /// all be parameters of the Drop impl itself (i.e., no
28 /// specialization like `impl Drop for Foo<i32>`), and,
30 /// 3. Any bounds on the generic parameters must be reflected in the
31 /// struct/enum definition for the nominal type itself (i.e.
32 /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
34 pub fn check_drop_impl(tcx
: TyCtxt
<'_
>, drop_impl_did
: DefId
) -> Result
<(), ErrorGuaranteed
> {
35 let dtor_self_type
= tcx
.type_of(drop_impl_did
);
36 let dtor_predicates
= tcx
.predicates_of(drop_impl_did
);
37 match dtor_self_type
.kind() {
38 ty
::Adt(adt_def
, self_to_impl_substs
) => {
39 ensure_drop_params_and_item_params_correspond(
41 drop_impl_did
.expect_local(),
46 ensure_drop_predicates_are_implied_by_item_defn(
49 adt_def
.did().expect_local(),
54 // Destructors only work on nominal types. This was
55 // already checked by coherence, but compilation may
56 // not have been terminated.
57 let span
= tcx
.def_span(drop_impl_did
);
58 let reported
= tcx
.sess
.delay_span_bug(
60 &format
!("should have been rejected by coherence check: {dtor_self_type}"),
67 fn ensure_drop_params_and_item_params_correspond
<'tcx
>(
69 drop_impl_did
: LocalDefId
,
70 drop_impl_ty
: Ty
<'tcx
>,
72 ) -> Result
<(), ErrorGuaranteed
> {
73 let drop_impl_hir_id
= tcx
.hir().local_def_id_to_hir_id(drop_impl_did
);
75 // check that the impl type can be made to match the trait type.
77 tcx
.infer_ctxt().enter(|ref infcx
| {
78 let impl_param_env
= tcx
.param_env(self_type_did
);
80 let mut fulfillment_cx
= <dyn TraitEngine
<'_
>>::new(tcx
);
82 let named_type
= tcx
.type_of(self_type_did
);
84 let drop_impl_span
= tcx
.def_span(drop_impl_did
);
85 let fresh_impl_substs
=
86 infcx
.fresh_substs_for_item(drop_impl_span
, drop_impl_did
.to_def_id());
87 let fresh_impl_self_ty
= EarlyBinder(drop_impl_ty
).subst(tcx
, fresh_impl_substs
);
89 let cause
= &ObligationCause
::misc(drop_impl_span
, drop_impl_hir_id
);
90 match infcx
.at(cause
, impl_param_env
).eq(named_type
, fresh_impl_self_ty
) {
91 Ok(InferOk { obligations, .. }
) => {
92 fulfillment_cx
.register_predicate_obligations(infcx
, obligations
);
95 let item_span
= tcx
.def_span(self_type_did
);
96 let self_descr
= tcx
.def_kind(self_type_did
).descr(self_type_did
);
97 let reported
= struct_span_err
!(
101 "`Drop` impls cannot be specialized"
106 "use the same sequence of generic type, lifetime and const parameters \
107 as the {self_descr} definition",
111 return Err(reported
);
115 let errors
= fulfillment_cx
.select_all_or_error(&infcx
);
116 if !errors
.is_empty() {
117 // this could be reached when we get lazy normalization
118 let reported
= infcx
.report_fulfillment_errors(&errors
, None
, false);
119 return Err(reported
);
122 // NB. It seems a bit... suspicious to use an empty param-env
123 // here. The correct thing, I imagine, would be
124 // `OutlivesEnvironment::new(impl_param_env)`, which would
125 // allow region solving to take any `a: 'b` relations on the
126 // impl into account. But I could not create a test case where
127 // it did the wrong thing, so I chose to preserve existing
128 // behavior, since it ought to be simply more
129 // conservative. -nmatsakis
130 let outlives_env
= OutlivesEnvironment
::new(ty
::ParamEnv
::empty());
132 infcx
.resolve_regions_and_report_errors(
133 drop_impl_did
.to_def_id(),
135 RegionckMode
::default(),
141 /// Confirms that every predicate imposed by dtor_predicates is
142 /// implied by assuming the predicates attached to self_type_did.
143 fn ensure_drop_predicates_are_implied_by_item_defn
<'tcx
>(
145 dtor_predicates
: ty
::GenericPredicates
<'tcx
>,
146 self_type_did
: LocalDefId
,
147 self_to_impl_substs
: SubstsRef
<'tcx
>,
148 ) -> Result
<(), ErrorGuaranteed
> {
149 let mut result
= Ok(());
151 // Here is an example, analogous to that from
152 // `compare_impl_method`.
154 // Consider a struct type:
156 // struct Type<'c, 'b:'c, 'a> {
157 // x: &'a Contents // (contents are irrelevant;
158 // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
163 // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
164 // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
167 // We start out with self_to_impl_substs, that maps the generic
168 // parameters of Type to that of the Drop impl.
170 // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
172 // Applying this to the predicates (i.e., assumptions) provided by the item
173 // definition yields the instantiated assumptions:
177 // We then check all of the predicates of the Drop impl:
181 // and ensure each is in the list of instantiated
182 // assumptions. Here, `'y:'z` is present, but `'x:'y` is
183 // absent. So we report an error that the Drop impl injected a
184 // predicate that is not present on the struct definition.
186 // We can assume the predicates attached to struct/enum definition
188 let generic_assumptions
= tcx
.predicates_of(self_type_did
);
190 let assumptions_in_impl_context
= generic_assumptions
.instantiate(tcx
, &self_to_impl_substs
);
191 let assumptions_in_impl_context
= assumptions_in_impl_context
.predicates
;
193 let self_param_env
= tcx
.param_env(self_type_did
);
195 // An earlier version of this code attempted to do this checking
196 // via the traits::fulfill machinery. However, it ran into trouble
197 // since the fulfill machinery merely turns outlives-predicates
198 // 'a:'b and T:'b into region inference constraints. It is simpler
199 // just to look for all the predicates directly.
201 assert_eq
!(dtor_predicates
.parent
, None
);
202 for &(predicate
, predicate_sp
) in dtor_predicates
.predicates
{
203 // (We do not need to worry about deep analysis of type
204 // expressions etc because the Drop impls are already forced
205 // to take on a structure that is roughly an alpha-renaming of
206 // the generic parameters of the item definition.)
208 // This path now just checks *all* predicates via an instantiation of
209 // the `SimpleEqRelation`, which simply forwards to the `relate` machinery
210 // after taking care of anonymizing late bound regions.
212 // However, it may be more efficient in the future to batch
213 // the analysis together via the fulfill (see comment above regarding
214 // the usage of the fulfill machinery), rather than the
215 // repeated `.iter().any(..)` calls.
217 // This closure is a more robust way to check `Predicate` equality
218 // than simple `==` checks (which were the previous implementation).
219 // It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`,
220 // `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait),
221 // while delegating on simple equality for the other `Predicate`.
222 // This implementation solves (Issue #59497) and (Issue #58311).
223 // It is unclear to me at the moment whether the approach based on `relate`
224 // could be extended easily also to the other `Predicate`.
225 let predicate_matches_closure
= |p
: Predicate
<'tcx
>| {
226 let mut relator
: SimpleEqRelation
<'tcx
> = SimpleEqRelation
::new(tcx
, self_param_env
);
227 let predicate
= predicate
.kind();
229 match (predicate
.skip_binder(), p
.skip_binder()) {
230 (ty
::PredicateKind
::Trait(a
), ty
::PredicateKind
::Trait(b
)) => {
231 // Since struct predicates cannot have ~const, project the impl predicate
232 // onto one that ignores the constness. This is equivalent to saying that
233 // we match a `Trait` bound on the struct with a `Trait` or `~const Trait`
236 ty
::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }
;
237 relator
.relate(predicate
.rebind(non_const_a
), p
.rebind(b
)).is_ok()
239 (ty
::PredicateKind
::Projection(a
), ty
::PredicateKind
::Projection(b
)) => {
240 relator
.relate(predicate
.rebind(a
), p
.rebind(b
)).is_ok()
243 ty
::PredicateKind
::ConstEvaluatable(a
),
244 ty
::PredicateKind
::ConstEvaluatable(b
),
245 ) => tcx
.try_unify_abstract_consts(self_param_env
.and((a
, b
))),
247 ty
::PredicateKind
::TypeOutlives(ty
::OutlivesPredicate(ty_a
, lt_a
)),
248 ty
::PredicateKind
::TypeOutlives(ty
::OutlivesPredicate(ty_b
, lt_b
)),
250 relator
.relate(predicate
.rebind(ty_a
), p
.rebind(ty_b
)).is_ok()
251 && relator
.relate(predicate
.rebind(lt_a
), p
.rebind(lt_b
)).is_ok()
257 if !assumptions_in_impl_context
.iter().copied().any(predicate_matches_closure
) {
258 let item_span
= tcx
.def_span(self_type_did
);
259 let self_descr
= tcx
.def_kind(self_type_did
).descr(self_type_did
.to_def_id());
260 let reported
= struct_span_err
!(
264 "`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not",
266 .span_note(item_span
, "the implementor must specify the same requirement")
268 result
= Err(reported
);
275 /// This function is not only checking that the dropck obligations are met for
276 /// the given type, but it's also currently preventing non-regular recursion in
277 /// types from causing stack overflows (dropck_no_diverge_on_nonregular_*.rs).
278 crate fn check_drop_obligations
<'a
, 'tcx
>(
279 rcx
: &mut RegionCtxt
<'a
, 'tcx
>,
284 debug
!("check_drop_obligations typ: {:?}", ty
);
286 let cause
= &ObligationCause
::misc(span
, body_id
);
287 let infer_ok
= rcx
.infcx
.at(cause
, rcx
.fcx
.param_env
).dropck_outlives(ty
);
288 debug
!("dropck_outlives = {:#?}", infer_ok
);
289 rcx
.fcx
.register_infer_ok_obligations(infer_ok
);
292 // This is an implementation of the TypeRelation trait with the
293 // aim of simply comparing for equality (without side-effects).
294 // It is not intended to be used anywhere else other than here.
295 crate struct SimpleEqRelation
<'tcx
> {
297 param_env
: ty
::ParamEnv
<'tcx
>,
300 impl<'tcx
> SimpleEqRelation
<'tcx
> {
301 fn new(tcx
: TyCtxt
<'tcx
>, param_env
: ty
::ParamEnv
<'tcx
>) -> SimpleEqRelation
<'tcx
> {
302 SimpleEqRelation { tcx, param_env }
306 impl<'tcx
> TypeRelation
<'tcx
> for SimpleEqRelation
<'tcx
> {
307 fn tcx(&self) -> TyCtxt
<'tcx
> {
311 fn param_env(&self) -> ty
::ParamEnv
<'tcx
> {
315 fn tag(&self) -> &'
static str {
316 "dropck::SimpleEqRelation"
319 fn a_is_expected(&self) -> bool
{
323 fn relate_with_variance
<T
: Relate
<'tcx
>>(
326 _info
: ty
::VarianceDiagInfo
<'tcx
>,
329 ) -> RelateResult
<'tcx
, T
> {
330 // Here we ignore variance because we require drop impl's types
331 // to be *exactly* the same as to the ones in the struct definition.
335 fn tys(&mut self, a
: Ty
<'tcx
>, b
: Ty
<'tcx
>) -> RelateResult
<'tcx
, Ty
<'tcx
>> {
336 debug
!("SimpleEqRelation::tys(a={:?}, b={:?})", a
, b
);
337 ty
::relate
::super_relate_tys(self, a
, b
)
344 ) -> RelateResult
<'tcx
, ty
::Region
<'tcx
>> {
345 debug
!("SimpleEqRelation::regions(a={:?}, b={:?})", a
, b
);
347 // We can just equate the regions because LBRs have been
348 // already anonymized.
352 // I'm not sure is this `TypeError` is the right one, but
353 // it should not matter as it won't be checked (the dropck
354 // will emit its own, more informative and higher-level errors
355 // in case anything goes wrong).
356 Err(TypeError
::RegionsPlaceholderMismatch
)
364 ) -> RelateResult
<'tcx
, ty
::Const
<'tcx
>> {
365 debug
!("SimpleEqRelation::consts(a={:?}, b={:?})", a
, b
);
366 ty
::relate
::super_relate_consts(self, a
, b
)
371 a
: ty
::Binder
<'tcx
, T
>,
372 b
: ty
::Binder
<'tcx
, T
>,
373 ) -> RelateResult
<'tcx
, ty
::Binder
<'tcx
, T
>>
377 debug
!("SimpleEqRelation::binders({:?}: {:?}", a
, b
);
379 // Anonymizing the LBRs is necessary to solve (Issue #59497).
380 // After we do so, it should be totally fine to skip the binders.
381 let anon_a
= self.tcx
.anonymize_late_bound_regions(a
);
382 let anon_b
= self.tcx
.anonymize_late_bound_regions(b
);
383 self.relate(anon_a
.skip_binder(), anon_b
.skip_binder())?
;