Skip to content

Commit 0159fd0

Browse files
Use MaybeCause::or to allow constraints from overflows if they are combined with ambiguity
1 parent 1a95cc6 commit 0159fd0

File tree

7 files changed

+128
-24
lines changed

7 files changed

+128
-24
lines changed

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,14 @@ where
129129
(Certainty::Yes, NestedNormalizationGoals(goals))
130130
}
131131
_ => {
132-
let certainty = shallow_certainty.unify_with(goals_certainty);
132+
let certainty = shallow_certainty.and(goals_certainty);
133133
(certainty, NestedNormalizationGoals::empty())
134134
}
135135
};
136136

137-
if let Certainty::Maybe(cause @ MaybeCause::Overflow { .. }) = certainty {
137+
if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) =
138+
certainty
139+
{
138140
// If we have overflow, it's probable that we're substituting a type
139141
// into itself infinitely and any partial substitutions in the query
140142
// response are probably not useful anyways, so just return an empty
@@ -190,6 +192,7 @@ where
190192
debug!(?num_non_region_vars, "too many inference variables -> overflow");
191193
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
192194
suggest_increasing_limit: true,
195+
keep_constraints: false,
193196
}));
194197
}
195198
}

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ where
653653
Certainty::Yes => {}
654654
Certainty::Maybe(_) => {
655655
self.nested_goals.push((source, with_resolved_vars));
656-
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
656+
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
657657
}
658658
}
659659
} else {
@@ -667,7 +667,7 @@ where
667667
Certainty::Yes => {}
668668
Certainty::Maybe(_) => {
669669
self.nested_goals.push((source, goal));
670-
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
670+
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
671671
}
672672
}
673673
}

compiler/rustc_next_trait_solver/src/solve/mod.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,18 @@ where
253253
}
254254

255255
fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
256-
debug_assert!(!responses.is_empty());
257-
if let Certainty::Maybe(maybe_cause) =
258-
responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
259-
certainty.unify_with(response.value.certainty)
260-
})
261-
{
262-
self.make_ambiguous_response_no_constraints(maybe_cause)
263-
} else {
264-
panic!("expected flounder response to be ambiguous")
265-
}
256+
debug_assert!(responses.len() > 1);
257+
let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| {
258+
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
259+
// these responses, b/c we're combining more than one response and this we
260+
// don't know which one applies.
261+
let candidate = match response.value.certainty {
262+
Certainty::Yes => MaybeCause::Ambiguity,
263+
Certainty::Maybe(candidate) => candidate,
264+
};
265+
maybe_cause.or(candidate)
266+
});
267+
self.make_ambiguous_response_no_constraints(maybe_cause)
266268
}
267269

268270
/// If we fail to merge responses we flounder and return overflow or ambiguity.

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
9999
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
100100
(FulfillmentErrorCode::Ambiguity { overflow: None }, true)
101101
}
102-
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
102+
Ok((
103+
_,
104+
Certainty::Maybe(MaybeCause::Overflow {
105+
suggest_increasing_limit,
106+
keep_constraints: _,
107+
}),
108+
)) => (
103109
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
104110
// Don't look into overflows because we treat overflows weirdly anyways.
105111
// We discard the inference constraints from overflowing goals, so

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
382382
if let Some(term_hack) = normalizes_to_term_hack {
383383
infcx
384384
.probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
385-
.map(|certainty| ok.value.certainty.unify_with(certainty))
385+
.map(|certainty| ok.value.certainty.and(certainty))
386386
} else {
387387
Ok(ok.value.certainty)
388388
}

compiler/rustc_type_ir/src/solve/mod.rs

+47-8
Original file line numberDiff line numberDiff line change
@@ -266,17 +266,17 @@ impl Certainty {
266266
/// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar`
267267
/// in ambiguity without changing the inference state, we still want to tell the
268268
/// user that `T: Baz` results in overflow.
269-
pub fn unify_with(self, other: Certainty) -> Certainty {
269+
pub fn and(self, other: Certainty) -> Certainty {
270270
match (self, other) {
271271
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
272272
(Certainty::Yes, Certainty::Maybe(_)) => other,
273273
(Certainty::Maybe(_), Certainty::Yes) => self,
274-
(Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)),
274+
(Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)),
275275
}
276276
}
277277

278278
pub const fn overflow(suggest_increasing_limit: bool) -> Certainty {
279-
Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit })
279+
Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false })
280280
}
281281
}
282282

