Skip to content

Commit ac9bcd8

Browse files
committed
alias-relate: add fast reject optimization
1 parent 5065123 commit ac9bcd8

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)]
@@ -29,6 +32,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
2932
let tcx = self.tcx();
3033
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
3134

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

0 commit comments

Comments
 (0)