Skip to content

Make inductive cycles always ambiguous#122791

Merged
bors merged 3 commits into
rust-lang:masterfrom
compiler-errors:make-coinductive-always
Apr 3, 2024
Merged

Make inductive cycles always ambiguous#122791
bors merged 3 commits into
rust-lang:masterfrom
compiler-errors:make-coinductive-always

Conversation

@compiler-errors

@compiler-errors compiler-errors commented Mar 20, 2024

Copy link
Copy Markdown
Contributor

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 #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 to:

// 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 #121123)

Due to the changes in #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 #121123.


r? lcnr for an initial review

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 20, 2024
@workingjubilee

Copy link
Copy Markdown
Member

ALL other regressions are due to commit_verify@0.11.0-beta.1 - 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.

-beta.5 should be a higher SemVer than -beta.1, and cargo should resolve these appropriately.

@compiler-errors

Copy link
Copy Markdown
Contributor Author

Apologies. I misread the crater report. The regressions are a combination of 0.10.x and 0.11.0-beta.1 (I guess they're hard-coding that in their cargo toml? or have their cargo locks commited? or something).

For the record, the regression was removed when a bunch of impls were reworked in: LNP-BP/client_side_validation#152.

@compiler-errors

compiler-errors commented Mar 21, 2024

Copy link
Copy Markdown
Contributor Author

Regarding commit_verify, the regression is because there's a fatal overflow when trying to prove: for<'a> Holder<&'a &T, strategies::AsRef>: encode::CommitEncode:

/// Used only internally for blank implementation on reference types.
#[doc(hidden)]
pub enum AsRef {}

impl<'a, T> CommitEncode for Holder<&'a T, AsRef>
where
    T: CommitEncode,
{
    fn commit_encode(&self, e: &mut impl io::Write) {
        self.as_type().commit_encode(e);
    }
}

impl<T> CommitStrategy for &T
where
    T: CommitEncode,
{
    type Strategy = AsRef;
}

impl<T> CommitEncode for T
where
    T: CommitStrategy,
    for<'a> Holder<&'a T, T::Strategy>: CommitEncode,
{
    fn commit_encode(&self, e: &mut impl io::Write) 
        Holder::new(self).commit_encode(e)
    }
}

... which I guess because we don't treat ambiguity as error, we end up actually evaluating to overflow.

Removing AsRef (which is #[doc(hidden)] in the code anyways, and "[u]sed only internally for blank implementation on reference types") fixes the problem, for the record.

@lcnr lcnr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

"fatal overflow" i guess?

What is a test which is affected here. How does this handle <AssocTy as Id>::Assoc: Bound? writing a test rn

/// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`.
/// This can't be trivially cached because the result depends on the
/// stack results.
EvaluatedToAmbigStackDependent,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated: isn't this just utterly broken if you have stack dependent ambiguity resulting in difference inference constraints, causing a later goal to error without stack dependence?

anyways, the old solver cannot be salvaged. This is an existing issue 😁

Comment thread compiler/rustc_trait_selection/src/traits/select/mod.rs Outdated
@lcnr

lcnr commented Mar 21, 2024

Copy link
Copy Markdown
Contributor

This already fails on nightly, so this PR seems good to me. I am a bit confused why though, we somehow keep the <Self::Assoc as Id>::This: Copy as a trait where-bound instead of an alias item bound?

trait Id {
    type This: ?Sized;
}
impl<T: ?Sized> Id for T {
    type This = T;
}

trait Trait
where
    <Self::Assoc as Id>::This: Copy,
{
    type Assoc;
}

fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) {
    (x, x)
}

edit: it's because that hasn't landed yet '^^ that is from a separate PR #120752

The following also already fails, which makes sense, but seems like a shortcoming of our approach to normalization

#![feature(associated_type_bounds)]
trait Id {
    type This: ?Sized;
}
impl<T: ?Sized> Id for T {
    type This = T;
}

trait Trait {
    type Assoc: Id<This: Copy>;
}

fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) {
    (x, x)
}

however, why does the following compile?

#![feature(associated_type_bounds)]
trait Id {
    type This: ?Sized;
}

trait Trait {
    type Assoc: Id<This: Copy>;
}

fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc)
where
    T::Assoc: Id<This = T::Assoc>,
{
    (x, x)
}

please add these all as tests 😁

@lcnr lcnr added T-types Relevant to the types team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 21, 2024
@lcnr

lcnr commented Mar 21, 2024

Copy link
Copy Markdown
Contributor

Ignoring the question about nested item_bounds, this PR changes inductive cycles to always result in ambiguity. This matches the behavior of the new trait solver and is an imo necessary step towards changing more cycles to be coinductive, at which point the cycles change to success. This is already the behavior during coherence since #118649.

For more details, see the PR description by @compiler-errors.

@rfcbot fcp merge

@rfcbot

rfcbot commented Mar 21, 2024

Copy link
Copy Markdown

Team member @lcnr has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Mar 21, 2024
@bors

bors commented Mar 21, 2024

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (presumably #121123) made this pull request unmergeable. Please resolve the merge conflicts.

@compiler-errors

This comment was marked as outdated.

@compiler-errors

Copy link
Copy Markdown
Contributor Author

Actually, yeah, it's due to candidate shadowing, just for associated item bounds shadowing the global impl.

@rfcbot rfcbot added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Mar 22, 2024
@rfcbot

rfcbot commented Mar 22, 2024

Copy link
Copy Markdown

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot removed the proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. label Mar 22, 2024
@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job aarch64-gnu failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[RUSTC-TIMING] rustc_monomorphize test:false 25.753
   Compiling rustc_trait_selection v0.0.0 (/checkout/compiler/rustc_trait_selection)
[RUSTC-TIMING] rustc_pattern_analysis test:false 26.309
[RUSTC-TIMING] rustc_smir test:false 29.654
##[error]The runner has received a shutdown signal. This can happen when the runner service is stopped, or a manually started runner is canceled.

@bors

bors commented Apr 3, 2024

Copy link
Copy Markdown
Collaborator

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Apr 3, 2024
@compiler-errors

Copy link
Copy Markdown
Contributor Author

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 3, 2024
@bors

bors commented Apr 3, 2024

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 56dbeeb with merge 40f743d...

@bors

bors commented Apr 3, 2024

Copy link
Copy Markdown
Collaborator

☀️ Test successful - checks-actions
Approved by: lcnr
Pushing 40f743d to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Apr 3, 2024
@bors bors merged commit 40f743d into rust-lang:master Apr 3, 2024
@rustbot rustbot added this to the 1.79.0 milestone Apr 3, 2024
@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (40f743d): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
2.2% [0.8%, 4.2%] 5
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.2% [0.8%, 4.2%] 5

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
1.1% [1.1%, 1.1%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.0% [-1.0%, -1.0%] 1
All ❌✅ (primary) 1.1% [1.1%, 1.1%] 1

Binary size

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.2% [-0.2%, -0.2%] 1

Bootstrap: 668.585s -> 668.957s (0.06%)
Artifact size: 315.61 MiB -> 315.62 MiB (0.00%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Order-dependence of dyn Trait: Supertrait goals causes incompleteness (old solver)

10 participants