Skip to content

Commit 32f1b4a

Browse files
committed
Auto merge of rust-lang#122791 - compiler-errors:make-coinductive-always, r=lcnr
Make inductive cycles always ambiguous This makes inductive cycles always result in ambiguity rather than be treated like a stack-dependent error. This has some interactions with specialization, and so breaks a few UI tests that I don't agree should've ever worked in the first place, and also breaks a handful of crates in a way that I don't believe is a problem. On the bright side, it puts us in a better spot when it comes to eventually enabling coinduction everywhere. ## Results This was cratered in rust-lang#116494 (comment), which boils down to two regressions: * `lu_packets` - This code should have never compiled in the first place. More below. * **ALL** other regressions are due to `commit_verify@0.11.0-beta.1` (edit: and `commit_verify@0.10.x`) - This actually seems to be fixed in version `0.11.0-beta.5`, which is the *most* up to date version, but it's still prerelease on crates.io so I don't think cargo ends up picking `beta.5` when building dependent crates. ### `lu_packets` Firstly, this crate uses specialization, so I think it's automatically worth breaking. However, I've minimized [the regression](https://crater-reports.s3.amazonaws.com/pr-116494-3/try%23d614ed876e31a5f3ad1d0fbf848fcdab3a29d1d8/gh/lcdr.lu_packets/log.txt) to: ```rust // Upstream crate pub trait Serialize {} impl Serialize for &() {} impl<S> Serialize for &[S] where for<'a> &'a S: Serialize {} // ----------------------------------------------------------------------- // // Downstream crate #![feature(specialization)] #![allow(incomplete_features, unused)] use upstream::Serialize; trait Replica { fn serialize(); } impl<T> Replica for T { default fn serialize() {} } impl<T> Replica for Option<T> where for<'a> &'a T: Serialize, { fn serialize() {} } ``` Specifically this fails when computing the specialization graph for the `downstream` crate. The code ends up cycling on `&[?0]: Serialize` when we equate `&?0 = &[?1]` during impl matching, which ends up needing to prove `&[?1]: Serialize`, which since cycles are treated like ambiguity, ends up in a **fatal overflow**. For some reason this requires two crates, squashing them into one crate doesn't work. Side-note: This code is subtly order dependent. When minimizing, I ended up having the code start failing on `nightly` very easily after removing and reordering impls. This seems to me all the more reason to remove this behavior altogether. ## Side-note: Item Bounds (edit: this was fixed independently in rust-lang#121123) Due to the changes in rust-lang#120584 where we now consider an alias's item bounds *and* all the item bounds of the alias's nested self type aliases, I've had to add e6b64c6 which is a hack to make sure we're not eagerly normalizing bounds that have nothing to do with the predicate we're trying to solve, and which result in. This is fixed in a more principled way in rust-lang#121123. --- r? lcnr for an initial review
2 parents 88c2f4f + 56dbeeb commit 32f1b4a

File tree

12 files changed

+104
-117
lines changed

12 files changed

+104
-117
lines changed

compiler/rustc_middle/src/traits/select.rs

