Skip to content

Commit bcc9aa0

Browse files
authored
Rollup merge of #110577 - compiler-errors:drop-impl-fulfill, r=lcnr
Use fulfillment to check `Drop` impl compatibility Use an `ObligationCtxt` to ensure that a `Drop` impl does not have stricter requirements than the ADT that it's implemented for, rather than using a `SimpleEqRelation` to (more or less) syntactically equate predicates on an ADT with predicates on an impl. r? types ### Some background The old code reads: ```rust // An earlier version of this code attempted to do this checking // via the traits::fulfill machinery. However, it ran into trouble // since the fulfill machinery merely turns outlives-predicates // 'a:'b and T:'b into region inference constraints. It is simpler // just to look for all the predicates directly. ``` I'm not sure what this means, but perhaps in the 8 years since that this comment was written (cc #23638) it's gotten easier to process region constraints after doing fulfillment? I don't know how this logic differs from anything we do in the `compare_impl_item` module. Ironically, later on it says: ```rust // However, it may be more efficient in the future to batch // the analysis together via the fulfill (see comment above regarding // the usage of the fulfill machinery), rather than the // repeated `.iter().any(..)` calls. ``` Also: * Removes `SimpleEqRelation` which was far too syntactical in its relation. * Fixes #110557
2 parents 151a070 + 2e346b6 commit bcc9aa0

17 files changed

+448
-234
lines changed
+91-233
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
22
//
33
// We don't do any drop checking during hir typeck.
4+
use rustc_data_structures::fx::FxHashSet;
45
use rustc_errors::{struct_span_err, ErrorGuaranteed};
5-
use rustc_middle::ty::error::TypeError;
6-
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
6+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
7+
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
78
use rustc_middle::ty::subst::SubstsRef;
89
use rustc_middle::ty::util::IgnoreRegions;
9-
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
10+
use rustc_middle::ty::{self, TyCtxt};
11+
use rustc_trait_selection::traits::{self, ObligationCtxt};
1012

