Skip to content

Commit 5908e13

Browse files
committed
alias-relate: add fast reject optimization
1 parent 0f40f14 commit 5908e13

File tree

1 file changed

+114
-1
lines changed

1 file changed

+114
-1
lines changed

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+114-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
//! relate them structurally.
1717
1818
use super::EvalCtxt;
19+
use rustc_data_structures::fx::FxHashSet;
20+
use rustc_middle::traits::query::NoSolution;
1921
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
20-
use rustc_middle::ty;
22+
use rustc_middle::ty::TypeVisitor;
23+
use rustc_middle::ty::{self, Ty, TyCtxt};
24+
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
2125

2226
impl<'tcx> EvalCtxt<'_, 'tcx> {
2327
#[instrument(level = "debug", skip(self), ret)]
@@ -28,6 +32,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
2832
let tcx = self.tcx();
2933
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
3034

35+
if self.fast_reject_unnameable_rigid_term(param_env, lhs, rhs)
36+
&& self.fast_reject_unnameable_rigid_term(param_env, rhs, lhs)
37+
{
38+
return Err(NoSolution);
39+
}
40+
3141
// Structurally normalize the lhs.
3242
let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) {
3343
let term = self.next_term_infer_of_kind(lhs);
@@ -83,3 +93,106 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
8393
}
8494
}
8595
}
96+
97+
enum IgnoreAliases {
98+
Yes,
99+
No,
100+
}
101+
102+
impl<'tcx> EvalCtxt<'_, '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 fast_reject_unnameable_rigid_term(
108+
&mut self,
109+
param_env: ty::ParamEnv<'tcx>,
110+
rigid_term: ty::Term<'tcx>,
111+
alias: ty::Term<'tcx>,
112+
) -> bool {
113+
let tcx = self.tcx();
114+
// Check that the rigid term is actually rigid.
115+
if rigid_term.to_alias_ty(tcx).is_some() || alias.to_alias_ty(tcx).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)