+4-48
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,6 @@ pub enum SelectionCandidate<'tcx> {
193193
/// The evaluation results are ordered:
194194
/// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions`
195195
/// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent`
196-
/// - `EvaluatedToErr` implies `EvaluatedToErrStackDependent`
197196
/// - the "union" of evaluation results is equal to their maximum -
198197
/// all the "potential success" candidates can potentially succeed,
199198
/// so they are noops when unioned with a definite error, and within
@@ -219,52 +218,9 @@ pub enum EvaluationResult {
219218
/// variables. We are somewhat imprecise there, so we don't actually
220219
/// know the real result.
221220
///
222-
/// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`.
221+
/// This can't be trivially cached because the result depends on the
222+
/// stack results.
223223
EvaluatedToAmbigStackDependent,
224-
/// Evaluation failed because we encountered an obligation we are already
225-
/// trying to prove on this branch.
226-
///
227-
/// We know this branch can't be a part of a minimal proof-tree for
228-
/// the "root" of our cycle, because then we could cut out the recursion
229-
/// and maintain a valid proof tree. However, this does not mean
230-
/// that all the obligations on this branch do not hold -- it's possible
231-
/// that we entered this branch "speculatively", and that there
232-
/// might be some other way to prove this obligation that does not
233-
/// go through this cycle -- so we can't cache this as a failure.
234-
///
235-
/// For example, suppose we have this:
236-
///
237-
/// ```rust,ignore (pseudo-Rust)
238-
/// pub trait Trait { fn xyz(); }
239-
/// // This impl is "useless", but we can still have
240-
/// // an `impl Trait for SomeUnsizedType` somewhere.
241-
/// impl<T: Trait + Sized> Trait for T { fn xyz() {} }
242-
///
243-
/// pub fn foo<T: Trait + ?Sized>() {
244-
/// <T as Trait>::xyz();
245-
/// }
246-
/// ```
247-
///
248-
/// When checking `foo`, we have to prove `T: Trait`. This basically
249-
/// translates into this:
250-
///
251-
/// ```plain,ignore
252-
/// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait
253-
/// ```
254-
///
255-
/// When we try to prove it, we first go the first option, which
256-
/// recurses. This shows us that the impl is "useless" -- it won't
257-
/// tell us that `T: Trait` unless it already implemented `Trait`
258-
/// by some other means. However, that does not prevent `T: Trait`
259-
/// does not hold, because of the bound (which can indeed be satisfied
260-
/// by `SomeUnsizedType` from another crate).
261-
//
262-
// FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we
263-
// ought to convert it to an `EvaluatedToErr`, because we know
264-
// there definitely isn't a proof tree for that obligation. Not
265-
// doing so is still sound -- there isn't any proof tree, so the
266-
// branch still can't be a part of a minimal one -- but does not re-enable caching.
267-
EvaluatedToErrStackDependent,
268224
/// Evaluation failed.
269225
EvaluatedToErr,
270226
}
@@ -290,13 +246,13 @@ impl EvaluationResult {
290246
| EvaluatedToAmbig
291247
| EvaluatedToAmbigStackDependent => true,
292248

293-
EvaluatedToErr | EvaluatedToErrStackDependent => false,
249+
EvaluatedToErr => false,
294250
}
295251
}
296252

