Skip to content

Commit e15510c

Browse files
committed
Auto merge of rust-lang#75494 - matthewjasper:defer-recursive-projection-error, r=nikomatsakis
Don't immediately error for cycles during normalization rust-lang#73452 meant some normalization cycles could be detected earlier, breaking some code. This PR makes defers errors for normalization cycles to fulfillment, fixing said code. Fixes rust-lang#74868 r? @nikomatsakis
2 parents 3323691 + 67fb583 commit e15510c

16 files changed

+171
-86
lines changed

src/librustc_trait_selection/traits/auto_trait.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ impl AutoTraitFinder<'tcx> {
738738
// and turn them into an explicit negative impl for our type.
739739
debug!("Projecting and unifying projection predicate {:?}", predicate);
740740

741-
match poly_project_and_unify_type(select, &obligation.with(p)) {
741+
match project::poly_project_and_unify_type(select, &obligation.with(p)) {
742742
Err(e) => {
743743
debug!(
744744
"evaluate_nested_obligations: Unable to unify predicate \
@@ -747,7 +747,11 @@ impl AutoTraitFinder<'tcx> {
747747
);
748748
return false;
749749
}
750-
Ok(Some(v)) => {
750+
Ok(Err(project::InProgress)) => {
751+
debug!("evaluate_nested_obligations: recursive projection predicate");
752+
return false;
753+
}
754+
Ok(Ok(Some(v))) => {
751755
// We only care about sub-obligations
752756
// when we started out trying to unify
753757
// some inference variables. See the comment above
@@ -766,7 +770,7 @@ impl AutoTraitFinder<'tcx> {
766770
}
767771
}
768772
}
769-
Ok(None) => {
773+
Ok(Ok(None)) => {
770774
// It's ok not to make progress when have no inference variables -
771775
// in that case, we were only performing unifcation to check if an
772776
// error occurred (which would indicate that it's impossible for our

src/librustc_trait_selection/traits/fulfill.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -622,15 +622,20 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
622622
project_obligation: PolyProjectionObligation<'tcx>,
623623
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
624624
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
625+
let tcx = self.selcx.tcx();
625626
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
626-
Ok(None) => {
627+
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
628+
Ok(Ok(None)) => {
627629
*stalled_on = trait_ref_infer_vars(
628630
self.selcx,
629631
project_obligation.predicate.to_poly_trait_ref(self.selcx.tcx()),
630632
);
631633
ProcessResult::Unchanged
632634
}
633-
Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)),
635+
// Let the caller handle the recursion
636+
Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![
637+
project_obligation.with(project_obligation.predicate.to_predicate(tcx)),
638+
])),
634639
Err(e) => ProcessResult::Error(CodeProjectionError(e)),
635640
}
636641
}

src/librustc_trait_selection/traits/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,7 @@ pub use self::object_safety::is_vtable_safe_method;
5151
pub use self::object_safety::MethodViolationCode;
5252
pub use self::object_safety::ObjectSafetyViolation;
5353
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
54-
pub use self::project::{
55-
normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type,
56-
};
54+
pub use self::project::{normalize, normalize_projection_type, normalize_to};
5755
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
5856
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
5957
pub use self::specialize::specialization_graph::FutureCompatOverlapError;

src/librustc_trait_selection/traits/project.rs

+45-29
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'
4141

4242
pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>;
4343

44+
pub(super) struct InProgress;
45+
4446
/// When attempting to resolve `<T as TraitRef>::Name` ...
4547
#[derive(Debug)]
4648
pub enum ProjectionTyError<'tcx> {
@@ -143,10 +145,26 @@ impl<'tcx> ProjectionTyCandidateSet<'tcx> {
143145
///
144146
/// If successful, this may result in additional obligations. Also returns
145147
/// the projection cache key used to track these additional obligations.
146-
pub fn poly_project_and_unify_type<'cx, 'tcx>(
148+
///
149+
/// ## Returns
150+
///
151+
/// - `Err(_)`: the projection can be normalized, but is not equal to the
152+
/// expected type.
153+
/// - `Ok(Err(InProgress))`: this is called recursively while normalizing
154+
/// the same projection.
155+
/// - `Ok(Ok(None))`: The projection cannot be normalized due to ambiguity
156+
/// (resolving some inference variables in the projection may fix this).
157+
/// - `Ok(Ok(Some(obligations)))`: The projection bound holds subject to
158+
/// the given obligations. If the projection cannot be normalized because
159+
/// the required trait bound doesn't hold this returned with `obligations`
160+
/// being a predicate that cannot be proven.
161+
pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
147162
selcx: &mut SelectionContext<'cx, 'tcx>,
148163
obligation: &PolyProjectionObligation<'tcx>,
149-
) -> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>> {
164+
) -> Result<
165+
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
166+
MismatchedProjectionTypes<'tcx>,
167+
> {
150168
debug!("poly_project_and_unify_type(obligation={:?})", obligation);
151169

152170
let infcx = selcx.infcx();
@@ -165,10 +183,15 @@ pub fn poly_project_and_unify_type<'cx, 'tcx>(
165183
/// <T as Trait>::U == V
166184
///
167185
/// If successful, this may result in additional obligations.
186+
///
187+
/// See [poly_project_and_unify_type] for an explanation of the return value.
168188
fn project_and_unify_type<'cx, 'tcx>(
169189
selcx: &mut SelectionContext<'cx, 'tcx>,
170190
obligation: &ProjectionObligation<'tcx>,
171-
) -> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>> {
191+
) -> Result<
192+
Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>,
193+
MismatchedProjectionTypes<'tcx>,
194+
> {
172195
debug!("project_and_unify_type(obligation={:?})", obligation);
173196

174197
let mut obligations = vec![];
@@ -180,8 +203,9 @@ fn project_and_unify_type<'cx, 'tcx>(
180203
obligation.recursion_depth,
181204
&mut obligations,
182205
) {
183-
Some(n) => n,
184-
None => return Ok(None),
206+
Ok(Some(n)) => n,
207+
Ok(None) => return Ok(Ok(None)),
208+
Err(InProgress) => return Ok(Err(InProgress)),
185209
};
186210

187211
debug!(
@@ -196,7 +220,7 @@ fn project_and_unify_type<'cx, 'tcx>(
196220
{
197221
Ok(InferOk { obligations: inferred_obligations, value: () }) => {
198222
obligations.extend(inferred_obligations);
199-
Ok(Some(obligations))
223+
Ok(Ok(Some(obligations)))
200224
}
201225
Err(err) => {
202226
debug!("project_and_unify_type: equating types encountered error {:?}", err);
@@ -419,6 +443,8 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
419443
depth,
420444
obligations,
421445
)
446+
.ok()
447+
.flatten()
422448
.unwrap_or_else(move || {
423449
// if we bottom out in ambiguity, create a type variable
424450
// and a deferred predicate to resolve this when more type
@@ -455,7 +481,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
455481
cause: ObligationCause<'tcx>,
456482
depth: usize,
457483
obligations: &mut Vec<PredicateObligation<'tcx>>,
458-
) -> Option<Ty<'tcx>> {
484+
) -> Result<Option<Ty<'tcx>>, InProgress> {
459485
let infcx = selcx.infcx();
460486

461487
let projection_ty = infcx.resolve_vars_if_possible(&projection_ty);
@@ -487,7 +513,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
487513
"opt_normalize_projection_type: \
488514
found cache entry: ambiguous"
489515
);
490-
return None;
516+
return Ok(None);
491517
}
492518
Err(ProjectionCacheEntry::InProgress) => {
493519
// If while normalized A::B, we are asked to normalize
@@ -502,24 +528,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
502528
// to normalize `A::B`, we will want to check the
503529
// where-clauses in scope. So we will try to unify `A::B`
504530
// with `A::B`, which can trigger a recursive
505-
// normalization. In that case, I think we will want this code:
506-
//
507-
// ```
508-
// let ty = selcx.tcx().mk_projection(projection_ty.item_def_id,
509-
// projection_ty.substs;
510-
// return Some(NormalizedTy { value: v, obligations: vec![] });
511-
// ```
531+
// normalization.
512532

513533
debug!(
514534
"opt_normalize_projection_type: \
515535
found cache entry: in-progress"
516536
);
517537

518-
// But for now, let's classify this as an overflow:
519-
let recursion_limit = selcx.tcx().sess.recursion_limit();
520-
let obligation =
521-
Obligation::with_depth(cause, recursion_limit.0, param_env, projection_ty);
522-
selcx.infcx().report_overflow_error(&obligation, false);
538+
return Err(InProgress);
523539
}
524540
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
525541
// This is the hottest path in this function.
@@ -555,7 +571,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
555571
cause,
556572
depth,
557573
));
558-
return Some(ty.value);
574+
return Ok(Some(ty.value));
559575
}
560576
Err(ProjectionCacheEntry::Error) => {
561577
debug!(
@@ -564,7 +580,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
564580
);
565581
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
566582
obligations.extend(result.obligations);
567-
return Some(result.value);
583+
return Ok(Some(result.value));
568584
}
569585
}
570586

@@ -611,7 +627,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
611627
let cache_value = prune_cache_value_obligations(infcx, &result);
612628
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value);
613629
obligations.extend(result.obligations);
614-
Some(result.value)
630+
Ok(Some(result.value))
615631
}
616632
Ok(ProjectedTy::NoProgress(projected_ty)) => {
617633
debug!(
@@ -622,15 +638,15 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
622638
let result = Normalized { value: projected_ty, obligations: vec![] };
623639
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
624640
// No need to extend `obligations`.
625-
Some(result.value)
641+
Ok(Some(result.value))
626642
}
627643
Err(ProjectionTyError::TooManyCandidates) => {
628644
debug!(
629645
"opt_normalize_projection_type: \
630646
too many candidates"
631647
);
632648
infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key);
633-
None
649+
Ok(None)
634650
}
635651
Err(ProjectionTyError::TraitSelectionError(_)) => {
636652
debug!("opt_normalize_projection_type: ERROR");
@@ -642,7 +658,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
642658
infcx.inner.borrow_mut().projection_cache().error(cache_key);
643659
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
644660
obligations.extend(result.obligations);
645-
Some(result.value)
661+
Ok(Some(result.value))
646662
}
647663
}
648664
}
@@ -1116,11 +1132,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
11161132
}
11171133
super::ImplSourceAutoImpl(..) | super::ImplSourceBuiltin(..) => {
11181134
// These traits have no associated types.
1119-
span_bug!(
1135+
selcx.tcx().sess.delay_span_bug(
11201136
obligation.cause.span,
1121-
"Cannot project an associated type from `{:?}`",
1122-
impl_source
1137+
&format!("Cannot project an associated type from `{:?}`", impl_source),
11231138
);
1139+
return Err(());
11241140
}
11251141
};
11261142

src/librustc_trait_selection/traits/select/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
503503
let data = ty::Binder::bind(data);
504504
let project_obligation = obligation.with(data);
505505
match project::poly_project_and_unify_type(self, &project_obligation) {
506-
Ok(Some(mut subobligations)) => {
506+
Ok(Ok(Some(mut subobligations))) => {
507507
self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
508508
let result = self.evaluate_predicates_recursively(
509509
previous_stack,
@@ -516,7 +516,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
516516
}
517517
result
518518
}
519-
Ok(None) => Ok(EvaluatedToAmbig),
519+
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
520+
// EvaluatedToRecur might also be acceptable here, but use
521+
// Unknown for now because it means that we won't dismiss a
522+
// selection candidate solely because it has a projection
523+
// cycle. This is closest to the previous behavior of
524+
// immediately erroring.
525+
Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown),
520526
Err(_) => Ok(EvaluatedToErr),
521527
}
522528
}

src/test/ui/associated-types/defaults-cyclic-fail-1.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ impl Tr for u32 {
2626

2727
// ...but only if this actually breaks the cycle
2828
impl Tr for bool {
29-
//~^ ERROR overflow evaluating the requirement
29+
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
3030
type A = Box<Self::B>;
31-
//~^ ERROR overflow evaluating the requirement
31+
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
3232
}
3333
// (the error is shown twice for some reason)
3434

3535
impl Tr for usize {
36-
//~^ ERROR overflow evaluating the requirement
36+
//~^ ERROR type mismatch resolving `<usize as Tr>::B == _`
3737
type B = &'static Self::A;
38-
//~^ ERROR overflow evaluating the requirement
38+
//~^ ERROR type mismatch resolving `<usize as Tr>::A == _`
3939
}
4040

4141
fn main() {
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
1-
error[E0275]: overflow evaluating the requirement `<() as Tr>::B`
1+
error[E0275]: overflow evaluating the requirement `<() as Tr>::B == _`
22
--> $DIR/defaults-cyclic-fail-1.rs:10:6
33
|
44
LL | impl Tr for () {}
55
| ^^
66

7-
error[E0275]: overflow evaluating the requirement `<bool as Tr>::B`
7+
error[E0271]: type mismatch resolving `<bool as Tr>::B == _`
88
--> $DIR/defaults-cyclic-fail-1.rs:28:6
99
|
1010
LL | impl Tr for bool {
11-
| ^^
11+
| ^^ cyclic type of infinite size
1212

13-
error[E0275]: overflow evaluating the requirement `<usize as Tr>::B`
13+
error[E0271]: type mismatch resolving `<usize as Tr>::B == _`
1414
--> $DIR/defaults-cyclic-fail-1.rs:35:6
1515
|
1616
LL | impl Tr for usize {
17-
| ^^
17+
| ^^ cyclic type of infinite size
1818

19-
error[E0275]: overflow evaluating the requirement `<bool as Tr>::B`
19+
error[E0271]: type mismatch resolving `<bool as Tr>::B == _`
2020
--> $DIR/defaults-cyclic-fail-1.rs:30:5
2121
|
2222
LL | type A = Box<Self::B>;
23-
| ^^^^^^^^^^^^^^^^^^^^^^
23+
| ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
2424

25-
error[E0275]: overflow evaluating the requirement `<usize as Tr>::A`
25+
error[E0271]: type mismatch resolving `<usize as Tr>::A == _`
2626
--> $DIR/defaults-cyclic-fail-1.rs:37:5
2727
|
2828
LL | type B = &'static Self::A;
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size
3030

3131
error: aborting due to 5 previous errors
3232

33-
For more information about this error, try `rustc --explain E0275`.
33+
Some errors have detailed explanations: E0271, E0275.
34+
For more information about an error, try `rustc --explain E0271`.

src/test/ui/associated-types/defaults-cyclic-fail-2.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ trait Tr {
1010

1111
// ...but is an error in any impl that doesn't override at least one of the defaults
1212
impl Tr for () {}
13-
//~^ ERROR overflow evaluating the requirement
13+
//~^ ERROR type mismatch resolving `<() as Tr>::B == _`
1414

1515
// As soon as at least one is redefined, it works:
1616
impl Tr for u8 {
@@ -28,16 +28,16 @@ impl Tr for u32 {
2828

2929
// ...but only if this actually breaks the cycle
3030
impl Tr for bool {
31-
//~^ ERROR overflow evaluating the requirement
31+
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
3232
type A = Box<Self::B>;
33-
//~^ ERROR overflow evaluating the requirement
33+
//~^ ERROR type mismatch resolving `<bool as Tr>::B == _`
3434
}
3535
// (the error is shown twice for some reason)
3636

3737
impl Tr for usize {
38-
//~^ ERROR overflow evaluating the requirement
38+
//~^ ERROR type mismatch resolving `<usize as Tr>::B == _`
3939
type B = &'static Self::A;
40-
//~^ ERROR overflow evaluating the requirement
40+
//~^ ERROR type mismatch resolving `<usize as Tr>::A == _`
4141
}
4242

4343
fn main() {

0 commit comments

Comments
 (0)