Skip to content

Commit bbbbbb4

Browse files
committed
Auto merge of #133502 - lcnr:rust4, r=<try>
[DO NOT MERGE] bootstrap with `-Znext-solver=globally` A revival of #124812. Current status: ~~`./x.py b --stage 2` passes 🎉~~ `try` builds succeed 🎉 🎉 🎉 [first perf run](#133502 (comment)) 👻 ### in-flight changes - ce66d92 is a rebased version of #125334, unsure whether I actually want to land this PR for now - #136824 r? `@ghost`
2 parents 69482e8 + 3c982f3 commit bbbbbb4

File tree

19 files changed

+572
-330
lines changed

19 files changed

+572
-330
lines changed

compiler/rustc_hir_typeck/src/method/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
494494
}
495495
}
496496

497+
// HACK: TODO
498+
let self_ty = {
499+
let infer = self.infcx.next_ty_var(span);
500+
self.demand_eqtype(span, infer, self_ty);
501+
self.select_obligations_where_possible(|_| {});
502+
self.resolve_vars_if_possible(infer)
503+
};
504+
497505
let pick = self.probe_for_name(
498506
probe::Mode::Path,
499507
method_name,

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl<'tcx> InferCtxt<'tcx> {
3333
/// This is *not* expected to be used anywhere except for an implementation of
3434
/// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
3535
/// other usecases (i.e. setting the value of a type var).
36-
#[instrument(level = "debug", skip(self, relation))]
36+
#[instrument(level = "debug", skip(self, relation), fields(structurally_relate_aliases = ?relation.structurally_relate_aliases()))]
3737
pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
3838
&self,
3939
relation: &mut R,

compiler/rustc_next_trait_solver/src/solve/alias_relate.rs

+110-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,19 @@
1515
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
1616
//! relate them structurally.
1717
18+
use rustc_type_ir::data_structures::HashSet;
1819
use rustc_type_ir::inherent::*;
20+
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
1921
use rustc_type_ir::{self as ty, Interner};
2022
use tracing::{instrument, trace};
2123

2224
use crate::delegate::SolverDelegate;
23-
use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
25+
use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult};
26+
27+
enum IgnoreAliases {
28+
Yes,
29+
No,
30+
}
2431

2532
impl<D, I> EvalCtxt<'_, D>
2633
where
@@ -36,6 +43,12 @@ where
3643
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
3744
debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
3845

46+
if self.alias_cannot_name_placeholder_in_rigid(param_env, lhs, rhs)
47+
|| self.alias_cannot_name_placeholder_in_rigid(param_env, rhs, lhs)
48+
{
49+
return Err(NoSolution);
50+
}
51+
3952
// Structurally normalize the lhs.
4053
let lhs = if let Some(alias) = lhs.to_alias_term() {
4154
let term = self.next_term_infer_of_kind(lhs);
@@ -96,4 +109,100 @@ where
96109
}
97110
}
98111
}
112+
113+
/// In case a rigid term refers to a placeholder which is not referenced by the
114+
/// alias, the alias cannot be normalized to that rigid term unless it contains
115+
/// either inference variables or these placeholders are referenced in a term
116+
/// of a `Projection`-clause in the environment.
117+
fn alias_cannot_name_placeholder_in_rigid(
118+
&mut self,
119+
param_env: I::ParamEnv,
120+
rigid_term: I::Term,
121+
alias: I::Term,
122+
) -> bool {
123+
// Check that the rigid term is actually rigid.
124+
if rigid_term.to_alias_term().is_some() || alias.to_alias_term().is_none() {
125+
return false;
126+
}
127+
128+
// If the alias has any type or const inference variables,
129+
// do not try to apply the fast path as these inference variables
130+
// may resolve to something containing placeholders.
131+
if alias.has_non_region_infer() {
132+
return false;
133+
}
134+
135+
let mut referenced_placeholders =
136+
self.collect_placeholders_in_term(rigid_term, IgnoreAliases::Yes);
137+
for clause in param_env.caller_bounds().iter() {
138+
match clause.kind().skip_binder() {
139+
ty::ClauseKind::Projection(ty::ProjectionPredicate { term, .. }) => {
140+
if term.has_non_region_infer() {
141+
return false;
142+
}
143+
144+
let env_term_placeholders =
145+
self.collect_placeholders_in_term(term, IgnoreAliases::No);
146+
#[allow(rustc::potential_query_instability)]
147+
referenced_placeholders.retain(|p| !env_term_placeholders.contains(p));
148+
}
149+
ty::ClauseKind::Trait(_)
150+
| ty::ClauseKind::HostEffect(_)
151+
| ty::ClauseKind::TypeOutlives(_)
152+
| ty::ClauseKind::RegionOutlives(_)
153+
| ty::ClauseKind::ConstArgHasType(..)
154+
| ty::ClauseKind::WellFormed(_)
155+
| ty::ClauseKind::ConstEvaluatable(_) => continue,
156+
}
157+
}
158+
159+
if referenced_placeholders.is_empty() {
160+
return false;
161+
}
162+
163+
let alias_placeholders = self.collect_placeholders_in_term(alias, IgnoreAliases::No);
164+
// If the rigid term references a placeholder not mentioned by the alias,
165+
// they can never unify.
166+
!referenced_placeholders.is_subset(&alias_placeholders)
167+
}
168+
169+
fn collect_placeholders_in_term(
170+
&mut self,
171+
term: I::Term,
172+
ignore_aliases: IgnoreAliases,
173+
) -> HashSet<I::Term> {
174+
// Fast path to avoid walking the term.
175+
if !term.has_placeholders() {
176+
return Default::default();
177+
}
178+
179+
struct PlaceholderCollector<I: Interner> {
180+
ignore_aliases: IgnoreAliases,
181+
placeholders: HashSet<I::Term>,
182+
}
183+
impl<I: Interner> TypeVisitor<I> for PlaceholderCollector<I> {
184+
type Result = ();
185+
186+
fn visit_ty(&mut self, t: I::Ty) {
187+
match t.kind() {
188+
ty::Placeholder(_) => drop(self.placeholders.insert(t.into())),
189+
ty::Alias(..) if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {}
190+
_ => t.super_visit_with(self),
191+
}
192+
}
193+
194+
fn visit_const(&mut self, ct: I::Const) {
195+
match ct.kind() {
196+
ty::ConstKind::Placeholder(_) => drop(self.placeholders.insert(ct.into())),
197+
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_)
198+
if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {}
199+
_ => ct.super_visit_with(self),
200+
}
201+
}
202+
}
203+
204+
let mut visitor = PlaceholderCollector { ignore_aliases, placeholders: Default::default() };
205+
term.visit_with(&mut visitor);
206+
visitor.placeholders
207+
}
99208
}

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

+23-8
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,12 @@ where
789789
/// treat the alias as rigid.
790790
///
791791
/// See trait-system-refactor-initiative#124 for more details.
792-
#[instrument(level = "debug", skip(self), ret)]
792+
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
793793
pub(super) fn merge_candidates(
794794
&mut self,
795795
proven_via: Option<TraitGoalProvenVia>,
796796
candidates: Vec<Candidate<I>>,
797+
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
797798
) -> QueryResult<I> {
798799
let Some(proven_via) = proven_via else {
799800
// We don't care about overflow. If proving the trait goal overflowed, then
@@ -810,13 +811,27 @@ where
810811
// FIXME(const_trait_impl): should this behavior also be used by
811812
// constness checking. Doing so is *at least theoretically* breaking,
812813
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
813-
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => candidates
814-
.iter()
815-
.filter(|c| {
816-
matches!(c.source, CandidateSource::AliasBound | CandidateSource::ParamEnv(_))
817-
})
818-
.map(|c| c.result)
819-
.collect(),
814+
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
815+
let mut candidates_from_env: Vec<_> = candidates
816+
.iter()
817+
.filter(|c| {
818+
matches!(
819+
c.source,
820+
CandidateSource::AliasBound | CandidateSource::ParamEnv(_)
821+
)
822+
})
823+
.map(|c| c.result)
824+
.collect();
825+
826+
// If the trait goal has been proven by using the environment, we want to treat
827+
// aliases as rigid if there are no applicable projection bounds in the environment.
828+
if candidates_from_env.is_empty() {
829+
if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
830+
candidates_from_env.push(response);
831+
}
832+
}
833+
candidates_from_env
834+
}
820835
TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(),
821836
};
822837

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,27 @@ where
154154
let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
155155

