Skip to content

Commit ca49a37

Browse files
Reuse the selection context, compute failing obligations first in ambig mode
1 parent d2a14df commit ca49a37

File tree

3 files changed

+80
-88
lines changed

3 files changed

+80
-88
lines changed

compiler/rustc_lint_defs/src/builtin.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4431,6 +4431,8 @@ declare_lint! {
44314431
/// ### Example
44324432
///
44334433
/// ```rust,compile_fail
4434+
/// #![deny(coinductive_overlap_in_coherence)]
4435+
///
44344436
/// use std::borrow::Borrow;
44354437
/// use std::cmp::Ordering;
44364438
/// use std::marker::PhantomData;

compiler/rustc_trait_selection/src/traits/coherence.rs

+65-76
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::infer::outlives::env::OutlivesEnvironment;
88
use crate::infer::InferOk;
99
use crate::traits::outlives_bounds::InferCtxtExt as _;
10-
use crate::traits::select::IntercrateAmbiguityCause;
10+
use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
1111
use crate::traits::util::impl_subject_and_oblig;
1212
use crate::traits::SkipLeakCheck;
1313
use crate::traits::{
@@ -210,16 +210,53 @@ fn overlap<'tcx>(
210210
let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
211211
debug!("overlap: unification check succeeded");
212212

213-
if overlap_mode.use_implicit_negative()
214-
&& impl_intersection_has_impossible_obligation(
215-
selcx,
216-
param_env,
217-
&impl1_header,
218-
impl2_header,
219-
equate_obligations,
220-
)
221-
{
222-
return None;
213+
if overlap_mode.use_implicit_negative() {
214+
for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] {
215+
if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| {
216+
impl_intersection_has_impossible_obligation(
217+
selcx,
218+
param_env,
219+
&impl1_header,
220+
&impl2_header,
221+
&equate_obligations,
222+
)
223+
}) {
224+
if matches!(mode, TreatInductiveCycleAs::Recur) {
225+
let first_local_impl = impl1_header
226+
.impl_def_id
227+
.as_local()
228+
.or(impl2_header.impl_def_id.as_local())
229+
.expect("expected one of the impls to be local");
230+
infcx.tcx.struct_span_lint_hir(
231+
COINDUCTIVE_OVERLAP_IN_COHERENCE,
232+
infcx.tcx.local_def_id_to_hir_id(first_local_impl),
233+
infcx.tcx.def_span(first_local_impl),
234+
"impls that are not considered to overlap may be considered to \
235+
overlap in the future",
236+
|lint| {
237+
lint.span_label(
238+
infcx.tcx.def_span(impl1_header.impl_def_id),
239+
"the first impl is here",
240+
)
241+
.span_label(
242+
infcx.tcx.def_span(impl2_header.impl_def_id),
243+
"the second impl is here",
244+
);
245+
if !failing_obligation.cause.span.is_dummy() {
246+
lint.span_label(
247+
failing_obligation.cause.span,
248+
"this where-clause causes a cycle, but it may be treated \
249+
as possibly holding in the future, causing the impls to overlap",
250+
);
251+
}
252+
lint
253+
},
254+
);
255+
}
256+
257+
return None;
258+
}
259+
}
223260
}
224261

225262
// We toggle the `leak_check` by using `skip_leak_check` when constructing the
@@ -287,78 +324,30 @@ fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
287324
selcx: &mut SelectionContext<'cx, 'tcx>,
288325
param_env: ty::ParamEnv<'tcx>,
289326
impl1_header: &ty::ImplHeader<'tcx>,
290-
impl2_header: ty::ImplHeader<'tcx>,
291-
obligations: PredicateObligations<'tcx>,
292-
) -> bool {
327+
impl2_header: &ty::ImplHeader<'tcx>,
328+
obligations: &PredicateObligations<'tcx>,
329+
) -> Option<PredicateObligation<'tcx>> {
293330
let infcx = selcx.infcx;
294331

295-
let obligation_guaranteed_to_fail = |obligation: &PredicateObligation<'tcx>| {
296-
if infcx.next_trait_solver() {
297-
infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
298-
} else {
299-
// We use `evaluate_root_obligation` to correctly track intercrate
300-
// ambiguity clauses. We cannot use this in the new solver.
301-
selcx.evaluate_root_obligation(obligation).map_or(
302-
false, // Overflow has occurred, and treat the obligation as possibly holding.
303-
|result| !result.may_apply(),
304-
)
305-
}
306-
};
307-
308-
let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates]
332+
[&impl1_header.predicates, &impl2_header.predicates]
309333
.into_iter()
310334
.flatten()
311335
.map(|&(predicate, span)| {
312336
Obligation::new(infcx.tcx, ObligationCause::dummy_with_span(span), param_env, predicate)
313337
})
314-
.chain(obligations)
315-
.find(obligation_guaranteed_to_fail);
316-
317-
if let Some(failing_obligation) = opt_failing_obligation {
318-
// Check the failing obligation once again, treating inductive cycles as
319-
// ambiguous instead of error.
320-
if !infcx.next_trait_solver()
321-
&& SelectionContext::with_treat_inductive_cycle_as_ambiguous(infcx)
322-
.evaluate_root_obligation(&failing_obligation)
323-
.map_or(true, |result| result.may_apply())
324-
{
325-
let first_local_impl = impl1_header
326-
.impl_def_id
327-
.as_local()
328-
.or(impl2_header.impl_def_id.as_local())
329-
.expect("expected one of the impls to be local");
330-
infcx.tcx.struct_span_lint_hir(
331-
COINDUCTIVE_OVERLAP_IN_COHERENCE,
332-
infcx.tcx.local_def_id_to_hir_id(first_local_impl),
333-
infcx.tcx.def_span(first_local_impl),
334-
"impls that are not considered to overlap may be considered to \
335-
overlap in the future",
336-
|lint| {
337-
lint.span_label(
338-
infcx.tcx.def_span(impl1_header.impl_def_id),
339-
"the first impl is here",
340-
)
341-
.span_label(
342-
infcx.tcx.def_span(impl2_header.impl_def_id),
343-
"the second impl is here",
344-
);
345-
if !failing_obligation.cause.span.is_dummy() {
346-
lint.span_label(
347-
failing_obligation.cause.span,
348-
"this where-clause causes a cycle, but it may be treated \
349-
as possibly holding in the future, causing the impls to overlap",
350-
);
351-
}
352-
lint
353-
},
354-
);
355-
}
356-
357-
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
358-
true
359-
} else {
360-
false
361-
}
338+
.chain(obligations.into_iter().cloned())
339+
.find(|obligation: &PredicateObligation<'tcx>| {
340+
if infcx.next_trait_solver() {
341+
infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
342+
} else {
343+
// We use `evaluate_root_obligation` to correctly track intercrate
344+
// ambiguity clauses. We cannot use this in the new solver.
345+
selcx.evaluate_root_obligation(obligation).map_or(
346+
false, // Overflow has occurred, and treat the obligation as possibly holding.
347+
|result| !result.may_apply(),
348+
)
349+
}
350+
})
362351
}
363352

364353
/// Check if both impls can be satisfied by a common type by considering whether

compiler/rustc_trait_selection/src/traits/select/mod.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,8 @@ enum BuiltinImplConditions<'tcx> {
200200
Ambiguous,
201201
}
202202

203-
enum TreatInductiveCycleAs {
203+
#[derive(Copy, Clone)]
204+
pub enum TreatInductiveCycleAs {
204205
Recur,
205206
Ambig,
206207
}
@@ -216,17 +217,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
216217
}
217218
}
218219

219-
pub fn with_treat_inductive_cycle_as_ambiguous(
220-
infcx: &'cx InferCtxt<'tcx>,
221-
) -> SelectionContext<'cx, 'tcx> {
222-
assert!(infcx.intercrate, "this doesn't do caching yet, so don't use it out of intercrate");
223-
SelectionContext {
224-
infcx,
225-
freshener: infcx.freshener(),
226-
intercrate_ambiguity_causes: None,
227-
query_mode: TraitQueryMode::Standard,
228-
treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
229-
}
220+
// Sets the `TreatInductiveCycleAs` mode temporarily in the selection context
221+
pub fn with_treat_inductive_cycle_as<T>(
222+
&mut self,
223+
treat_inductive_cycle: TreatInductiveCycleAs,
224+
f: impl FnOnce(&mut Self) -> T,
225+
) -> T {
226+
let treat_inductive_cycle =
227+
std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle);
228+
let value = f(self);
229+
self.treat_inductive_cycle = treat_inductive_cycle;
230+
value
230231
}
231232

232233
pub fn with_query_mode(

0 commit comments

Comments
 (0)