Skip to content

Commit 37ec98f

Browse files
committed
Auto merge of rust-lang#146725 - lcnr:eager-instantiate-binder, r=BoxyUwU
`-Znext-solver` instantiate predicate binder without recanonicalizing goal This strengthens the leak check to match the old trait solver. The new trait solver now also instantiates higher ranked goals in the same scope as candidate selection, so the leak check in each candidate detects placeholder errors involving this higher ranked goal. E.g. let's look at tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs ```rust trait Trait<T, U> {} impl<'a> Trait<&'a str, &'a str> for () {} impl<'a> Trait<&'a str, String> for () {} fn impls_trait<T: for<'a> Trait<&'a str, U>, U>() {} fn main() { impls_trait::<(), _>(); } ``` Here proving `(): for<'a> Trait<&'a str, ?u>` via `impl<'a> Trait<&'a str, &'a str> for ()` equates `?u` with `&'!a str` which results in a leak check error as `?u` cannot name `'a`. If this leak check error happens while considering candidates we drop the first impl and infer `?u` to `String`. If not, this remains ambiguous. This behavior is a bit iffy, see the FCP proposal in rust-lang#119820 for more details on why this current behavior is somewhat undesirable. However, considering placeholders from higher-ranked goals for candidate selection does allow more code to compile and a lot of the code *feels like it should compile*. **This caused us to revert the change of rust-lang#119820 in rust-lang#127568.** I originally expected that we can avoid breakage with the new solver differently here, e.g. by considering OR-region constraints. However, doing so is a significant change and I don't have a great idea for how that should work. Matching the old solver behavior for now should not make this cleaner approach any more difficult in the future, so let's just go with what actually allows us to stabilize the new solver for now. This PR changing the new solver to match the behavior of the old one wrt the leak check. As the new solver is already used by default in coherence, this allows more code to compile, see `tests/ui/higher-ranked/leak-check/leak-check-in-selection-7-coherence.rs`: ```rust struct W<T, U>(T, U); trait Trait<T> {} // using this impl results in a higher-ranked region error. impl<'a> Trait<W<&'a str, &'a str>> for () {} impl<'a> Trait<W<&'a str, String>> for () {} trait NotString {} impl NotString for &str {} impl NotString for u32 {} trait Overlap<U> {} impl<T: for<'a> Trait<W<&'a str, U>>, U> Overlap<U> for T {} impl<U: NotString> Overlap<U> for () {} fn main() {} ``` This behavior is quite arbitrary and not something I expect users to rely on in practice, however, it should still go through an FCP imo. r? `@BoxyUwU` originally implemented by `@compiler-errors` in rust-lang#136997. Closes rust-lang/trait-system-refactor-initiative#120.
2 parents 695857b + 5508b47 commit 37ec98f

23 files changed

+114
-213
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! A utility module to inspect currently ambiguous obligations in the current context.
22
33
use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
4-
use rustc_middle::traits::solve::GoalSource;
54
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
65
use rustc_span::Span;
76
use rustc_trait_selection::solve::Certainty;
@@ -127,21 +126,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
127126

128127
let tcx = self.fcx.tcx;
129128
let goal = inspect_goal.goal();
130-
if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty)
131-
// We do not push the instantiated forms of goals as it would cause any
132-
// aliases referencing bound vars to go from having escaping bound vars to
133-
// being able to be normalized to an inference variable.
134-
//
135-
// This is mostly just a hack as arbitrary nested goals could still contain
136-
// such aliases while having a different `GoalSource`. Closure signature inference
137-
// however can't really handle *every* higher ranked `Fn` goal also being present
138-
// in the form of `?c: Fn<(<?x as Trait<'!a>>::Assoc)`.
139-
//
140-
// This also just better matches the behaviour of the old solver where we do not
141-
// encounter instantiated forms of goals, only nested goals that referred to bound
142-
// vars from instantiated goals.
143-
&& !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked)
144-
{
129+
if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) {
145130
self.obligations_for_self_ty.push(traits::Obligation::new(
146131
tcx,
147132
self.root_cause.clone(),

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,6 @@ where
297297
// corecursive functions as explained in #136824, relating types never
298298
// introduces a constructor which could cause the recursion to be guarded.
299299
GoalSource::TypeRelating => PathKind::Inductive,
300-
// Instantiating a higher ranked goal can never cause the recursion to be
301-
// guarded and is therefore unproductive.
302-
GoalSource::InstantiateHigherRanked => PathKind::Inductive,
303300
// These goal sources are likely unproductive and can be changed to
304301
// `PathKind::Inductive`. Keeping them as unknown until we're confident
305302
// about this and have an example where it is necessary.
@@ -567,66 +564,56 @@ where
567564
pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
568565
let Goal { param_env, predicate } = goal;
569566
let kind = predicate.kind();
570-
if let Some(kind) = kind.no_bound_vars() {
571-
match kind {
572-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
573-
self.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)
574-
}
575-
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
576-
self.compute_host_effect_goal(Goal { param_env, predicate })
577-
}
578-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
579-
self.compute_projection_goal(Goal { param_env, predicate })
580-
}
581-
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
582-
self.compute_type_outlives_goal(Goal { param_env, predicate })
583-
}
584-
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
585-
self.compute_region_outlives_goal(Goal { param_env, predicate })
586-
}
587-
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
588-
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
589-
}
590-
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
591-
self.compute_unstable_feature_goal(param_env, symbol)
592-
}
593-
ty::PredicateKind::Subtype(predicate) => {
594-
self.compute_subtype_goal(Goal { param_env, predicate })
595-
}
596-
ty::PredicateKind::Coerce(predicate) => {
597-
self.compute_coerce_goal(Goal { param_env, predicate })
598-
}
599-
ty::PredicateKind::DynCompatible(trait_def_id) => {
600-
self.compute_dyn_compatible_goal(trait_def_id)
601-
}
602-
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
603-
self.compute_well_formed_goal(Goal { param_env, predicate: term })
604-
}
605-
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
606-
self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
607-
}
608-
ty::PredicateKind::ConstEquate(_, _) => {
609-
panic!("ConstEquate should not be emitted when `-Znext-solver` is active")
610-
}
611-
ty::PredicateKind::NormalizesTo(predicate) => {
612-
self.compute_normalizes_to_goal(Goal { param_env, predicate })
613-
}
614-
ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self
615-
.compute_alias_relate_goal(Goal {
616-
param_env,
617-
predicate: (lhs, rhs, direction),
618-
}),
619-
ty::PredicateKind::Ambiguous => {
620-
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
621-
}
567+
self.enter_forall(kind, |ecx, kind| match kind {
568+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
569+
ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)
622570
}
623-
} else {
624-
self.enter_forall(kind, |ecx, kind| {
625-
let goal = goal.with(ecx.cx(), ty::Binder::dummy(kind));
626-
ecx.add_goal(GoalSource::InstantiateHigherRanked, goal);
627-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
628-
})
629-
}
571+
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => {
572+
ecx.compute_host_effect_goal(Goal { param_env, predicate })
573+
}
574+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
575+
ecx.compute_projection_goal(Goal { param_env, predicate })
576+
}
577+
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
578+
ecx.compute_type_outlives_goal(Goal { param_env, predicate })
579+
}
580+
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
581+
ecx.compute_region_outlives_goal(Goal { param_env, predicate })
582+
}
583+
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
584+
ecx.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
585+
}
586+
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => {
587+
ecx.compute_unstable_feature_goal(param_env, symbol)
588+
}
589+
ty::PredicateKind::Subtype(predicate) => {
590+
ecx.compute_subtype_goal(Goal { param_env, predicate })
591+
}
592+
ty::PredicateKind::Coerce(predicate) => {
593+
ecx.compute_coerce_goal(Goal { param_env, predicate })
594+
}
595+
ty::PredicateKind::DynCompatible(trait_def_id) => {
596+
ecx.compute_dyn_compatible_goal(trait_def_id)
597+
}
598+
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
599+
ecx.compute_well_formed_goal(Goal { param_env, predicate: term })
600+
}
601+
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
602+
ecx.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
603+
}
604+
ty::PredicateKind::ConstEquate(_, _) => {
605+
panic!("ConstEquate should not be emitted when `-Znext-solver` is active")
606+
}
607+
ty::PredicateKind::NormalizesTo(predicate) => {
608+
ecx.compute_normalizes_to_goal(Goal { param_env, predicate })
609+
}
610+
ty::PredicateKind::AliasRelate(lhs, rhs, direction) => {
611+
ecx.compute_alias_relate_goal(Goal { param_env, predicate: (lhs, rhs, direction) })
612+
}
613+
ty::PredicateKind::Ambiguous => {
614+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
615+
}
616+
})
630617
}
631618

