Skip to content

Commit 1101496

Browse files
committed
Don't immediately error for cycles during normalization
1 parent 1c5fe50 commit 1101496

16 files changed

+176
-90
lines changed

src/librustc_trait_selection/traits/auto_trait.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ impl AutoTraitFinder<'tcx> {
729729
// and turn them into an explicit negative impl for our type.
730730
debug!("Projecting and unifying projection predicate {:?}", predicate);
731731

732-
match poly_project_and_unify_type(select, &obligation.with(p)) {
732+
match project::poly_project_and_unify_type(select, &obligation.with(p)) {
733733
Err(e) => {
734734
debug!(
735735
"evaluate_nested_obligations: Unable to unify predicate \
@@ -738,7 +738,11 @@ impl AutoTraitFinder<'tcx> {
738738
);
739739
return false;
740740
}
741-
Ok(Some(v)) => {
741+
Ok(Err(project::InProgress)) => {
742+
debug!("evaluate_nested_obligations: recursive projection predicate");
743+
return false;
744+
}
745+
Ok(Ok(Some(v))) => {
742746
// We only care about sub-obligations
743747
// when we started out trying to unify
744748
// some inference variables. See the comment above
@@ -757,8 +761,8 @@ impl AutoTraitFinder<'tcx> {
757761
}
758762
}
759763
}
760-
Ok(None) => {
761-
// It's ok not to make progress when hvave no inference variables -
764+
Ok(Ok(None)) => {
765+
// It's ok not to make progress when have no inference variables -
762766
// in that case, we were only performing unifcation to check if an
763767
// error occurred (which would indicate that it's impossible for our
764768
// type to implement the auto trait).

src/librustc_trait_selection/traits/fulfill.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -426,14 +426,20 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
426426

427427
ty::PredicateKind::Projection(ref data) => {
428428
let project_obligation = obligation.with(*data);
429+
let tcx = self.selcx.tcx();
429430
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
430-
Ok(None) => {
431-
let tcx = self.selcx.tcx();
432-
pending_obligation.stalled_on =
433-
trait_ref_infer_vars(self.selcx, data.to_poly_trait_ref(tcx));
431+
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
432+
Ok(Ok(None)) => {
433+
pending_obligation.stalled_on = trait_ref_infer_vars(
434+
self.selcx,
435+
project_obligation.predicate.to_poly_trait_ref(tcx),
436+
);
434437
ProcessResult::Unchanged
435438
}
436-
Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)),
439+
// Let the caller handle the recursion
440+
Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![
441+
pending_obligation.obligation.clone(),
442+
])),
437443
Err(e) => ProcessResult::Error(CodeProjectionError(e)),
438444
}
439445
}

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
@@ -40,6 +40,8 @@ pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'
4040

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

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

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

173196
let mut obligations = vec![];
@@ -179,8 +202,9 @@ fn project_and_unify_type<'cx, 'tcx>(
179202
obligation.recursion_depth,
180203
&mut obligations,
181204
) {
182-
Some(n) => n,
183-
None => return Ok(None),
205+
Ok(Some(n)) => n,
206+
Ok(None) => return Ok(Ok(None)),
207+
Err(InProgress) => return Ok(Err(InProgress)),
184208
};
185209

