Skip to content

Commit 2f05b1f

Browse files
Auto merge of #144258 - lcnr:stupid-fast-path, r=<try>
`compute_trait_goal` structural equality fast path Fixes the hang in rayon and rust-lang/trait-system-refactor-initiative#210. This changes means we now have a far stronger reliance on structural equality making rust-lang/trait-system-refactor-initiative#127 a significantly larger problem. However, I've come to believe that fast paths based on structural equality are just too desirable. Fixes #139409. r? `@compiler-errors`
2 parents 6781992 + 8c737ce commit 2f05b1f

24 files changed

+205
-111
lines changed

compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
8585
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
8686
let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner;
8787

88-
let infcx =
89-
tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id));
88+
let infcx = tcx
89+
.infer_ctxt()
90+
.ignoring_regions()
91+
.in_hir_typeck()
92+
.build(TypingMode::typeck_for_body(tcx, def_id));
9093
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
9194
let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx));
9295

compiler/rustc_infer/src/infer/at.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> {
7171
tcx: self.tcx,
7272
typing_mode: self.typing_mode,
7373
considering_regions: self.considering_regions,
74+
in_hir_typeck: self.in_hir_typeck,
7475
skip_leak_check: self.skip_leak_check,
7576
inner: self.inner.clone(),
7677
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
@@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> {
9596
tcx: self.tcx,
9697
typing_mode,
9798
considering_regions: self.considering_regions,
99+
in_hir_typeck: self.in_hir_typeck,
98100
skip_leak_check: self.skip_leak_check,
99101
inner: self.inner.clone(),
100102
lexical_region_resolutions: self.lexical_region_resolutions.clone(),

compiler/rustc_infer/src/infer/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
2222
self.next_trait_solver
2323
}
2424

25+
fn in_hir_typeck(&self) -> bool {
26+
self.in_hir_typeck
27+
}
28+
2529
fn typing_mode(&self) -> ty::TypingMode<'tcx> {
2630
self.typing_mode()
2731
}

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ pub struct InferCtxt<'tcx> {
247247
/// the root universe. Most notably, this is used during hir typeck as region
248248
/// solving is left to borrowck instead.
249249
pub considering_regions: bool,
250+
pub in_hir_typeck: bool,
250251

251252
/// If set, this flag causes us to skip the 'leak check' during
252253
/// higher-ranked subtyping operations. This flag is a temporary one used
@@ -506,6 +507,7 @@ pub struct TypeOutlivesConstraint<'tcx> {
506507
pub struct InferCtxtBuilder<'tcx> {
507508
tcx: TyCtxt<'tcx>,
508509
considering_regions: bool,
510+
in_hir_typeck: bool,
509511
skip_leak_check: bool,
510512
/// Whether we should use the new trait solver in the local inference context,
511513
/// which affects things like which solver is used in `predicate_may_hold`.
@@ -518,6 +520,7 @@ impl<'tcx> TyCtxt<'tcx> {
518520
InferCtxtBuilder {
519521
tcx: self,
520522
considering_regions: true,
523+
in_hir_typeck: false,
521524
skip_leak_check: false,
522525
next_trait_solver: self.next_trait_solver_globally(),
523526
}
@@ -535,6 +538,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
535538
self
536539
}
537540

541+
pub fn in_hir_typeck(mut self) -> Self {
542+
self.in_hir_typeck = true;
543+
self
544+
}
545+
538546
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
539547
self.skip_leak_check = skip_leak_check;
540548
self
@@ -568,12 +576,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
568576
}
569577

570578
pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> {
571-
let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } =
572-
*self;
579+
let InferCtxtBuilder {
580+
tcx,
581+
considering_regions,
582+
in_hir_typeck,
583+
skip_leak_check,
584+
next_trait_solver,
585+
} = *self;
573586
InferCtxt {
574587
tcx,
575588
typing_mode,
576589
considering_regions,
590+
in_hir_typeck,
577591
skip_leak_check,
578592
inner: RefCell::new(InferCtxtInner::new()),
579593
lexical_region_resolutions: RefCell::new(None),

compiler/rustc_next_trait_solver/src/canonicalizer.rs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
1919
)
2020
.unwrap();
2121