1113
use crate::errors;
1214
use crate::hir::def_id::{DefId, LocalDefId};
@@ -43,21 +45,20 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro
4345
}
4446
}
4547
let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity();
46-
let dtor_predicates = tcx.predicates_of(drop_impl_did);
4748
match dtor_self_type.kind() {
48-
ty::Adt(adt_def, self_to_impl_substs) => {
49+
ty::Adt(adt_def, adt_to_impl_substs) => {
4950
ensure_drop_params_and_item_params_correspond(
5051
tcx,
5152
drop_impl_did.expect_local(),
5253
adt_def.did(),
53-
self_to_impl_substs,
54+
adt_to_impl_substs,
5455
)?;
5556

5657
ensure_drop_predicates_are_implied_by_item_defn(
5758
tcx,
58-
dtor_predicates,
59+
drop_impl_did.expect_local(),
5960
adt_def.did().expect_local(),
60-
self_to_impl_substs,
61+
adt_to_impl_substs,
6162
)
6263
}
6364
_ => {
@@ -78,9 +79,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
7879
tcx: TyCtxt<'tcx>,
7980
drop_impl_did: LocalDefId,
8081
self_type_did: DefId,
81-
drop_impl_substs: SubstsRef<'tcx>,
82+
adt_to_impl_substs: SubstsRef<'tcx>,
8283
) -> Result<(), ErrorGuaranteed> {
83-
let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else {
84+
let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, IgnoreRegions::No) else {
8485
return Ok(())
8586
};
8687

@@ -111,237 +112,94 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
111112
/// implied by assuming the predicates attached to self_type_did.
112113
fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
113114
tcx: TyCtxt<'tcx>,
114-
dtor_predicates: ty::GenericPredicates<'tcx>,
115-
self_type_did: LocalDefId,
116-
self_to_impl_substs: SubstsRef<'tcx>,
115+
drop_impl_def_id: LocalDefId,
116+
adt_def_id: LocalDefId,
117+
adt_to_impl_substs: SubstsRef<'tcx>,
117118
) -> Result<(), ErrorGuaranteed> {
118-
let mut result = Ok(());
119-
120-
// Here is an example, analogous to that from
121-
// `compare_impl_method`.
122-
//
123-
// Consider a struct type:
124-
//
125-
// struct Type<'c, 'b:'c, 'a> {
126-
// x: &'a Contents // (contents are irrelevant;
127-
// y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
128-
// }
129-
//
130-
// and a Drop impl:
131-
//
132-
// impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
133-
// fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
134-
// }
135-
//
136-
// We start out with self_to_impl_substs, that maps the generic
137-
// parameters of Type to that of the Drop impl.
119+
let infcx = tcx.infer_ctxt().build();
120+
let ocx = ObligationCtxt::new(&infcx);
121+
122+
// Take the param-env of the adt and substitute the substs that show up in
123+
// the implementation's self type. This gives us the assumptions that the
124+
// self ty of the implementation is allowed to know just from it being a
125+
// well-formed adt, since that's all we're allowed to assume while proving
126+
// the Drop implementation is not specialized.
138127
//
139-
// self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
140-
//
141-
// Applying this to the predicates (i.e., assumptions) provided by the item
142-
// definition yields the instantiated assumptions:
143-
//
144-
// ['y : 'z]
145-
//
146-
// We then check all of the predicates of the Drop impl:
147-
//
148-
// ['y:'z, 'x:'y]
149-
//
150-
// and ensure each is in the list of instantiated
151-
// assumptions. Here, `'y:'z` is present, but `'x:'y` is
152-
// absent. So we report an error that the Drop impl injected a
153-
// predicate that is not present on the struct definition.
154-
155-
// We can assume the predicates attached to struct/enum definition
156-
// hold.
157-
let generic_assumptions = tcx.predicates_of(self_type_did);
158-
159-
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
160-
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
161-
162-
debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
163-
164-
let self_param_env = tcx.param_env(self_type_did);
165-
166-
// An earlier version of this code attempted to do this checking
167-
// via the traits::fulfill machinery. However, it ran into trouble
168-
// since the fulfill machinery merely turns outlives-predicates
169-
// 'a:'b and T:'b into region inference constraints. It is simpler
170-
// just to look for all the predicates directly.
171-
172-
assert_eq!(dtor_predicates.parent, None);
173-
for &(predicate, predicate_sp) in dtor_predicates.predicates {
174-
// (We do not need to worry about deep analysis of type
175-
// expressions etc because the Drop impls are already forced
176-
// to take on a structure that is roughly an alpha-renaming of
177-
// the generic parameters of the item definition.)
178-
179-
// This path now just checks *all* predicates via an instantiation of
180-
// the `SimpleEqRelation`, which simply forwards to the `relate` machinery
181-
// after taking care of anonymizing late bound regions.
182-
//
183-
// However, it may be more efficient in the future to batch
184-
// the analysis together via the fulfill (see comment above regarding
185-
// the usage of the fulfill machinery), rather than the
186-
// repeated `.iter().any(..)` calls.
128+
// We don't need to normalize this param-env or anything, since we're only
129+
// substituting it with free params, so no additional param-env normalization
130+
// can occur on top of what has been done in the param_env query itself.
131+
let param_env = ty::EarlyBinder(tcx.param_env(adt_def_id))
132+
.subst(tcx, adt_to_impl_substs)
133+
.with_constness(tcx.constness(drop_impl_def_id));
134+
135+
for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) {
136+
let normalize_cause = traits::ObligationCause::misc(span, adt_def_id);
137+
let pred = ocx.normalize(&normalize_cause, param_env, pred);
138+
let cause = traits::ObligationCause::new(span, adt_def_id, traits::DropImpl);
139+
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred));
140+
}
187141

188-
// This closure is a more robust way to check `Predicate` equality
189-
// than simple `==` checks (which were the previous implementation).
190-
// It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`,
191-
// `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait),
192-
// while delegating on simple equality for the other `Predicate`.
193-
// This implementation solves (Issue #59497) and (Issue #58311).
194-
// It is unclear to me at the moment whether the approach based on `relate`
195-
// could be extended easily also to the other `Predicate`.
196-
let predicate_matches_closure = |p: Predicate<'tcx>| {
197-
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
198-
let predicate = predicate.kind();
199-
let p = p.kind();
200-
match (predicate.skip_binder(), p.skip_binder()) {
201-
(
202-
ty::PredicateKind::Clause(ty::Clause::Trait(a)),
203-
ty::PredicateKind::Clause(ty::Clause::Trait(b)),
204-
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
205-
(
206-
ty::PredicateKind::Clause(ty::Clause::Projection(a)),
207-
ty::PredicateKind::Clause(ty::Clause::Projection(b)),
208-
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
209-
(
210-
ty::PredicateKind::ConstEvaluatable(a),
211-
ty::PredicateKind::ConstEvaluatable(b),
212-
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
213-
(
214-
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
215-
ty_a,
216-
lt_a,
217-
))),
218-
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
219-
ty_b,
220-
lt_b,
221-
))),
222-
) => {
223-
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
224-
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
225-
}
226-
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
227-
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
228-
}
229-
_ => predicate == p,
142+
// All of the custom error reporting logic is to preserve parity with the old
143+
// error messages.
144+
//
145+
// They can probably get removed with better treatment of the new `DropImpl`
146+
// obligation cause code, and perhaps some custom logic in `report_region_errors`.
147+
148+
let errors = ocx.select_all_or_error();
149+
if !errors.is_empty() {
150+
let mut guar = None;
151+
let mut root_predicates = FxHashSet::default();
152+
for error in errors {
153+
let root_predicate = error.root_obligation.predicate;
154+
if root_predicates.insert(root_predicate) {
155+
let item_span = tcx.def_span(adt_def_id);
156+
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
157+
guar = Some(
158+
struct_span_err!(
159+
tcx.sess,
160+
error.root_obligation.cause.span,
161+
E0367,
162+
"`Drop` impl requires `{root_predicate}` \
163+
but the {self_descr} it is implemented for does not",
164+
)
165+
.span_note(item_span, "the implementor must specify the same requirement")
166+
.emit(),
167+
);
230168
}
231-
};
232-
233-
if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) {
234-
let item_span = tcx.def_span(self_type_did);
235-
let self_descr = tcx.def_descr(self_type_did.to_def_id());
236-
let reported = struct_span_err!(
237-
tcx.sess,
238-
predicate_sp,
239-
E0367,
240-
"`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not",
241-
)
242-
.span_note(item_span, "the implementor must specify the same requirement")
243-
.emit();
244-
result = Err(reported);
245169
}
170+
return Err(guar.unwrap());
246171
}
247172