156156
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
157+
// Resolve inference variables here to improve the debug output :)
158+
let impl_trait_ref = ecx.resolve_vars_if_possible(impl_trait_ref);
159+
157160
let where_clause_bounds = cx
158161
.predicates_of(impl_def_id)
159162
.iter_instantiated(cx, impl_args)
160163
.map(|pred| goal.with(cx, pred));
161164
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
162165

166+
// When using an impl, we have to check that its super trait bounds are actually satisfied.
167+
// This is necessary as otherwise the impl `impl<T: Magic> Magic for T` would allow us to
168+
// incorrectly assume all super traits of `Magic`.
169+
for clause in elaborate::elaborate(
170+
cx,
171+
cx.explicit_super_predicates_of(impl_trait_ref.def_id)
172+
.iter_instantiated(cx, impl_trait_ref.args)
173+
.map(|(pred, _)| pred),
174+
) {
175+
ecx.add_goal(GoalSource::Misc, goal.with(cx, clause));
176+
}
177+
163178
// For this impl to be `const`, we need to check its `~const` bounds too.
164179
let const_conditions = cx
165180
.const_conditions(impl_def_id)
@@ -398,6 +413,6 @@ where
398413
goal.with(ecx.cx(), goal.predicate.trait_ref);
399414
ecx.compute_trait_goal(trait_goal)
400415
})?;
401-
self.merge_candidates(proven_via, candidates)
416+
self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution))
402417
}
403418
}

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

+25-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
99
use rustc_type_ir::inherent::*;
1010
use rustc_type_ir::relate::Relate;
1111
use rustc_type_ir::relate::solver_relating::RelateExt;
12+
use rustc_type_ir::search_graph::PathKind;
1213
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
1314
use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypingMode};
1415
use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
@@ -59,6 +60,12 @@ where
5960
/// when then adds these to its own context. The caller is always an `AliasRelate`
6061
/// goal so this never leaks out of the solver.
6162
is_normalizes_to_goal: bool,
63+
64+
/// Whether the current `probe` should be treated as a coinductive step when encountering
65+
/// trait solver cycles. This changes the step kind of all nested goals computed in that
66+
/// probe to be coinductive.
67+
is_coinductive_probe: bool,
68+
6269
pub(super) var_values: CanonicalVarValues<I>,
6370

6471
predefined_opaques_in_body: I::PredefinedOpaques,
@@ -230,6 +237,18 @@ where
230237
self.is_normalizes_to_goal = true;
231238
}
232239

