Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Anti-)regression between Rust 1.78.0 and Rust 1.79.0 with struct containing Cow<[Self]> #129541

Closed
zachs18 opened this issue Aug 25, 2024 · 27 comments · Fixed by #129542 or #133467
Closed
Labels
C-bug Category: This is a bug. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. finished-final-comment-period The final comment period is finished for this PR / Issue. regression-untriaged Untriaged performance or correctness regression. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@zachs18
Copy link
Contributor

zachs18 commented Aug 25, 2024

Code

I tried this code:

#![allow(unused)]
#[derive(Clone)] // nothing substantial changes if this is replaced with a manual impl Clone for Test
struct Hello {
    a: std::borrow::Cow<'static, [Self]>,
}
fn main(){}

I expected to see this happen: On 1.78 and below, this fails to compile with several E0277s (trait bound not satisfied)

full error message
error[E0277]: the trait bound `[Hello]: ToOwned` is not satisfied in `Hello`
 --> src/main.rs:4:32
  |
4 |   a: std::borrow::Cow<'static, [Self]>,
  |                                ^^^^^^ within `Hello`, the trait `ToOwned` is not implemented for `[Hello]`, which is required by `Hello: Sized`
  |
  = help: the trait `ToOwned` is implemented for `[T]`
note: required because it appears within the type `Hello`
 --> src/main.rs:3:8
  |
