Skip to content

Commit ee45388

Browse files
committed
Auto merge of rust-lang#119820 - lcnr:leak-check-2, r=<try>
instantiate higher ranked goals outside of candidate selection, remove the `coherence_leak_check` future compat lint - rust-lang#59490 - rust-lang#56105 --- and adapt the old solver to not rely on universe errors for higher ranked goals to impact candidate selection. This matches the behavior of the new solver: rust-lang/trait-system-refactor-initiative#34. See https://hackmd.io/gstwC83ORaG7V29w54nMdA for more details about this change r? `@nikomatsakis`
2 parents bf3c6c5 + 1352b2e commit ee45388

File tree

49 files changed

+463
-576
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+463
-576
lines changed

compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_index::IndexVec;
77
use rustc_middle::traits::specialization_graph::OverlapMode;
88
use rustc_middle::ty::{self, TyCtxt};
99
use rustc_span::{ErrorGuaranteed, Symbol};
10-
use rustc_trait_selection::traits::{self, SkipLeakCheck};
10+
use rustc_trait_selection::traits;
1111
use smallvec::SmallVec;
1212
use std::collections::hash_map::Entry;
1313

@@ -145,15 +145,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
145145
impl1_def_id: DefId,
146146
impl2_def_id: DefId,
147147
) -> Result<(), ErrorGuaranteed> {
148-
let maybe_overlap = traits::overlapping_impls(
149-
self.tcx,
150-
impl1_def_id,
151-
impl2_def_id,
152-
// We go ahead and just skip the leak check for
153-
// inherent impls without warning.
154-
SkipLeakCheck::Yes,
155-
overlap_mode,
156-
);
148+
let maybe_overlap =
149+
traits::overlapping_impls(self.tcx, impl1_def_id, impl2_def_id, overlap_mode);
157150

158151
if let Some(overlap) = maybe_overlap {
159152
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap)

compiler/rustc_infer/src/infer/at.rs

-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ impl<'tcx> InferCtxt<'tcx> {
7878
tcx: self.tcx,
7979
defining_use_anchor: self.defining_use_anchor,
8080
considering_regions: self.considering_regions,
81-
skip_leak_check: self.skip_leak_check,
8281
inner: self.inner.clone(),
8382
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
8483
selection_cache: self.selection_cache.clone(),

compiler/rustc_infer/src/infer/mod.rs

-15
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,6 @@ pub struct InferCtxt<'tcx> {
258258
/// solving is left to borrowck instead.
259259
pub considering_regions: bool,
260260

261-
/// If set, this flag causes us to skip the 'leak check' during
262-
/// higher-ranked subtyping operations. This flag is a temporary one used
263-
/// to manage the removal of the leak-check: for the time being, we still run the
264-
/// leak-check, but we issue warnings.
265-
skip_leak_check: bool,
266-
267261
pub inner: RefCell<InferCtxtInner<'tcx>>,
268262

269263
/// Once region inference is done, the values for each variable.
@@ -611,7 +605,6 @@ pub struct InferCtxtBuilder<'tcx> {
611605
tcx: TyCtxt<'tcx>,
612606
defining_use_anchor: DefiningAnchor,
613607
considering_regions: bool,
614-
skip_leak_check: bool,
615608
/// Whether we are in coherence mode.
616609
intercrate: bool,
617610
/// Whether we should use the new trait solver in the local inference context,
@@ -629,7 +622,6 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
629622
tcx: self,
630623
defining_use_anchor: DefiningAnchor::Error,
631624
considering_regions: true,
632-
skip_leak_check: false,
633625
intercrate: false,
634626
next_trait_solver: self.next_trait_solver_globally(),
635627
}
@@ -663,11 +655,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
663655
self
664656
}
665657