248-
result
249-
}
250-
251-
/// This is an implementation of the [`TypeRelation`] trait with the
252-
/// aim of simply comparing for equality (without side-effects).
253-
///
254-
/// It is not intended to be used anywhere else other than here.
255-
pub(crate) struct SimpleEqRelation<'tcx> {
256-
tcx: TyCtxt<'tcx>,
257-
param_env: ty::ParamEnv<'tcx>,
258-
}
259-
260-
impl<'tcx> SimpleEqRelation<'tcx> {
261-
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> {
262-
SimpleEqRelation { tcx, param_env }
263-
}
264-
}
265-
266-
impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
267-
fn tcx(&self) -> TyCtxt<'tcx> {
268-
self.tcx
269-
}
270-
271-
fn param_env(&self) -> ty::ParamEnv<'tcx> {
272-
self.param_env
273-
}
274-
275-
fn tag(&self) -> &'static str {
276-
"dropck::SimpleEqRelation"
277-
}
278-
279-
fn a_is_expected(&self) -> bool {
280-
true
281-
}
282-
283-
fn relate_with_variance<T: Relate<'tcx>>(
284-
&mut self,
285-
_: ty::Variance,
286-
_info: ty::VarianceDiagInfo<'tcx>,
287-
a: T,
288-
b: T,
289-
) -> RelateResult<'tcx, T> {
290-
// Here we ignore variance because we require drop impl's types
291-
// to be *exactly* the same as to the ones in the struct definition.
292-
self.relate(a, b)
293-
}
294-
295-
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
296-
debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b);
297-
ty::relate::super_relate_tys(self, a, b)
298-
}
299-
300-
fn regions(
301-
&mut self,
302-
a: ty::Region<'tcx>,
303-
b: ty::Region<'tcx>,
304-
) -> RelateResult<'tcx, ty::Region<'tcx>> {
305-
debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b);
306-
307-
// We can just equate the regions because LBRs have been
308-
// already anonymized.
309-
if a == b {
310-
Ok(a)
311-
} else {
312-
// I'm not sure is this `TypeError` is the right one, but
313-
// it should not matter as it won't be checked (the dropck
314-
// will emit its own, more informative and higher-level errors
315-
// in case anything goes wrong).
316-
Err(TypeError::RegionsPlaceholderMismatch)
173+
let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env));
174+
if !errors.is_empty() {
175+
let mut guar = None;
176+
for error in errors {
177+
let item_span = tcx.def_span(adt_def_id);
178+
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
179+
let outlives = match error {
180+
RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"),
181+
RegionResolutionError::GenericBoundFailure(_, generic, r) => {
182+
format!("{generic}: {r}")
183+
}
184+
RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"),
185+
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
186+
format!("{b}: {a}", a = tcx.mk_re_var(a))
187+
}
188+
};
189+
guar = Some(
190+
struct_span_err!(
191+
tcx.sess,
192+
error.origin().span(),
193+
E0367,
194+
"`Drop` impl requires `{outlives}` \
195+
but the {self_descr} it is implemented for does not",
196+
)
197+
.span_note(item_span, "the implementor must specify the same requirement")
198+
.emit(),
199+
);
317200
}
201+
return Err(guar.unwrap());
318202
}
319203

