Skip to content

Commit c3ef9cd

Browse files
committed
add alias-relate fast path optimization
1 parent a8dbd7a commit c3ef9cd

File tree

1 file changed

+110
-1
lines changed

1 file changed

+110
-1
lines changed

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
}

0 commit comments

Comments
 (0)