22+
#[derive(Debug, Clone, Copy)]
23+
enum CanonicalizeInputKind {
24+
ParamEnv,
25+
Predicate { is_hir_typeck_root_goal: bool },
26+
}
27+
2228
/// Whether we're canonicalizing a query input or the query response.
2329
///
2430
/// When canonicalizing an input we're in the context of the caller
@@ -29,7 +35,7 @@ enum CanonicalizeMode {
2935
/// When canonicalizing the `param_env`, we keep `'static` as merging
3036
/// trait candidates relies on it when deciding whether a where-bound
3137
/// is trivial.
32-
Input { keep_static: bool },
38+
Input(CanonicalizeInputKind),
3339
/// FIXME: We currently return region constraints referring to
3440
/// placeholders and inference variables from a binder instantiated
3541
/// inside of the query.
@@ -122,7 +128,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
122128
let mut variables = Vec::new();
123129
let mut env_canonicalizer = Canonicalizer {
124130
delegate,
125-
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
131+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
126132

127133
variables: &mut variables,
128134
variable_lookup_table: Default::default(),
@@ -154,7 +160,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
154160
} else {
155161
let mut env_canonicalizer = Canonicalizer {
156162
delegate,
157-
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
163+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
158164

159165
variables,
160166
variable_lookup_table: Default::default(),
@@ -180,6 +186,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
180186
pub fn canonicalize_input<P: TypeFoldable<I>>(
181187
delegate: &'a D,
182188
variables: &'a mut Vec<I::GenericArg>,
189+
is_hir_typeck_root_goal: bool,
183190
input: QueryInput<I, P>,
184191
) -> ty::Canonical<I, QueryInput<I, P>> {
185192
// First canonicalize the `param_env` while keeping `'static`
@@ -189,7 +196,9 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
189196
// while *mostly* reusing the canonicalizer from above.
190197
let mut rest_canonicalizer = Canonicalizer {
191198
delegate,
192-
canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
199+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
200+
is_hir_typeck_root_goal,
201+
}),
193202

194203
variables,
195204
variable_lookup_table,
@@ -413,10 +422,10 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
413422
// We don't canonicalize `ReStatic` in the `param_env` as we use it
414423
// when checking whether a `ParamEnv` candidate is global.
415424
ty::ReStatic => match self.canonicalize_mode {
416-
CanonicalizeMode::Input { keep_static: false } => {
425+
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
417426
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
418427
}
419-
CanonicalizeMode::Input { keep_static: true }
428+
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
420429
| CanonicalizeMode::Response { .. } => return r,
421430
},
422431

@@ -428,20 +437,20 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
428437
// `ReErased`. We may be able to short-circuit registering region
429438
// obligations if we encounter a `ReErased` on one side, for example.
430439
ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
431-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
440+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
432441
CanonicalizeMode::Response { .. } => return r,
433442
},
434443

435444
ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
436-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
445+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
437446
CanonicalizeMode::Response { .. } => {
438447
panic!("unexpected region in response: {r:?}")
439448
}
440449
},
441450

442451
ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
443452
// We canonicalize placeholder regions as existentials in query inputs.
444-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
453+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
445454
CanonicalizeMode::Response { max_input_universe } => {
446455
// If we have a placeholder region inside of a query, it must be from
447456
// a new universe.
@@ -459,23 +468,36 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
459468
"region vid should have been resolved fully before canonicalization"
460469
);
461470
match self.canonicalize_mode {
462-
CanonicalizeMode::Input { keep_static: _ } => {
463-
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
464-
}
471+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
465472
CanonicalizeMode::Response { .. } => {
466473
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
467474
}
468475
}
469476
}
470477
};
471478

472-
let var = self.get_or_insert_bound_var(r, kind);
479+
let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
480+
is_hir_typeck_root_goal: true,
481+
}) = self.canonicalize_mode
482+
{
483+
let var = ty::BoundVar::from(self.variables.len());
484+
self.variables.push(r.into());
485+
self.var_kinds.push(kind);
486+
var
487+
} else {
488+
self.get_or_insert_bound_var(r, kind)
489+
};
473490

474491
Region::new_anon_bound(self.cx(), self.binder_index, var)
475492
}
476493

477494
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
478-
if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
495+
if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
496+
is_hir_typeck_root_goal: true,
497+
}) = self.canonicalize_mode
498+
{
499+
self.cached_fold_ty(t)
500+
} else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
479501
ty
480502
} else {
481503
let res = self.cached_fold_ty(t);
@@ -541,9 +563,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
541563

542564
fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
543565
match self.canonicalize_mode {
544-
CanonicalizeMode::Input { keep_static: true }
566+
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
545567
| CanonicalizeMode::Response { max_input_universe: _ } => {}
546-
CanonicalizeMode::Input { keep_static: false } => {
568+
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
547569
panic!("erasing 'static in env")
548570
}
549571
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ where
5555
/// for each bound variable.
5656
pub(super) fn canonicalize_goal(
5757
&self,
58+
is_hir_typeck_root_goal: bool,
5859
goal: Goal<I, I::Predicate>,
5960
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) {
6061
// We only care about one entry per `OpaqueTypeKey` here,
@@ -67,6 +68,7 @@ where
6768
let canonical = Canonicalizer::canonicalize_input(
6869
self.delegate,
6970
&mut orig_values,
71+
is_hir_typeck_root_goal,
7072
QueryInput {
7173
goal,
7274
predefined_opaques_in_body: self

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,14 @@ where
197197
self.cx().recursion_limit(),
198198
GenerateProofTree::No,
199199
span,
200-
|ecx| ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on),
200+
|ecx| {
201+
ecx.evaluate_goal(
202+
GoalEvaluationKind::Root { in_hir_typeck: self.in_hir_typeck() },
203+
GoalSource::Misc,
204+
goal,
205+
stalled_on,
206+
)
207+
},
201208
)
202209
.0
203210
}
@@ -209,7 +216,12 @@ where
209216
) -> bool {
210217
self.probe(|| {
211218
EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, I::Span::dummy(), |ecx| {
212-
ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, None)
219+
ecx.evaluate_goal(
220+
GoalEvaluationKind::Root { in_hir_typeck: self.in_hir_typeck() },
221+
GoalSource::Misc,
222+
goal,
223+
None,
224+
)
213225
})
214226
.0
215227
})
@@ -230,7 +242,14 @@ where
230242
self.cx().recursion_limit(),
231243
GenerateProofTree::Yes,
232244
span,
233-
|ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, None),
245+
|ecx| {
246+
ecx.evaluate_goal_raw(
247+
GoalEvaluationKind::Root { in_hir_typeck: self.in_hir_typeck() },
248+
GoalSource::Misc,
249+
goal,
250+
None,
251+
)
252+
},
234253
);
235254
(result, proof_tree.unwrap())
236255
}
@@ -447,7 +466,10 @@ where
447466
));
448467
}
449468

450-
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
469+
let is_hir_typeck_root_goal =
470+
matches!(goal_evaluation_kind, GoalEvaluationKind::Root { in_hir_typeck: true });
471+
472+
let (orig_values, canonical_goal) = self.canonicalize_goal(is_hir_typeck_root_goal, goal);
451473
let mut goal_evaluation =
452474
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
453475
let canonical_result = self.search_graph.evaluate_goal(

compiler/rustc_next_trait_solver/src/solve/inspect/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
237237
kind: GoalEvaluationKind,
238238
) -> ProofTreeBuilder<D> {
239239
self.opt_nested(|| match kind {
240-
GoalEvaluationKind::Root => Some(WipGoalEvaluation {
240+
GoalEvaluationKind::Root { in_hir_typeck: _ } => Some(WipGoalEvaluation {
241241
uncanonicalized_goal,
242242
orig_values: orig_values.to_vec(),
243243
encountered_overflow: false,

compiler/rustc_next_trait_solver/src/solve/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const FIXPOINT_STEP_LIMIT: usize = 8;
4343

4444
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4545
enum GoalEvaluationKind {
46-
Root,
46+
Root { in_hir_typeck: bool },
4747
Nested,
4848
}
4949

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,21 @@ where
14281428
&mut self,
14291429
goal: Goal<I, TraitPredicate<I>>,
14301430
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
1431+
if goal
1432+
.param_env
1433+
.caller_bounds()
1434+
.iter()
1435+
.filter_map(|c| c.as_trait_clause())
1436+
.filter_map(|c| c.no_bound_vars())
1437+
.any(|p| p == goal.predicate)
1438+
{
1439+
let candidate = self
1440+
.probe_trait_candidate(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal))
1441+
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
1442+
.unwrap();
1443+
return Ok((candidate.result, Some(TraitGoalProvenVia::ParamEnv)));
1444+
}
1445+
14311446
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
14321447
self.merge_trait_candidates(candidates)
14331448
}

0 commit comments

Comments
 (0)