320-
fn consts(
321-
&mut self,
322-
a: ty::Const<'tcx>,
323-
b: ty::Const<'tcx>,
324-
) -> RelateResult<'tcx, ty::Const<'tcx>> {
325-
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
326-
ty::relate::super_relate_consts(self, a, b)
327-
}
328-
329-
fn binders<T>(
330-
&mut self,
331-
a: ty::Binder<'tcx, T>,
332-
b: ty::Binder<'tcx, T>,
333-
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
334-
where
335-
T: Relate<'tcx>,
336-
{
337-
debug!("SimpleEqRelation::binders({:?}: {:?}", a, b);
338-
339-
// Anonymizing the LBRs is necessary to solve (Issue #59497).
340-
// After we do so, it should be totally fine to skip the binders.
341-
let anon_a = self.tcx.anonymize_bound_vars(a);
342-
let anon_b = self.tcx.anonymize_bound_vars(b);
343-
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
344-
345-
Ok(a)
346-
}
204+
Ok(())
347205
}

compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ pub enum RegionResolutionError<'tcx> {
102102
),
103103
}
104104

105+
impl<'tcx> RegionResolutionError<'tcx> {
106+
pub fn origin(&self) -> &SubregionOrigin<'tcx> {
107+
match self {
108+
RegionResolutionError::ConcreteFailure(origin, _, _)
109+
| RegionResolutionError::GenericBoundFailure(origin, _, _)
110+
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
111+
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
112+
}
113+
}
114+
}
115+
105116
struct RegionAndOrigin<'tcx> {
106117
region: Region<'tcx>,
107118
origin: SubregionOrigin<'tcx>,

0 commit comments

Comments
 (0)