666-
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
667-
self.skip_leak_check = skip_leak_check;
668-
self
669-
}
670-
671658
/// Given a canonical value `C` as a starting point, create an
672659
/// inference context that contains each of the bound values
673660
/// within instantiated as a fresh variable. The `f` closure is
@@ -693,15 +680,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
693680
tcx,
694681
defining_use_anchor,
695682
considering_regions,
696-
skip_leak_check,
697683
intercrate,
698684
next_trait_solver,
699685
} = *self;
700686
InferCtxt {
701687
tcx,
702688
defining_use_anchor,
703689
considering_regions,
704-
skip_leak_check,
705690
inner: RefCell::new(InferCtxtInner::new()),
706691
lexical_region_resolutions: RefCell::new(None),
707692
selection_cache: Default::default(),

compiler/rustc_infer/src/infer/relate/higher_ranked.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,11 @@ impl<'tcx> InferCtxt<'tcx> {
116116
outer_universe: ty::UniverseIndex,
117117
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
118118
) -> RelateResult<'tcx, ()> {
119-
// If the user gave `-Zno-leak-check`, or we have been
120-
// configured to skip the leak check, then skip the leak check
121-
// completely. The leak check is deprecated. Any legitimate
122-
// subtyping errors that it would have caught will now be
123-
// caught later on, during region checking. However, we
124-
// continue to use it for a transition period.
125-
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check {
119+
// If the user gave `-Zno-leak-check`, then skip the leak check
120+
// completely. While we considered to remove the leak check at
121+
// some point, we are now confident that it will remain in some
122+
// form or another.
123+
if self.tcx.sess.opts.unstable_opts.no_leak_check {
126124
return Ok(());
127125
}
128126

compiler/rustc_lint/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,11 @@ fn register_builtins(store: &mut LintStore) {
416416
"converted into hard error, see issue #48950 \
417417
<https://github.com/rust-lang/rust/issues/48950> for more information",
418418
);
419+
store.register_removed(
420+
"coherence_leak_check",
421+
"no longer a warning, see issue #119820 \
422+
<https://github.com/rust-lang/rust/issues/119820> for more information",
423+
);
419424
store.register_removed(
420425
"resolve_trait_on_defaulted_unit",
421426
"converted into hard error, see issue #48950 \

compiler/rustc_lint_defs/src/builtin.rs

-41
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ declare_lint_pass! {
2525
BREAK_WITH_LABEL_AND_LOOP,
2626
BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
2727
CENUM_IMPL_DROP_CAST,
28-
COHERENCE_LEAK_CHECK,
2928
CONFLICTING_REPR_HINTS,
3029
CONST_EVALUATABLE_UNCHECKED,
3130
CONST_ITEM_MUTATION,
@@ -1467,46 +1466,6 @@ declare_lint! {
14671466
};
14681467
}
14691468

1470-
declare_lint! {
1471-
/// The `coherence_leak_check` lint detects conflicting implementations of
1472-
/// a trait that are only distinguished by the old leak-check code.
1473-
///
1474-
/// ### Example
1475-
///
1476-
/// ```rust
1477-
/// trait SomeTrait { }
1478-
/// impl SomeTrait for for<'a> fn(&'a u8) { }
1479-
/// impl<'a> SomeTrait for fn(&'a u8) { }
1480-
/// ```
1481-
///
1482-
/// {{produces}}
1483-
///
1484-
/// ### Explanation
1485-
///
1486-
/// In the past, the compiler would accept trait implementations for
1487-
/// identical functions that differed only in where the lifetime binder
1488-
/// appeared. Due to a change in the borrow checker implementation to fix
1489-
/// several bugs, this is no longer allowed. However, since this affects
1490-
/// existing code, this is a [future-incompatible] lint to transition this
1491-
/// to a hard error in the future.
1492-
///
1493-
/// Code relying on this pattern should introduce "[newtypes]",
1494-
/// like `struct Foo(for<'a> fn(&'a u8))`.
1495-
///
1496-
/// See [issue #56105] for more details.
1497-
///
1498-
/// [issue #56105]: https://github.com/rust-lang/rust/issues/56105
1499-
/// [newtypes]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
1500-
/// [future-incompatible]: ../index.md#future-incompatible-lints
1501-
pub COHERENCE_LEAK_CHECK,
1502-
Warn,
1503-
"distinct impls distinguished only by the leak-check code",
1504-
@future_incompatible = FutureIncompatibleInfo {
1505-
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
1506-
reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>",
1507-
};
1508-
}
1509-
15101469
declare_lint! {
15111470
/// The `deprecated` lint detects use of deprecated items.
15121471
///

compiler/rustc_trait_selection/src/traits/coherence.rs

+14-24
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt;
1414
use crate::traits::select::IntercrateAmbiguityCause;
1515
use crate::traits::structural_normalize::StructurallyNormalizeExt;
1616
use crate::traits::NormalizeExt;
17-
use crate::traits::SkipLeakCheck;
1817
use crate::traits::{
1918
Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
2019
SelectionContext,
@@ -85,12 +84,11 @@ impl TrackAmbiguityCauses {
8584
/// If there are types that satisfy both impls, returns `Some`
8685
/// with a suitably-freshened `ImplHeader` with those types
8786
/// substituted. Otherwise, returns `None`.
88-
#[instrument(skip(tcx, skip_leak_check), level = "debug")]
87+
#[instrument(skip(tcx), level = "debug")]
8988
pub fn overlapping_impls(
9089
tcx: TyCtxt<'_>,
9190
impl1_def_id: DefId,
9291
impl2_def_id: DefId,
93-
skip_leak_check: SkipLeakCheck,
9492
overlap_mode: OverlapMode,
9593
) -> Option<OverlapResult<'_>> {
9694
// Before doing expensive operations like entering an inference context, do
@@ -115,27 +113,14 @@ pub fn overlapping_impls(
115113
return None;
116114
}
117115

118-
let _overlap_with_bad_diagnostics = overlap(
119-
tcx,
120-
TrackAmbiguityCauses::No,
121-
skip_leak_check,
122-
impl1_def_id,
123-
impl2_def_id,
124-
overlap_mode,
125-
)?;
116+
let _overlap_with_bad_diagnostics =
117+
overlap(tcx, TrackAmbiguityCauses::No, impl1_def_id, impl2_def_id, overlap_mode)?;
126118

127119
// In the case where we detect an error, run the check again, but
128120
// this time tracking intercrate ambiguity causes for better
129121
// diagnostics. (These take time and can lead to false errors.)
130-
let overlap = overlap(
131-
tcx,
132-
TrackAmbiguityCauses::Yes,
133-
skip_leak_check,
134-
impl1_def_id,
135-
impl2_def_id,
136-
overlap_mode,
137-
)
138-
.unwrap();
122+
let overlap =
123+
overlap(tcx, TrackAmbiguityCauses::Yes, impl1_def_id, impl2_def_id, overlap_mode).unwrap();
139124
Some(overlap)
140125
}
141126

@@ -177,7 +162,6 @@ fn fresh_impl_header_normalized<'tcx>(
177162
fn overlap<'tcx>(
178163
tcx: TyCtxt<'tcx>,
179164
track_ambiguity_causes: TrackAmbiguityCauses,
180-
skip_leak_check: SkipLeakCheck,
181165
impl1_def_id: DefId,
182166
impl2_def_id: DefId,
183167
overlap_mode: OverlapMode,
@@ -193,7 +177,6 @@ fn overlap<'tcx>(
193177
let infcx = tcx
194178
.infer_ctxt()
195179
.with_opaque_type_inference(DefiningAnchor::Bubble)
196-
.skip_leak_check(skip_leak_check.is_yes())
197180
.intercrate(true)
198181
.with_next_trait_solver(tcx.next_trait_solver_in_coherence())
199182
.build();
@@ -231,8 +214,15 @@ fn overlap<'tcx>(
231214
}
232215
}
233216

234-
// We toggle the `leak_check` by using `skip_leak_check` when constructing the
235-
// inference context, so this may be a noop.
217+
// Detect any region errors caused by equating these two impls.
218+
//
219+
// Only higher ranked region errors are possible here, given that we
220+
// replaced all parameter regions with existentials.
221+
//
222+
// Unlike a full region check, which sometimes incompletely handles
223+
// `TypeOutlives` constraints, the leak check is a complete. While the
224+
// leak check does not detect all region errors, it never
225+
// fails in cases which would later pass full region checking.
236226
if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
237227
debug!("overlap: leak check failed");
238228
return None;

compiler/rustc_trait_selection/src/traits/mod.rs

-18
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,6 @@ pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upca
7171

7272
pub use rustc_infer::traits::*;
7373

74-
/// Whether to skip the leak check, as part of a future compatibility warning step.
75-
///
76-
/// The "default" for skip-leak-check corresponds to the current
77-
/// behavior (do not skip the leak check) -- not the behavior we are
78-
/// transitioning into.
79-
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
80-
pub enum SkipLeakCheck {
81-
Yes,
82-
#[default]
83-
No,
84-
}
85-
86-
impl SkipLeakCheck {
87-
fn is_yes(self) -> bool {
88-
self == SkipLeakCheck::Yes
89-
}
90-
}
91-
9274
/// The mode that trait queries run in.
9375
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9476
pub enum TraitQueryMode {

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

+36-12
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
419419
let mut no_candidates_apply = true;
420420

421421
for c in candidate_set.vec.iter() {
422-
if self.evaluate_candidate(stack, c)?.may_apply() {
422+
if self.evaluate_candidate(stack, c, false)?.may_apply() {
423423
no_candidates_apply = false;
424424
break;
425425
}
@@ -490,7 +490,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
490490
// is needed for specialization. Propagate overflow if it occurs.
491491
let mut candidates = candidates
492492
.into_iter()
493-
.map(|c| match self.evaluate_candidate(stack, &c) {
493+
.map(|c| match self.evaluate_candidate(stack, &c, false) {
494494
Ok(eval) if eval.may_apply() => {
495495
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
496496
}
@@ -580,7 +580,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
580580
obligation: &PredicateObligation<'tcx>,
581581
) -> Result<EvaluationResult, OverflowError> {
582582
debug_assert!(!self.infcx.next_trait_solver());
583-
self.evaluation_probe(|this| {
583+
self.evaluation_probe(|this, _outer_universe| {
584584
let goal =
585585
this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
586586
let mut result = this.evaluate_predicate_recursively(
@@ -596,13 +596,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
596596
})
597597
}
598598

599+
/// Computes the evaluation result of `op`, discarding any constraints.
600+
///
601+
/// This also runs for leak check to allow higher ranked region errors to impact
602+
/// selection. By default it checks for leaks from all universes created inside of
603+
/// `op`, but this can be overwritten if necessary.
599604
fn evaluation_probe(
600605
&mut self,
601-
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
606+
op: impl FnOnce(&mut Self, &mut ty::UniverseIndex) -> Result<EvaluationResult, OverflowError>,
602607
) -> Result<EvaluationResult, OverflowError> {
603608
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
604-
let outer_universe = self.infcx.universe();
605-
let result = op(self)?;
609+
let mut outer_universe = self.infcx.universe();
610+
let result = op(self, &mut outer_universe)?;
606611

607612
match self.infcx.leak_check(outer_universe, Some(snapshot)) {
608613
Ok(()) => {}
@@ -1229,7 +1234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12291234
}
12301235

12311236
match self.candidate_from_obligation(stack) {
1232-
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
1237+
Ok(Some(c)) => self.evaluate_candidate(stack, &c, true),
12331238
Ok(None) => Ok(EvaluatedToAmbig),
12341239
Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical),
12351240
Err(..) => Ok(EvaluatedToErr),
@@ -1264,10 +1269,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12641269
&mut self,
12651270
stack: &TraitObligationStack<'o, 'tcx>,
12661271
candidate: &SelectionCandidate<'tcx>,
1272+
leak_check_higher_ranked_goal: bool,
12671273
) -> Result<EvaluationResult, OverflowError> {
1268-
let mut result = self.evaluation_probe(|this| {
1269-
let candidate = (*candidate).clone();
1270-
match this.confirm_candidate(stack.obligation, candidate) {
1274+
let mut result = self.evaluation_probe(|this, outer_universe| {
1275+
// We eagerly instantiate higher ranked goals to prevent universe errors
1276+
// from impacting candidate selection. This matches the behavior of the new
1277+
// solver. This slightly weakens type inference.
1278+
//
1279+
// In case there are no unresolved type or const variables this
1280+
// should still not be necessary to select a unique impl as any overlap
1281+
// relying on a universe error from higher ranked goals should have resulted
1282+
// in an overlap error in coherence.
1283+
let p = self.infcx.instantiate_binder_with_placeholders(stack.obligation.predicate);
1284+
let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p));
1285+
if !leak_check_higher_ranked_goal {
1286+
*outer_universe = self.infcx.universe();
1287+
}
1288+
match this.confirm_candidate(&obligation, candidate.clone()) {
12711289
Ok(selection) => {
12721290
debug!(?selection);
12731291
this.evaluate_predicates_recursively(
@@ -1704,8 +1722,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
17041722
stack: &TraitObligationStack<'o, 'tcx>,
17051723
where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
17061724
) -> Result<EvaluationResult, OverflowError> {
1707-
self.evaluation_probe(|this| {
1708-
match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
1725+
self.evaluation_probe(|this, outer_universe| {
1726+
// Eagerly instantiate higher ranked goals.
1727+
//
1728+
// See the comment in `evaluate_candidate` to see why.
1729+
let p = self.infcx.instantiate_binder_with_placeholders(stack.obligation.predicate);
1730+
let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p));
1731+
*outer_universe = self.infcx.universe();
1732+
match this.match_where_clause_trait_ref(&obligation, where_clause_trait_ref) {
17091733
Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations),
17101734
Err(()) => Ok(EvaluatedToErr),
17111735
}

0 commit comments

Comments
 (0)