297253
pub fn is_stack_dependent(self) -> bool {
298254
match self {
299-
EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true,
255+
EvaluatedToAmbigStackDependent => true,
300256

301257
EvaluatedToOkModuloOpaqueTypes
302258
| EvaluatedToOk

compiler/rustc_trait_selection/src/infer.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ impl<'tcx> InferCtxt<'tcx> {
4949
/// - the parameter environment
5050
///
5151
/// Invokes `evaluate_obligation`, so in the event that evaluating
52-
/// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent
53-
/// (or EvaluatedToAmbigStackDependent) will be returned.
52+
/// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned.
5453
#[instrument(level = "debug", skip(self, params), ret)]
5554
fn type_implements_trait(
5655
&self,

compiler/rustc_trait_selection/src/traits/coherence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ fn overlap<'tcx>(
210210
.intercrate(true)
211211
.with_next_trait_solver(tcx.next_trait_solver_in_coherence())
212212
.build();
213-
let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx);
213+
let selcx = &mut SelectionContext::new(&infcx);
214214
if track_ambiguity_causes.is_yes() {
215215
selcx.enable_tracking_intercrate_ambiguity_causes();
216216
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+3-39
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ pub struct SelectionContext<'cx, 'tcx> {
126126
/// policy. In essence, canonicalized queries need their errors propagated
127127
/// rather than immediately reported because we do not have accurate spans.
128128
query_mode: TraitQueryMode,
129-
130-
treat_inductive_cycle: TreatInductiveCycleAs,
131129
}
132130

133131
// A stack that walks back up the stack frame.
@@ -208,47 +206,13 @@ enum BuiltinImplConditions<'tcx> {
208206
Ambiguous,
209207
}
210208

211-
#[derive(Copy, Clone)]
212-
pub enum TreatInductiveCycleAs {
213-
/// This is the previous behavior, where `Recur` represents an inductive
214-
/// cycle that is known not to hold. This is not forwards-compatible with
215-
/// coinduction, and will be deprecated. This is the default behavior
216-
/// of the old trait solver due to back-compat reasons.
217-
Recur,
218-
/// This is the behavior of the new trait solver, where inductive cycles
219-
/// are treated as ambiguous and possibly holding.
220-
Ambig,
221-
}
222-
223-
impl From<TreatInductiveCycleAs> for EvaluationResult {
224-
fn from(treat: TreatInductiveCycleAs) -> EvaluationResult {
225-
match treat {
226-
TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent,
227-
TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent,
228-
}
229-
}
230-
}
231-
232209
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
233210
pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
234211
SelectionContext {
235212
infcx,
236213
freshener: infcx.freshener(),
237214
intercrate_ambiguity_causes: None,
238215
query_mode: TraitQueryMode::Standard,
239-
treat_inductive_cycle: TreatInductiveCycleAs::Recur,
240-
}
241-
}
242-
243-
pub fn with_treat_inductive_cycle_as_ambig(
244-
infcx: &'cx InferCtxt<'tcx>,
245-
) -> SelectionContext<'cx, 'tcx> {
246-
// Should be executed in a context where caching is disabled,
247-
// otherwise the cache is poisoned with the temporary result.
248-
assert!(infcx.intercrate);
249-
SelectionContext {
250-
treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
251-
..SelectionContext::new(infcx)
252216
}
253217
}
254218

@@ -756,7 +720,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
756720
stack.update_reached_depth(stack_arg.1);
757721
return Ok(EvaluatedToOk);
758722
} else {
759-
return Ok(self.treat_inductive_cycle.into());
723+
return Ok(EvaluatedToAmbigStackDependent);
760724
}
761725
}
762726
return Ok(EvaluatedToOk);
@@ -875,7 +839,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
875839
}
876840
}
877841
ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig),
878-
ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()),
842+
ProjectAndUnifyResult::Recursive => Ok(EvaluatedToAmbigStackDependent),
879843
ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr),
880844
}
881845
}
@@ -1180,7 +1144,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11801144
Some(EvaluatedToOk)
11811145
} else {
11821146
debug!("evaluate_stack --> recursive, inductive");
1183-
Some(self.treat_inductive_cycle.into())
1147+
Some(EvaluatedToAmbigStackDependent)
11841148
}
11851149
} else {
11861150
None

tests/ui/marker_trait_attr/unsound-overlap.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ trait B {}
88
impl<T: A> B for T {}
99
impl<T: B> A for T {}
1010
impl A for &str {}
11+
//~^ ERROR type annotations needed: cannot satisfy `&str: A`
1112
impl<T: A + B> A for (T,) {}
1213
trait TraitWithAssoc {
1314
type Assoc;
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1+
error[E0283]: type annotations needed: cannot satisfy `&str: A`
2+
--> $DIR/unsound-overlap.rs:10:12
3+
|
4+
LL | impl A for &str {}
5+
| ^^^^
6+
|
7+
note: multiple `impl`s satisfying `&str: A` found
8+
--> $DIR/unsound-overlap.rs:9:1
9+
|
10+
LL | impl<T: B> A for T {}
11+
| ^^^^^^^^^^^^^^^^^^
12+
LL | impl A for &str {}
13+
| ^^^^^^^^^^^^^^^
14+
115
error[E0119]: conflicting implementations of trait `TraitWithAssoc` for type `((&str,),)`
2-
--> $DIR/unsound-overlap.rs:20:1
16+
--> $DIR/unsound-overlap.rs:21:1
317
|
418
LL | impl<T: A> TraitWithAssoc for T {
519
| ------------------------------- first implementation here
620
...
721
LL | impl TraitWithAssoc for ((&str,),) {
822
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `((&str,),)`
923

10-
error: aborting due to 1 previous error
24+
error: aborting due to 2 previous errors
1125

12-
For more information about this error, try `rustc --explain E0119`.
26+
Some errors have detailed explanations: E0119, E0283.
27+
For more information about an error, try `rustc --explain E0119`.

tests/ui/specialization/issue-39448.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ trait FromA<T> {
2222
}
2323

2424
impl<T: A, U: A + FromA<T>> FromA<T> for U {
25+
//~^ ERROR cycle detected when computing whether impls specialize one another
2526
default fn from(x: T) -> Self {
2627
ToA::to(x)
2728
}
@@ -42,7 +43,7 @@ where
4243

4344
#[allow(dead_code)]
4445
fn foo<T: A, U: A>(x: T, y: U) -> U {
45-
x.foo(y.to()).to() //~ ERROR overflow evaluating the requirement
46+
x.foo(y.to()).to()
4647
}
4748

4849
fn main() {

tests/ui/specialization/issue-39448.stderr

+12-19
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,21 @@ LL | #![feature(specialization)]
88
= help: consider using `min_specialization` instead, which is more stable and complete
99
= note: `#[warn(incomplete_features)]` on by default
1010

11-
error[E0275]: overflow evaluating the requirement `T: FromA<U>`
12-
--> $DIR/issue-39448.rs:45:13
13-
|
14-
LL | x.foo(y.to()).to()
15-
| ^^
16-
|
17-
note: required for `T` to implement `FromA<U>`
18-
--> $DIR/issue-39448.rs:24:29
11+
error[E0391]: cycle detected when computing whether impls specialize one another
12+
--> $DIR/issue-39448.rs:24:1
1913
|
2014
LL | impl<T: A, U: A + FromA<T>> FromA<T> for U {
21-
| -------- ^^^^^^^^ ^
22-
| |
23-
| unsatisfied trait bound introduced here
24-
note: required for `U` to implement `ToA<T>`
25-
--> $DIR/issue-39448.rs:34:12
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= note: ...which requires evaluating trait selection obligation `u16: FromA<u8>`...
18+
= note: ...which again requires computing whether impls specialize one another, completing the cycle
19+
note: cycle used when building specialization graph of trait `FromA`
20+
--> $DIR/issue-39448.rs:20:1
2621
|
27-
LL | impl<T, U> ToA<U> for T
28-
| ^^^^^^ ^
29-
LL | where
30-
LL | U: FromA<T>,
31-
| -------- unsatisfied trait bound introduced here
22+
LL | trait FromA<T> {
23+
| ^^^^^^^^^^^^^^
24+
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
3225

3326
error: aborting due to 1 previous error; 1 warning emitted
3427

35-
For more information about this error, try `rustc --explain E0275`.
28+
For more information about this error, try `rustc --explain E0391`.

tests/ui/specialization/issue-39618.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// FIXME(JohnTitor): Centril pointed out this looks suspicions, we should revisit here.
33
// More context: https://github.com/rust-lang/rust/pull/69192#discussion_r379846796
44

5-
//@ check-pass
6-
75
#![feature(specialization)] //~ WARN the feature `specialization` is incomplete
86

97
trait Foo {
@@ -19,6 +17,7 @@ impl<T> Bar for T where T: Foo {
1917
}
2018

2119
impl<T> Foo for T where T: Bar {
20+
//~^ ERROR cycle detected when computing whether impls specialize one another
2221
fn foo(&self) {}
2322
}
2423

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2-
--> $DIR/issue-39618.rs:7:12
2+
--> $DIR/issue-39618.rs:5:12
33
|
44
LL | #![feature(specialization)]
55
| ^^^^^^^^^^^^^^
@@ -8,5 +8,21 @@ LL | #![feature(specialization)]
88
= help: consider using `min_specialization` instead, which is more stable and complete
99
= note: `#[warn(incomplete_features)]` on by default
1010

11-
warning: 1 warning emitted
11+
error[E0391]: cycle detected when computing whether impls specialize one another
12+
--> $DIR/issue-39618.rs:19:1
13+
|
14+
LL | impl<T> Foo for T where T: Bar {
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= note: ...which requires evaluating trait selection obligation `u64: Bar`...
18+
= note: ...which again requires computing whether impls specialize one another, completing the cycle
19+
note: cycle used when building specialization graph of trait `Foo`
20+
--> $DIR/issue-39618.rs:7:1
21+
|
22+
LL | trait Foo {
23+
| ^^^^^^^^^
24+
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
25+
26+
error: aborting due to 1 previous error; 1 warning emitted
1227

28+
For more information about this error, try `rustc --explain E0391`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ check-pass
2+
// Regression test for <https://github.com/rust-lang/rust/issues/123303>.
3+
// This time EXCEPT without `dyn` builtin bounds :^)
4+
5+
pub trait Trait: Supertrait {}
6+
7+
trait Impossible {}
8+
impl<F: ?Sized + Impossible> Trait for F {}
9+
10+
pub trait Supertrait {}
11+
12+
impl<T: ?Sized + Trait + Impossible> Supertrait for T {}
13+
14+
fn needs_supertrait<T: ?Sized + Supertrait>() {}
15+
fn needs_trait<T: ?Sized + Trait>() {}
16+
17+
struct A;
18+
impl Trait for A where A: Supertrait {}
19+
impl Supertrait for A {}
20+
21+
fn main() {
22+
needs_supertrait::<A>();
23+
needs_trait::<A>();
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ check-pass
2+
// Regression test for <https://github.com/rust-lang/rust/issues/123303>.
3+
4+
pub trait Trait: Supertrait {}
5+
6+
trait Impossible {}
7+
impl<F: ?Sized + Impossible> Trait for F {}
8+
9+
pub trait Supertrait {}
10+
11+
impl<T: ?Sized + Trait + Impossible> Supertrait for T {}
12+
13+
fn needs_supertrait<T: ?Sized + Supertrait>() {}
14+
fn needs_trait<T: ?Sized + Trait>() {}
15+
16+
fn main() {
17+
needs_supertrait::<dyn Trait>();
18+
needs_trait::<dyn Trait>();
19+
}

0 commit comments

Comments
 (0)