632619
// Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ impl<'tcx> BestObligation<'tcx> {
231231
nested_goal.source(),
232232
GoalSource::ImplWhereBound
233233
| GoalSource::AliasBoundConstCondition
234-
| GoalSource::InstantiateHigherRanked
235234
| GoalSource::AliasWellFormed
236235
) && nested_goal.result().is_err()
237236
},
@@ -523,10 +522,6 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
523522
));
524523
impl_where_bound_count += 1;
525524
}
526-
// Skip over a higher-ranked predicate.
527-
(_, GoalSource::InstantiateHigherRanked) => {
528-
obligation = self.obligation.clone();
529-
}
530525
(ChildMode::PassThrough, _)
531526
| (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
532527
obligation = make_obligation(self.obligation.cause.clone());

compiler/rustc_trait_selection/src/traits/coherence.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -735,9 +735,10 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
735735
// For bound predicates we simply call `infcx.enter_forall`
736736
// and then prove the resulting predicate as a nested goal.
737737
let Goal { param_env, predicate } = goal.goal();
738-
let trait_ref = match predicate.kind().no_bound_vars() {
739-
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
740-
Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)))
738+
let predicate_kind = goal.infcx().enter_forall_and_leak_universe(predicate.kind());
739+
let trait_ref = match predicate_kind {
740+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) => tr.trait_ref,
741+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))
741742
if matches!(
742743
infcx.tcx.def_kind(proj.projection_term.def_id),
743744
DefKind::AssocTy | DefKind::AssocConst

compiler/rustc_type_ir/src/solve/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ pub enum GoalSource {
7878
ImplWhereBound,
7979
/// Const conditions that need to hold for `[const]` alias bounds to hold.
8080
AliasBoundConstCondition,
81-
/// Instantiating a higher-ranked goal and re-proving it.
82-
InstantiateHigherRanked,
8381
/// Predicate required for an alias projection to be well-formed.
8482
/// This is used in three places:
8583
/// 1. projecting to an opaque whose hidden type is already registered in

tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | impl<T> Trait for Box<T> {}
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
1313
|
1414
= note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>`
15-
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<_>`
15+
= note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<<std::boxed::Box<_> as WithAssoc<'a>>::Assoc>`
1616

1717
error: aborting due to 1 previous error
1818

tests/crashes/140609.rs renamed to tests/ui/const-generics/generic_const_exprs/negative-coherence-ice-140609.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
//@ known-bug: #140609
1+
//@ check-pass
22
#![feature(with_negative_coherence)]
33
#![feature(generic_const_exprs)]
4+
#![allow(incomplete_features)]
45
#![crate_type = "lib"]
56
trait Trait {}
67
struct A<const B: bool>;

tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.next.stderr

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ revisions: old next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
23
//@[next] compile-flags: -Znext-solver
3-
//@[old] check-pass
4+
//@ check-pass
45

56
// cc #119820
67

@@ -24,8 +25,11 @@ where
2425
// but are able to successfully use the impl candidate. Without
2526
// the leak check both candidates may apply and we prefer the
2627
// `param_env` candidate in winnowing.
28+
//
29+
// By doing the leak check for each candidate, we discard the `param_env`
30+
// candidate and use the impl candidate instead, allowing this to
31+
// compile.
2732
hr_bound::<&T>();
28-
//[next]~^ ERROR the trait bound `for<'a> &'a &T: Trait` is not satisfied
2933
}
3034

3135
fn main() {}

tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)