Skip to content

Commit 248d5b9

Browse files
committed
alias-relate: add fast reject optimization
1 parent 40e990a commit 248d5b9

File tree

1 file changed

+112
-1
lines changed

1 file changed

+112
-1
lines changed

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+112-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
//! relate them structurally.
1717
1818
use super::EvalCtxt;
19+
use rustc_data_structures::fx::FxHashSet;
1920
use rustc_infer::infer::InferCtxt;
21+
use rustc_middle::traits::query::NoSolution;
2022
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
21-
use rustc_middle::ty;
23+
use rustc_middle::ty::{self, Ty, TyCtxt};
24+
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
2225

2326
impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
2427
#[instrument(level = "trace", skip(self), ret)]
@@ -30,6 +33,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
3033
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
3134
debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
3235

36+
if self.fast_reject_unnameable_rigid_term(param_env, lhs, rhs)
37+
|| self.fast_reject_unnameable_rigid_term(param_env, rhs, lhs)
38+
{
39+
return Err(NoSolution);
40+
}
41+
3342
// Structurally normalize the lhs.
3443
let lhs = if let Some(alias) = lhs.to_alias_term() {
3544
let term = self.next_term_infer_of_kind(lhs);
@@ -85,3 +94,105 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
8594
}
8695
}
8796
}
97+
98+
enum IgnoreAliases {
99+
Yes,
100+
No,
101+
}
102+
103+
impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
104+
/// In case a rigid term refers to a placeholder which is not referenced by the
105+
/// alias, the alias cannot be normalized to that rigid term unless it contains
106+
/// either inference variables or these placeholders are referenced in a term
107+
/// of a `Projection`-clause in the environment.
108+
fn fast_reject_unnameable_rigid_term(
109+
&mut self,
110+
param_env: ty::ParamEnv<'tcx>,
111+
rigid_term: ty::Term<'tcx>,
112+
alias: ty::Term<'tcx>,
113+
) -> bool {
114+
// Check that the rigid term is actually rigid.
115+
if rigid_term.to_alias_term().is_some() || alias.to_alias_term().is_none() {
116+
return false;
117+
}
118+
119+
// If the alias has any type or const inference variables,
120+
// do not try to apply the fast path as these inference variables
121+
// may resolve to something containing placeholders.
122+
if alias.has_non_region_infer() {
123+
return false;
124+
}
125+
126+
let mut referenced_placeholders =
127+
self.collect_placeholders_in_term(rigid_term, IgnoreAliases::Yes);
128+
for clause in param_env.caller_bounds() {
129+
match clause.kind().skip_binder() {
130+
ty::ClauseKind::Projection(ty::ProjectionPredicate { term, .. }) => {
131+
if term.has_non_region_infer() {
132+
return false;
133+
}
134+
135+
let env_term_placeholders =
136+
self.collect_placeholders_in_term(term, IgnoreAliases::No);
137+
#[allow(rustc::potential_query_instability)]
138+
referenced_placeholders.retain(|p| !env_term_placeholders.contains(p));
139+
}
140+
ty::ClauseKind::Trait(_)
141+
| ty::ClauseKind::TypeOutlives(_)
142+
| ty::ClauseKind::RegionOutlives(_)
143+
| ty::ClauseKind::ConstArgHasType(..)
144+
| ty::ClauseKind::WellFormed(_)
145+
| ty::ClauseKind::ConstEvaluatable(_) => continue,
146+
}
147+
}
148+
149+
if referenced_placeholders.is_empty() {
150+
return false;
151+
}
152+
153+
let alias_placeholders = self.collect_placeholders_in_term(alias, IgnoreAliases::No);
154+
// If the rigid term references a placeholder not mentioned by the alias,
155+
// they can never unify.
156+
!referenced_placeholders.is_subset(&alias_placeholders)
157+
}
158+
159+
fn collect_placeholders_in_term(
160+
&mut self,
161+
term: ty::Term<'tcx>,
162+
ignore_aliases: IgnoreAliases,
163+
) -> FxHashSet<ty::Term<'tcx>> {
164+
// Fast path to avoid walking the term.
165+
if !term.has_placeholders() {
166+
return Default::default();
167+
}
168+
169+
struct PlaceholderCollector<'tcx> {
170+
ignore_aliases: IgnoreAliases,
171+
placeholders: FxHashSet<ty::Term<'tcx>>,
172+
}
173+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlaceholderCollector<'tcx> {
174+
type Result = ();
175+
176+
fn visit_ty(&mut self, t: Ty<'tcx>) {
177+
match t.kind() {
178+
ty::Placeholder(_) => drop(self.placeholders.insert(t.into())),
179+
ty::Alias(..) if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {}
180+
_ => t.super_visit_with(self),
181+
}
182+
}
183+
184+
fn visit_const(&mut self, ct: ty::Const<'tcx>) {
185+
match ct.kind() {
186+
ty::ConstKind::Placeholder(_) => drop(self.placeholders.insert(ct.into())),
187+
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_)
188+
if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {}
189+
_ => ct.super_visit_with(self),
190+
}
191+
}
192+
}
193+
194+
let mut visitor = PlaceholderCollector { ignore_aliases, placeholders: Default::default() };
195+
term.visit_with(&mut visitor);
196+
visitor.placeholders
197+
}
198+
}

0 commit comments

Comments
 (0)