Skip to content

Commit 3ea7c51

Browse files
Fall back to bidirectional normalizes-to if no subst-eq in alias-eq goal
1 parent 7a2cdf2 commit 3ea7c51

File tree

4 files changed

+145
-23
lines changed

4 files changed

+145
-23
lines changed

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+72-23
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
7979
// them. This is necessary for inference during typeck.
8080
//
8181
// As this is incomplete, we must not do so during coherence.
82-
match (self.solver_mode(), subst_relate_response) {
83-
(SolverMode::Normal, Ok(response)) => Ok(response),
84-
(SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => {
85-
self.flounder(&candidates)
82+
match self.solver_mode() {
83+
SolverMode::Normal => {
84+
if let Ok(subst_relate_response) = subst_relate_response {
85+
Ok(subst_relate_response)
86+
} else if let Ok(bidirectional_normalizes_to_response) = self
87+
.assemble_bidirectional_normalizes_to_candidate(
88+
param_env, lhs, rhs, direction,
89+
)
90+
{
91+
Ok(bidirectional_normalizes_to_response)
92+
} else {
93+
self.flounder(&candidates)
94+
}
8695
}
96+
SolverMode::Coherence => self.flounder(&candidates),
8797
}
8898
}
8999
}
@@ -100,29 +110,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
100110
invert: Invert,
101111
) -> QueryResult<'tcx> {
102112
self.probe(|ecx| {
103-
let other = match direction {
104-
// This is purely an optimization.
105-
ty::AliasRelationDirection::Equate => other,
106-
107-
ty::AliasRelationDirection::Subtype => {
108-
let fresh = ecx.next_term_infer_of_kind(other);
109-
let (sub, sup) = match invert {
110-
Invert::No => (fresh, other),
111-
Invert::Yes => (other, fresh),
112-
};
113-
ecx.sub(param_env, sub, sup)?;
114-
fresh
115-
}
116-
};
117-
ecx.add_goal(Goal::new(
118-
ecx.tcx(),
119-
param_env,
120-
ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
121-
));
113+
ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
122114
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
123115
})
124116
}
125117

118+
fn normalizes_to_inner(
119+
&mut self,
120+
param_env: ty::ParamEnv<'tcx>,
121+
alias: ty::AliasTy<'tcx>,
122+
other: ty::Term<'tcx>,
123+
direction: ty::AliasRelationDirection,
124+
invert: Invert,
125+
) -> Result<(), NoSolution> {
126+
let other = match direction {
127+
// This is purely an optimization.
128+
ty::AliasRelationDirection::Equate => other,
129+
130+
ty::AliasRelationDirection::Subtype => {
131+
let fresh = self.next_term_infer_of_kind(other);
132+
let (sub, sup) = match invert {
133+
Invert::No => (fresh, other),
134+
Invert::Yes => (other, fresh),
135+
};
136+
self.sub(param_env, sub, sup)?;
137+
fresh
138+
}
139+
};
140+
self.add_goal(Goal::new(
141+
self.tcx(),
142+
param_env,
143+
ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
144+
));
145+
146+
Ok(())
147+
}
148+
126149
fn assemble_subst_relate_candidate(
127150
&mut self,
128151
param_env: ty::ParamEnv<'tcx>,
@@ -143,4 +166,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
143166
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
144167
})
145168
}
169+
170+
fn assemble_bidirectional_normalizes_to_candidate(
171+
&mut self,
172+
param_env: ty::ParamEnv<'tcx>,
173+
lhs: ty::Term<'tcx>,
174+
rhs: ty::Term<'tcx>,
175+
direction: ty::AliasRelationDirection,
176+
) -> QueryResult<'tcx> {
177+
self.probe(|ecx| {
178+
ecx.normalizes_to_inner(
179+
param_env,
180+
lhs.to_alias_ty(ecx.tcx()).unwrap(),
181+
rhs,
182+
direction,
183+
Invert::No,
184+
)?;
185+
ecx.normalizes_to_inner(
186+
param_env,
187+
rhs.to_alias_ty(ecx.tcx()).unwrap(),
188+
lhs,
189+
direction,
190+
Invert::Yes,
191+
)?;
192+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
193+
})
194+
}
146195
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
#![feature(type_alias_impl_trait)]
5+
6+
// Similar to tests/ui/traits/new-solver/tait-eq-proj.rs
7+
// but check the alias-sub relation in the other direction.
8+
9+
type Tait = impl Iterator<Item = impl Sized>;
10+
11+
fn mk<T>() -> T { todo!() }
12+
13+
fn a() {
14+
let x: Tait = mk();
15+
let mut array = mk();
16+
let mut z = IntoIterator::into_iter(array);
17+
z = x;
18+
array = [0i32; 32];
19+
}
20+
21+
fn main() {}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
#![feature(type_alias_impl_trait)]
5+
6+
type Tait = impl Iterator<Item = impl Sized>;
7+
8+
/*
9+
10+
Consider the goal - AliasRelate(Tait, <[i32; 32] as IntoIterator>::IntoIter)
11+
which is registered on the line above.
12+
13+
A. SubstRelate - fails (of course).
14+
15+
B. NormalizesToRhs - Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter
16+
* infer definition - Tait := <[i32; 32] as IntoIterator>::IntoIter
17+
18+
C. NormalizesToLhs - <[i32; 32] as IntoIterator>::IntoIter normalizes-to Tait
19+
* Find impl candidate, after substitute - std::array::IntoIter<i32, 32>
20+
* Equate std::array::IntoIter<i32, 32> and Tait
21+
* infer definition - Tait := std::array::IntoIter<i32, 32>
22+
23+
B and C are not equal, but they are equivalent modulo normalization.
24+
25+
We get around this by evaluating both the NormalizesToRhs and NormalizesToLhs
26+
goals together. Essentially:
27+
A alias-relate B if A normalizes-to B and B normalizes-to A.
28+
29+
*/
30+
31+
fn a() {
32+
let _: Tait = IntoIterator::into_iter([0i32; 32]);
33+
}
34+
35+
fn main() {}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
// Not exactly sure if this is the inference behavior we *want*,
5+
// but it is a side-effect of the lazy normalization of TAITs.
6+
7+
#![feature(type_alias_impl_trait)]
8+
9+
type Tait = impl Sized;
10+
type Tait2 = impl Sized;
11+
12+
fn mk<T>() -> T { todo!() }
13+
14+
fn main() {
15+
let x: Tait = 1u32;
16+
let y: Tait2 = x;
17+
}

0 commit comments

Comments
 (0)