3 | struct Hello {
  |        ^^^^^
  = note: slice and array elements must have `Sized` type

error[E0277]: the trait bound `[Hello]: ToOwned` is not satisfied in `Hello`
 --> src/main.rs:2:10
  |
2 | #[derive(Clone)]
  |          ^^^^^ within `Hello`, the trait `ToOwned` is not implemented for `[Hello]`, which is required by `Hello: Sized`
  |
  = help: the trait `ToOwned` is implemented for `[T]`
note: required because it appears within the type `Hello`
 --> src/main.rs:3:8
  |
3 | struct Hello {
  |        ^^^^^
note: required by a bound in `Clone`
 --> /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/clone.rs:147:1
  = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `[Hello]: ToOwned` is not satisfied in `Hello`
 --> src/main.rs:2:10
  |
2 | #[derive(Clone)]
  |          ^^^^^ within `Hello`, the trait `ToOwned` is not implemented for `[Hello]`, which is required by `Hello: Sized`
  |
  = help: the trait `ToOwned` is implemented for `[T]`
note: required because it appears within the type `Hello`
 --> src/main.rs:3:8
  |
3 | struct Hello {
  |        ^^^^^
  = note: the return type of a function must have a statically known size
  = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `[Hello]: ToOwned` is not satisfied in `Cow<'_, [Hello]>`
 --> src/main.rs:4:3
  |
2 | #[derive(Clone)]
  |          ----- in this derive macro expansion
3 | struct Hello {
4 |   a: std::borrow::Cow<'static, [Self]>,
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `Cow<'_, [Hello]>`, the trait `ToOwned` is not implemented for `[Hello]`, which is required by `Cow<'_, [Hello]>: Sized`
  |
  = help: the trait `ToOwned` is implemented for `[T]`
note: required because it appears within the type `Cow<'_, [Hello]>`
 --> /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/borrow.rs:180:10
note: required by a bound in `clone`
 --> /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/clone.rs:160:5
  = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `bisectee` (bin "bisectee") due to 4 previous errors

Instead, this happened: On Rust 1.79 and above, it compiles without error

Version it didn't work on

It most recently didn't work on: Rust 1.78.0

Version with anti-regression

rustc --version --verbose:

rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: x86_64-unknown-linux-gnu
release: 1.79.0
LLVM version: 18.1.7

Bisection:

searched nightlies: from nightly-2024-03-16 to nightly-2024-04-28
regressed nightly: nightly-2024-03-20
searched commit range: 3c85e56...a7e4de1
regressed commit: 196ff44

bisected with cargo-bisect-rustc v0.6.9

Host triple: x86_64-unknown-linux-gnu
Reproduce with:

cargo bisect-rustc --start=1.78.0 --end=1.79.0 --regress success --preserve 

cc @lukas-code The description of #122493 says "This should not make more or less code compile, but it can change error output in some rare cases."

Probably nothing to do but add a test to make sure this keeps compiling in the future?

@zachs18 zachs18 added C-bug Category: This is a bug. regression-untriaged Untriaged performance or correctness regression. labels Aug 25, 2024
@rustbot rustbot added I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Aug 25, 2024
@theemathas
Copy link
Contributor

The code you show has Self but the error has [Self]. Is this intentional?

@workingjubilee
Copy link
Member

Fixed. The gression did indeed happen for [Self]:

https://rust.godbolt.org/z/4TY7a54Ps

zachs18 added a commit to zachs18/rust that referenced this issue Aug 25, 2024
@jieyouxu jieyouxu added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Aug 25, 2024
@compiler-errors
Copy link
Member

So I expect this code to compile, especially given that Cow<'_, [T]> is Sized, so this struct is coinductively Sized. There's a slight "risk" w/ this code compiling wrt the way we handle coinduction the solver, but I'll leave that to @lcnr to judge.

It would be interesting to know what specifically about #122493 caused this to compile, though I haven't looked at all about it.

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Aug 25, 2024

I think this is more about Clone rather than Sized-ness by itself. (for Sized there is an example which compiled since forever, Cow<[T]> doesn't require coinduction to be Sized -- it contains a &[T] (trivially sized) and <[T] as ToOwned>::Owned (which has an implicit : Sized bound)).

What I think happened is that #[derive(Clone)] expansion requires Cow<'static, [Self]>: Clone which requires Cow<'static, [Self]>: Sized which, before #122493, required wf(Cow<'static, [Self]>) which requires [Self]: ToOwned which requires Self: Clone which requires Cow<'static, [Self]>: Clone making a cycle.

I think some details in my explanation are wrong (particularly the last step?), because I'm not familiar enough with type checking in rustc; but. I do think that "not requiring wf(Enum) when evaluating Enum: Sized" makes it so we don't evaluate bounds which lead to a cycle.

@lukas-code
Copy link
Member

This was properly fixed in #122791, which also landed in 1.78.0.

That PR also allows this similar code to compile, which still fails after #122493:

#[derive(Clone)]
struct Hello {
    a: <[Hello] as ToOwned>::Owned,
}

This happens, because normalizing <Hello as ToOwned>::Owned in this example involves a cycle during candidate selection, which is no longer an error after #122791:

normalize <[Hello] as ToOwned>::Owned
    candidate #1: impl ToOwned for T where T: Sized [+ Clone]
        requires [Hello]: Sized
        -> error (unimplemented)
    candidate #2: impl ToOwned for [T] where T: Sized [+ Clone]
        requires Hello: Sized
            requires <[Hello] as ToOwned>::Owned: Sized
                normalize <[Hello] as ToOwned>::Owned
        -> ambiguous (cycle)
    -> candidate #2 is ambiguous, but it is the only applicable candidate
-> normalized to Vec<Hello> via candidate #2

The Code in the OP compiles with just #122493, because that PR changed Cow<T>: Sized to always hold and not require checking <T as ToOwned>::Owned: Sized and therefore avoid the cycle.

@marmeladema
Copy link
Contributor

I think this has solved #100347

@apiraino
Copy link
Contributor

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-medium

@rustbot rustbot added P-medium Medium priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Aug 26, 2024
@mbartlett21
Copy link
Contributor

Issues resolved include #23714, #47032, #89940, #100347 and #107481.

@lcnr
Copy link
Contributor

lcnr commented Aug 29, 2024

#129541 (comment) this uncovers a case where we forget ambiguity. Even if we're able to use an ambiguous candidate to normalize, this should force the goal to be ambiguous. This currently fails with the new solver and should do so until we've changed our cycle handling.

https://rust.godbolt.org/z/sYGY15Gv5

I don't fully understand why this happens and whether this hides a more serious issue. I want to look more deeply into the code once I am back home next week. Thanks @zachs18 for opening this issue and thanks @lukas-code for looking into this.

@lcnr lcnr added P-high High priority I-types-nominated Nominated for discussion during a types team meeting. and removed P-medium Medium priority labels Aug 29, 2024
@workingjubilee
Copy link
Member

workingjubilee commented Aug 29, 2024

I was actually hoping for unsized unions to remain possible, honestly, so I'm somewhat disappointed by this event being caused by #122493, since that makes it impossible to even explore adding such support without it then being a regression.

@lukas-code
Copy link
Member

I was actually hoping for unsized unions to remain possible, honestly, so I'm somewhat disappointed by this event being caused by #122493, since that makes it impossible to even explore adding such support without it then being a regression.

Before #122493, unions would only be unsized if the last field was unsized, e.g.

// This was unsized before.
union Foo {
    a: (),
    b: [u8],
}

// This was always sized.
union Bar {
    a: [u8],
    b: (),
}

That's probably not the semantics that we want for unsized unions and the same can already be achieved with a sized union and a struct. So it seemed fine to me to remove this weird special case.

I'm actually more concerned about the future of unsized enums now, because I thought we could just revert the relevant changes if they ever become a thing, but this issue shows that that's evidently not the case.

@workingjubilee
Copy link
Member

I agree that that rule for unions seems like the wrong semantics! To be clear, I was hoping for something like this becoming possible:

#[repr(C)]
union Varlena {
    pack: (u8, [u8]),
    full: (u32, [u8]),
}

Which would be quite useful for binding against C code with various kinds of "we have enums at home" solutions.

@saethlin saethlin removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Aug 31, 2024
@lcnr
Copy link
Contributor

lcnr commented Sep 3, 2024

#129541 (comment) this uncovers a case where we forget ambiguity. Even if we're able to use an ambiguous candidate to normalize, this should force the goal to be ambiguous. This currently fails with the new solver and should do so until we've changed our cycle handling.

https://rust.godbolt.org/z/sYGY15Gv5

I don't fully understand why this happens and whether this hides a more serious issue. I want to look more deeply into the code once I am back home next week. Thanks @zachs18 for opening this issue and thanks @lukas-code for looking into this.

ah, this is #106040 😅 that's good as this issue is already known and something we'll have to handle regardless.

@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 Sep 3, 2024
@lcnr
Copy link
Contributor

lcnr commented Sep 3, 2024

Okay, so this issue contains 2 unintended changes:

I think these changes in behavior are acceptable and desirable. They will be automatically supported once we've implemented the new cycle semantics. I believe we were already forced to handle #106040 in the new solver even before #122791 made it easier to exploit.

I propose that we accept this accidental stabilization (given that it's already stable since for 2 versions) and close this issue after adding regression tests.

@rfcbot rfcbot added to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Sep 15, 2024
@rfcbot
Copy link

rfcbot commented Sep 15, 2024

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@lcnr lcnr added E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. and removed P-high High priority labels Sep 17, 2024
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Sep 19, 2024
bors added a commit to rust-lang-ci/rust that referenced this issue Sep 20, 2024
…llaumeGomez

Rollup of 6 pull requests

Successful merges:

 - rust-lang#129542 (Add regression test for rust-lang#129541)
 - rust-lang#129755 (test: cross-edition metavar fragment specifiers)
 - rust-lang#130566 (Break up compiletest `runtest.rs` into smaller helper modules)
 - rust-lang#130585 (Add tidy check for rustdoc templates to ensure the whitespace characters are all stripped)
 - rust-lang#130605 (Fix feature name in test)
 - rust-lang#130607 ([Clippy] Remove final std paths for diagnostic item)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors closed this as completed in 5c60185 Sep 20, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 20, 2024
Rollup merge of rust-lang#129542 - zachs18:cow-self-test, r=compiler-errors

Add regression test for rust-lang#129541

(maybe?) closes rust-lang#129541 by adding a test that the code in question continues to compile.
@lcnr
Copy link
Contributor

lcnr commented Sep 21, 2024

please also add the 3 tests from #129541 (comment)

@Enselic
Copy link
Member

Enselic commented Nov 25, 2024

@lcnr I think you link to the wrong comment? I think you mean "tests from issues referenced from #129541 (comment)" ?

@lcnr
Copy link
Contributor

lcnr commented Nov 25, 2024

no, to close this issue I would like to also want the following tests from my FCP proposal to get added as regression tests:

@Enselic
Copy link
Member

Enselic commented Nov 25, 2024

PR that adds those tests: #133467
I'm a bit confused though because one of them still fails. If I did something wrong it's hopefully easy for you to point out what I should change.

@lcnr
Copy link
Contributor

lcnr commented Nov 25, 2024

I want to merge the failing test to also catch when its behavior changes, even if it goes from pass to fail. I want to make sure we realize that we accidentally stabilized something before it hits stable 😁

jhpratt added a commit to jhpratt/rust that referenced this issue Nov 26, 2024
tests: Add recursive associated type bound regression tests

Add regression tests for rust-lang#129541 as requested in rust-lang#129541 (comment).

Closes rust-lang#129541

r? `@lcnr`
@bors bors closed this as completed in c8c225f Nov 26, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Nov 26, 2024
Rollup merge of rust-lang#133467 - Enselic:recurse-tests, r=lcnr

tests: Add recursive associated type bound regression tests

Add regression tests for rust-lang#129541 as requested in rust-lang#129541 (comment).

Closes rust-lang#129541

r? ``@lcnr``
jieyouxu added a commit to jieyouxu/rust that referenced this issue Nov 30, 2024
tests: Add regression test for self referential structs with cow as last field

Making compilation pass for this code was retroactively stabilized via FCP in 1.79. The code does not compile in 1.78.

See rust-lang#129541 for details.

Closes rust-lang#107481
jieyouxu added a commit to jieyouxu/rust that referenced this issue Nov 30, 2024
tests: Add regression test for self referential structs with cow as last field

Making compilation pass for this code was retroactively stabilized via FCP in 1.79. The code does not compile in 1.78.

See rust-lang#129541 for details.

Closes rust-lang#107481
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Nov 30, 2024
Rollup merge of rust-lang#133488 - Enselic:recurse-2, r=BoxyUwU

tests: Add regression test for self referential structs with cow as last field

Making compilation pass for this code was retroactively stabilized via FCP in 1.79. The code does not compile in 1.78.

See rust-lang#129541 for details.

Closes rust-lang#107481
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. finished-final-comment-period The final comment period is finished for this PR / Issue. regression-untriaged Untriaged performance or correctness regression. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
16 participants