@@ -289,19 +289,58 @@ pub enum MaybeCause {
289289
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
290290
Ambiguity,
291291
/// We gave up due to an overflow, most often by hitting the recursion limit.
292-
Overflow { suggest_increasing_limit: bool },
292+
Overflow { suggest_increasing_limit: bool, keep_constraints: bool },
293293
}
294294

295295
impl MaybeCause {
296-
fn unify_with(self, other: MaybeCause) -> MaybeCause {
296+
fn and(self, other: MaybeCause) -> MaybeCause {
297297
match (self, other) {
298298
(MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity,
299299
(MaybeCause::Ambiguity, MaybeCause::Overflow { .. }) => other,
300300
(MaybeCause::Overflow { .. }, MaybeCause::Ambiguity) => self,
301301
(
302-
MaybeCause::Overflow { suggest_increasing_limit: a },
303-
MaybeCause::Overflow { suggest_increasing_limit: b },
304-
) => MaybeCause::Overflow { suggest_increasing_limit: a || b },
302+
MaybeCause::Overflow {
303+
suggest_increasing_limit: limit_a,
304+
keep_constraints: keep_a,
305+
},
306+
MaybeCause::Overflow {
307+
suggest_increasing_limit: limit_b,
308+
keep_constraints: keep_b,
309+
},
310+
) => MaybeCause::Overflow {
311+
suggest_increasing_limit: limit_a && limit_b,
312+
keep_constraints: keep_a && keep_b,
313+
},
314+
}
315+
}
316+
317+
pub fn or(self, other: MaybeCause) -> MaybeCause {
318+
match (self, other) {
319+
(MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity,
320+
321+
// When combining ambiguity + overflow, we can keep constraints.
322+
(
323+
MaybeCause::Ambiguity,
324+
MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
325+
) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true },
326+
(
327+
MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
328+
MaybeCause::Ambiguity,
329+
) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true },
330+
331+
(
332+
MaybeCause::Overflow {
333+
suggest_increasing_limit: limit_a,
334+
keep_constraints: keep_a,
335+
},
336+
MaybeCause::Overflow {
337+
suggest_increasing_limit: limit_b,
338+
keep_constraints: keep_b,
339+
},
340+
) => MaybeCause::Overflow {
341+
suggest_increasing_limit: limit_a || limit_b,
342+
keep_constraints: keep_a || keep_b,
343+
},
305344
}
306345
}
307346
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//@ check-pass
2+
//@ compile-flags: -Znext-solver
3+
4+
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/201>.
5+
// See comment below on `fn main`.
6+
7+
trait Intersect<U> {
8+
type Output;
9+
}
10+
11+
impl<T, U> Intersect<Vec<U>> for T
12+
where
13+
T: Intersect<U>,
14+
{
15+
type Output = T;
16+
}
17+
18+
impl Intersect<Cuboid> for Cuboid {
19+
type Output = Cuboid;
20+
}
21+
22+
fn intersect<T, U>(_: &T, _: &U) -> T::Output
23+
where
24+
T: Intersect<U>,
25+
{
26+
todo!()
27+
}
28+
29+
struct Cuboid;
30+
impl Cuboid {
31+
fn method(&self) {}
32+
}
33+
34+
fn main() {
35+
let x = vec![];
36+
// Here we end up trying to normalize `<Cuboid as Intersect<Vec<?0>>>::Output`
37+
// for the return type of the function, to constrain `y`. The impl then requires
38+
// `Cuboid: Intersect<?0>`, which has two candidates. One that constrains
39+
// `?0 = Vec<?1>`, which itself has the same two candidates and ends up leading
40+
// to a recursion depth overflow. In the second impl, we constrain `?0 = Cuboid`.
41+
// Floundering leads to us combining the overflow candidate and yes candidate to
42+
// overflow. Because the response was overflow, we used to bubble it up to the
43+
// parent normalizes-to goal, which caused us to drop the constraint that would
44+
// guide us to normalize the associated type mentioned before. After this PR, we
45+
// implement a new floundering "algebra" such that `Overflow OR Maybe` returns a
46+
// new `Overflow { keep_constraints: true }`, which means that we don't need to
47+
// drop constraints in the parent normalizes-to goal. This allows us to normalize
48+
// `y` to `Cuboid`, and allows us to call the method successfully. We then
49+
// constrain the `?0` in `let x: Vec<Cuboid> = x` below, so that we don't have a
50+
// left over ambiguous goal.
51+
let y = intersect(&Cuboid, &x);
52+
y.method();
53+
let x: Vec<Cuboid> = x;
54+
}

0 commit comments

Comments
 (0)