240+
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
241+
if self.is_coinductive_probe {
242+
return PathKind::Coinductive;
243+
}
244+
245+
match source {
246+
GoalSource::ImplWhereBound | GoalSource::AliasWellFormed => PathKind::Coinductive,
247+
_ => PathKind::Inductive,
248+
249+
}
250+
}
251+
233252
/// Creates a root evaluation context and search graph. This should only be
234253
/// used from outside of any evaluation, and other methods should be preferred
235254
/// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]).
@@ -257,6 +276,7 @@ where
257276
variables: Default::default(),
258277
var_values: CanonicalVarValues::dummy(),
259278
is_normalizes_to_goal: false,
279+
is_coinductive_probe: false,
260280
origin_span,
261281
tainted: Ok(()),
262282
};
@@ -295,6 +315,7 @@ where
295315
variables: canonical_input.canonical.variables,
296316
var_values,
297317
is_normalizes_to_goal: false,
318+
is_coinductive_probe: false,
298319
predefined_opaques_in_body: input.predefined_opaques_in_body,
299320
max_input_universe: canonical_input.canonical.max_universe,
300321
search_graph,
@@ -340,6 +361,7 @@ where
340361
cx: I,
341362
search_graph: &'a mut SearchGraph<D>,
342363
canonical_input: CanonicalInput<I>,
364+
step_kind_from_parent: PathKind,
343365
goal_evaluation: &mut ProofTreeBuilder<D>,
344366
) -> QueryResult<I> {
345367
let mut canonical_goal_evaluation =
@@ -352,6 +374,7 @@ where
352374
search_graph.with_new_goal(
353375
cx,
354376
canonical_input,
377+
step_kind_from_parent,
355378
&mut canonical_goal_evaluation,
356379
|search_graph, canonical_goal_evaluation| {
357380
EvalCtxt::enter_canonical(
@@ -395,12 +418,10 @@ where
395418
/// `NormalizesTo` is only used by `AliasRelate`, all other callsites
396419
/// should use [`EvalCtxt::evaluate_goal`] which discards that empty
397420
/// storage.
398-
// FIXME(-Znext-solver=coinduction): `_source` is currently unused but will
399-
// be necessary once we implement the new coinduction approach.
400421
pub(super) fn evaluate_goal_raw(
401422
&mut self,
402423
goal_evaluation_kind: GoalEvaluationKind,
403-
_source: GoalSource,
424+
source: GoalSource,
404425
goal: Goal<I, I::Predicate>,
405426
) -> Result<(NestedNormalizationGoals<I>, HasChanged, Certainty), NoSolution> {
406427
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
@@ -410,6 +431,7 @@ where
410431
self.cx(),
411432
self.search_graph,
412433
canonical_goal,
434+
self.step_kind_for_source(source),
413435
&mut goal_evaluation,
414436
);
415437
let response = match canonical_response {

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

+36
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,42 @@ where
3535
variables: outer_ecx.variables,
3636
var_values: outer_ecx.var_values,
3737
is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal,
38+
is_coinductive_probe: outer_ecx.is_coinductive_probe,
39+
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
40+
max_input_universe,
41+
search_graph: outer_ecx.search_graph,
42+
nested_goals: outer_ecx.nested_goals.clone(),
43+
origin_span: outer_ecx.origin_span,
44+
tainted: outer_ecx.tainted,
45+
inspect: outer_ecx.inspect.take_and_enter_probe(),
46+
};
47+
let r = nested_ecx.delegate.probe(|| {
48+
let r = f(&mut nested_ecx);
49+
nested_ecx.inspect.probe_final_state(delegate, max_input_universe);
50+
r
51+
});
52+
if !nested_ecx.inspect.is_noop() {
53+
let probe_kind = probe_kind(&r);
54+
nested_ecx.inspect.probe_kind(probe_kind);
55+
outer_ecx.inspect = nested_ecx.inspect.finish_probe();
56+
}
57+
r
58+
}
59+
60+
pub(in crate::solve) fn enter_coinductively(
61+
self,
62+
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
63+
) -> T {
64+
let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
65+
66+
let delegate = outer_ecx.delegate;
67+
let max_input_universe = outer_ecx.max_input_universe;
68+
let mut nested_ecx = EvalCtxt {
69+
delegate,
70+
variables: outer_ecx.variables,
71+
var_values: outer_ecx.var_values,
72+
is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal,
73+
is_coinductive_probe: true,
3874
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
3975
max_input_universe,
4076
search_graph: outer_ecx.search_graph,

compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ where
3939
//
4040
// FIXME(-Znext-solver=coinductive): I think this should be split
4141
// and we tag the impl bounds with `GoalSource::ImplWhereBound`?
42-
// Right not this includes both the impl and the assoc item where bounds,
42+
// Right now this includes both the impl and the assoc item where bounds,
4343
// and I don't think the assoc item where-bounds are allowed to be coinductive.
4444
self.add_goals(
4545
GoalSource::Misc,

0 commit comments

Comments
 (0)