186210
debug!(
@@ -195,7 +219,7 @@ fn project_and_unify_type<'cx, 'tcx>(
195219
{
196220
Ok(InferOk { obligations: inferred_obligations, value: () }) => {
197221
obligations.extend(inferred_obligations);
198-
Ok(Some(obligations))
222+
Ok(Ok(Some(obligations)))
199223
}
200224
Err(err) => {
201225
debug!("project_and_unify_type: equating types encountered error {:?}", err);
@@ -418,6 +442,8 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
418442
depth,
419443
obligations,
420444
)
445+
.ok()
446+
.flatten()
421447
.unwrap_or_else(move || {
422448
// if we bottom out in ambiguity, create a type variable
423449
// and a deferred predicate to resolve this when more type
@@ -454,7 +480,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
454480
cause: ObligationCause<'tcx>,
455481
depth: usize,
456482
obligations: &mut Vec<PredicateObligation<'tcx>>,
457-
) -> Option<Ty<'tcx>> {
483+
) -> Result<Option<Ty<'tcx>>, InProgress> {
458484
let infcx = selcx.infcx();
459485

460486
let projection_ty = infcx.resolve_vars_if_possible(&projection_ty);
@@ -486,7 +512,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
486512
"opt_normalize_projection_type: \
487513
found cache entry: ambiguous"
488514
);
489-
return None;
515+
return Ok(None);
490516
}
491517
Err(ProjectionCacheEntry::InProgress) => {
492518
// If while normalized A::B, we are asked to normalize
@@ -501,24 +527,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
501527
// to normalize `A::B`, we will want to check the
502528
// where-clauses in scope. So we will try to unify `A::B`
503529
// with `A::B`, which can trigger a recursive
504-
// normalization. In that case, I think we will want this code:
505-
//
506-
// ```
507-
// let ty = selcx.tcx().mk_projection(projection_ty.item_def_id,
508-
// projection_ty.substs;
509-
// return Some(NormalizedTy { value: v, obligations: vec![] });
510-
// ```
530+
// normalization.
511531

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

517-
// But for now, let's classify this as an overflow:
518-
let recursion_limit = selcx.tcx().sess.recursion_limit();
519-
let obligation =
520-
Obligation::with_depth(cause, recursion_limit.0, param_env, projection_ty);
521-
selcx.infcx().report_overflow_error(&obligation, false);
537+
return Err(InProgress);
522538
}
523539
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
524540
// This is the hottest path in this function.
@@ -554,7 +570,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
554570
cause,
555571
depth,
556572
));
557-
return Some(ty.value);
573+
return Ok(Some(ty.value));
558574
}
559575
Err(ProjectionCacheEntry::Error) => {
560576
debug!(
@@ -563,7 +579,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
563579
);
564580
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
565581
obligations.extend(result.obligations);
566-
return Some(result.value);
582+
return Ok(Some(result.value));
567583
}
568584
}
569585

@@ -610,7 +626,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
610626
let cache_value = prune_cache_value_obligations(infcx, &result);
611627
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value);
612628
obligations.extend(result.obligations);
613-
Some(result.value)
629+
Ok(Some(result.value))
614630
}
615631
Ok(ProjectedTy::NoProgress(projected_ty)) => {
616632
debug!(
@@ -621,15 +637,15 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
621637
let result = Normalized { value: projected_ty, obligations: vec![] };
622638
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
623639
// No need to extend `obligations`.
624-
Some(result.value)
640+
Ok(Some(result.value))
625641
}
626642
Err(ProjectionTyError::TooManyCandidates) => {
627643
debug!(
628644
"opt_normalize_projection_type: \
629645
too many candidates"
630646
);
631647
infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key);
632-
None
648+
Ok(None)
633649
}
634650
Err(ProjectionTyError::TraitSelectionError(_)) => {
635651
debug!("opt_normalize_projection_type: ERROR");
@@ -641,7 +657,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
641657
infcx.inner.borrow_mut().projection_cache().error(cache_key);
642658
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
643659
obligations.extend(result.obligations);
644-
Some(result.value)
660+
Ok(Some(result.value))
645661
}
646662
}
647663
}
@@ -1112,11 +1128,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
11121128
}
11131129
super::ImplSourceAutoImpl(..) | super::ImplSourceBuiltin(..) => {
11141130
// These traits have no associated types.
1115-
span_bug!(
1131+
selcx.tcx().sess.delay_span_bug(
11161132
obligation.cause.span,
1117-
"Cannot project an associated type from `{:?}`",
1118-
impl_source
1133+
&format!("Cannot project an associated type from `{:?}`", impl_source),
11191134
);
1135+
return Err(());
11201136
}
11211137
};
11221138

src/librustc_trait_selection/traits/select/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
458458
&ty::PredicateKind::Projection(data) => {
459459
let project_obligation = obligation.with(data);
460460
match project::poly_project_and_unify_type(self, &project_obligation) {
461-
Ok(Some(mut subobligations)) => {
461+
Ok(Ok(Some(mut subobligations))) => {
462462
self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
463463
let result = self.evaluate_predicates_recursively(
464464
previous_stack,
@@ -471,7 +471,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
471471
}
472472
result
473473
}
474-
Ok(None) => Ok(EvaluatedToAmbig),
474+
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
475+
// EvaluatedToRecur might also be acceptable here, but use
476+
// Unknown for now because it means that we won't dismiss a
477+
// selection candidate solely because it has a projection
478+
// cycle. This is closest to the previous behavior of
479+
// immediately erroring.
480+
Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown),
475481
Err(_) => Ok(EvaluatedToErr),
476482
}
477483
}

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)