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

Tuples containing references no longer pass PartialEq bounds check for all lifetimes of the other reference #112895

Closed
smwoj opened this issue Jun 21, 2023 · 10 comments · Fixed by #113163
Labels
C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. P-medium Medium priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@smwoj
Copy link

smwoj commented Jun 21, 2023

Code

I tried this code:

trait T {
    type Key<'a>: for<'b> PartialEq<Self::Key<'b>>
    where
        Self: 'a;
}

impl T for () {
    type Key<'a> = (&'a str, u32);
}

It used to compile on 1.69, so I expected it to compile under 1.70, but it failed with the following error

error: implementation of `PartialEq` is not general enough
 --> src/lib.rs:8:20
  |
8 |     type Key<'a> = (&'a str, u32);
  |                    ^^^^^^^^^^^^^^ implementation of `PartialEq` is not general enough
  |
  = note: `(&'a str, u32)` must implement `PartialEq<(&'0 str, u32)>`, for any lifetime `'0`...
  = note: ...but it actually implements `PartialEq`

The same happens for structs that contain a reference and derive PartialEq.

(godbolt link)

Version it worked on

1.69

Version with regression

1.70

rustc --version --verbose:

rustc 1.70.0 (90c541806 2023-05-31)
binary: rustc
commit-hash: 90c541806f23a127002de5b4038be731ba1458ca
commit-date: 2023-05-31
host: x86_64-unknown-linux-gnu
release: 1.70.0
LLVM version: 16.0.2

@rustbot modify labels: +regression-from-stable-to-stable -regression-untriaged

@smwoj smwoj added C-bug Category: This is a bug. regression-untriaged Untriaged performance or correctness regression. labels Jun 21, 2023
@rustbot rustbot added I-prioritize Issue: Indicates that prioritization has been requested for this issue. regression-from-stable-to-stable Performance or correctness regression from one stable version to another. and removed regression-untriaged Untriaged performance or correctness regression. labels Jun 21, 2023
@compiler-errors
Copy link
Member

searched nightlies: from nightly-2023-03-01 to nightly-2023-06-21
regressed nightly: nightly-2023-03-29
searched commit range: 2036fdd...478cbb4
regressed commit: 478cbb4

bisected with cargo-bisect-rustc v0.6.5

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

cargo bisect-rustc --start=2023-03-01 

This probably regressed in #109470.

@compiler-errors
Copy link
Member

compiler-errors commented Jun 21, 2023

I don't know if this predicate should have held in the first place 🤔

fn test<'a>() where (&'a str, u32): for<'b> PartialEq<(&'b str, u32)> {}

fn main() {
    test();
}
error[E0277]: can't compare `(&str, u32)` with `(&'b str, u32)`
 --> <source>:4:5
  |
4 |     test();
  |     ^^^^ no implementation for `(&str, u32) == (&'b str, u32)`
  |
  = help: the trait `for<'b> PartialEq<(&'b str, u32)>` is not implemented for `(&str, u32)`

I think code demonstrates that (&str, u32): for<'b> PartialEq<(&'b str, u32)> doesn't hold -- the lifetimes have to be equal as a consequence of the PartialEq implementation for tuples

@compiler-errors
Copy link
Member

compiler-errors commented Jun 21, 2023

The previous behavior is unsound, demonstrated by this code which passed in 1.69 and fails in 1.70:

trait Lengthen<T> {
    fn lengthen(self) -> T;
}

impl<'a> Lengthen<&'a str> for &'a str {
    fn lengthen(self) -> &'a str { self }
}

trait Gat {
    type Gat<'a>: for<'b> Lengthen<Self::Gat<'b>>;

    fn lengthen(s: Self::Gat<'_>) -> Self::Gat<'static> {
        s.lengthen()
    }
}

impl Gat for () {
    type Gat<'a> = &'a str;
}

fn main() {
    let s = "hello, garbage".to_string();
    let borrow: &'static str = <() as Gat>::lengthen(&s);
    drop(s);

    println!("{borrow}");
}

@BoxyUwU
Copy link
Member

BoxyUwU commented Jun 21, 2023

fwiw this could possible pass if the PartialEq impl for tuples in std was impl<T, U> PartialEq<(T,)> for (U,) where U: PartialEq<T> { instead of impl<T: PartialEq> PartialEq for (T,) {. But that won't fix the general cause of breakage which is that code was being accepted without trait bounds holding which is unsound, and now those bounds do have to hold which cannot be fixed for all breakages.

@lcnr
Copy link
Contributor

lcnr commented Jun 27, 2023

I think there are two actionable steps there:

@compiler-errors
Copy link
Member

compiler-errors commented Jun 27, 2023

(1.) is not feasible, I think. See the crater results for #112899

But yeah, we should add a test for the unsoundness I shared above.

@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 Jun 28, 2023
@apiraino apiraino added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 28, 2023
@workingjubilee
Copy link
Member

Breaking code via breaking inference is, strictly speaking, a permitted breakage.
Of course, breaking unsound code is, in a greater sense, also permitted breakage.

@workingjubilee workingjubilee added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Jun 29, 2023
@smwoj
Copy link
Author

smwoj commented Jul 4, 2023

I think there are two actionable steps there:

to fix this concrete breakage, and because it is strictly more powerful, we can try to change the PartialEq impls in core to impl<T, U> PartialEq<(T,)> for (U,) where U: PartialEq as stated by @BoxyUwU. this may worsen inference but feels generally worthwhile to me.
(...)

(1.) is not feasible, I think. See the crater results for #112899

Is there a way to measure the impact of that crater run on the ecosystemt? Breakage in serde or chrono would be surely a more serious problem than a breakage in a toy application and with untrained eye I can't tell if the breakage is disastrous or just a little beyond acceptable.

Improving the PartialEq implementation for tuples strikes me as a "right thing to do" for the standard library. If we can't have it now, would it be possible to change it in a new edition? It would be sad if we were stuck forever with the current implementation when a strictly more powerful one exists.

@workingjubilee
Copy link
Member

The stdlib's implementations are largely unrelated to the edition mechanism: the editions mostly introduce syntactic changes, not library changes, with the one blurriness being with macros which interact with syntax.

What really needs to improve is the algorithm for deducing types, then it would be easier for the stdlib to carry more powerful implementations without fear of breaking inference.

JohnTitor added a commit to JohnTitor/rust that referenced this issue Jul 5, 2023
Signed-off-by: Yuki Okushi <jtitor@2k36.org>
bors added a commit to rust-lang-ci/rust that referenced this issue Jul 6, 2023
…fee1-dead

Rollup of 9 pull requests

Successful merges:

 - rust-lang#111119 (style-guide: Add chapter about formatting for nightly-only syntax)
 - rust-lang#112791 (llvm ffi: Expose `CallInst->setTailCallKind`)
 - rust-lang#113145 (style-guide: Document newline rules for assignment operators)
 - rust-lang#113163 (Add a regression test for rust-lang#112895)
 - rust-lang#113332 (resolve: Use `Interned` for some interned structures)
 - rust-lang#113334 (Revert the lexing of `c"…"` string literals)
 - rust-lang#113350 (Fix the issue of wrong diagnosis for extern pub fn)
 - rust-lang#113371 (Fix submodule handling when the current branch is named after a tag)
 - rust-lang#113384 (style-guide: Clarify grammar for small patterns (not a semantic change))

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors closed this as completed in a105aa2 Jul 6, 2023
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. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. P-medium Medium priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants