Closed RustyYato closed 1 year ago
This works on stable and beta (as in, it compiles without error if you add a : Sized
supertrait to Access
) , but not on nightly.
@rustbot label regression-from-stable-to-nightly
searched nightlies: from nightly-2020-12-25 to nightly-2021-01-20 regressed nightly: nightly-2021-01-18 searched commits: from https://github.com/rust-lang/rust/commit/8a6518427e11e6dd13d6f39663b82eb4f810ca05 to https://github.com/rust-lang/rust/commit/4253153db205251f72ea4493687a31e04a2a8ca0 regressed commit: https://github.com/rust-lang/rust/commit/edeb631ad0cd6fdf31e2e31ec90e105d1768be28
I think part of the problem is that f.field(...)
looks for methods on &DebugTuple
and sees the <DebugTuple as Access>::field(&self)
before it looks for methods on &mut DebugTuple
and finds DebugTuple::field(&mut self, ...)
. This issue with autoref/autoderef interacting poorly with inherent method's precedence over trait methods has cropped up before, e.g. in #39232 (where the compiler finds <T as BorrowMut>::borrow_mut(self)
before trying autoref and finding RefCell::borrow_mut(&self)
). But I don't have an explanation for why this started being a problem recently.
On another note, I notice that #[derive(Debug)]
appears to be the only built-in derive that ever uses .
method calls instead of UFCS with fully qualified paths, is there a reason for that? This issue could be fixed by changing #[derive(Debug)]
to not use .
calls, but I'm concerned that the root cause might have broken something else.
Looking at the rollup that this regressed in, #80765 seems like it might be the cause.
cc @petrochenkov
Minimized:
#[derive(Debug)]
struct Foo(u8);
pub trait Access {
fn field(&self) {}
}
impl<T> Access for T {}
derive(Debug)
should certainly use UFCS instead of method calls, regardless of https://github.com/rust-lang/rust/pull/80765.
If https://github.com/rust-lang/rust/pull/80765 causes other issues we'll find out sooner or later, in theory it shouldn't change any behavior unless macros 2.0 (which are unstable) or built-in macros (which we can fix) are involved.
(I'll leave the fix itself to someone else, unassigning myself.)
Assigning P-high
as discussed as part of the Prioritization Working Group procedure and removing I-prioritize
.
(Regarding the assignments here: i am going to make derive(Debug)
use UFCS. @Mark-Simulacrum is going to investigate if there is other fallout from PR #80765, I think via a directed crater run.)
I have something put together that seems to fix the problem for the cases listed in this bug, namely field
, and presumably finish
though I need to double-check that one.
However, I also tried applying my solution to the debug_struct
and debug_tuple
calls that we make on the Formatter
argument, and in those cases, the UFCS code I generated did not compile. I'm still working that out (and also working out whether the injection of an alternative field
method illustrated in the description of this bug #81211 even applies to the debug_struct
and debug_tuple
methods as well).
Okay, so it seems to me like there's some oddity in how we resolve method calls that had left us somewhat robust against injections of new methods on fmt::Formatter
and fmt::DebugTuple
, etc, all this time, and that #81211 removed some of that accidental robustness, but not all of it.
Here is the result of my experiments in the space (play):
As I note in the comments in the code, I'm a little confused about some of the deviations I'm seeing on stable/beta. It certainly seems like nightly is behaving more consistently overall, and has just exposed a latent weakness in the derive(Debug)
expansion.
I think the resolution is still generally consistent with the rules given in https://doc.rust-lang.org/reference/expressions/method-call-expr.html. I am not 100% sure of that claim because I would have thought an &self
method would still take precedence over an &mut self
method if resolve had to go through the same number of derefs to reach either one (and I think we are going through the same number of derefs in both paths here...?), but the fact that adding &mut
borrows side-steps the method collision from the trait Access
shows that either my thinking is wrong, or there is another bug lying in wait here.
&self
method when you need to on a value of type &mut Foo
: just deref and autoref back. But I think that is interpreted as a longer path than just passing the &mut Foo
directly to a &mut self
method if one is present, and thus that is probably when a &mut self
method can take precedence over a &self
method in a parallel trait. Still: Tricky stuff.@craterbot run start=master#7d3818152d8ab5649d2e5cc6d7851ed7c03055fe end=master#edeb631ad0cd6fdf31e2e31ec90e105d1768be28 mode=check-only
This should hopefully give us some sense of the breakage caused by the rollup here, hopefully the majority of which is down to #80765.
:ok_hand: Experiment pr-81211
created and queued.
:mag: You can check out the queue and this experiment's details.
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
@pnkfelix -- one thing that this makes me think would be an excellent outcome is to eventually write up, perhaps on that reference page, an explicit hierarchy of what is checked/reached first, at least for 2-3 autoref/deref steps or so.
@pnkfelix -- one thing that this makes me think would be an excellent outcome is to eventually write up, perhaps on that reference page, an explicit hierarchy of what is checked/reached first, at least for 2-3 autoref/deref steps or so.
Hey @Mark-Simulacrum, do you mean like the test that I wrote in PR #81294? Is that an example of the kind of thing you think should be added, in some fashion, to the documentation?
Yeah, perhaps as something like https://en.wikipedia.org/wiki/Partially_ordered_set#/media/File:Hasse_diagram_of_powerset_of_3.svg but I think not a Hasse diagram - basically, showing which is considered first and so forth. I guess we'd want multiple such graphs for different types of calls (e.g., if you call via method whether original type is &T, &mut T, T, or impl Deref<target= T>
, &impl Deref<target= T>
, &mut impl Deref<target= T>
etc)
:construction: Experiment pr-81211
is now running
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
:rotating_light: Experiment pr-81211
has encountered an error: some threads returned an error
:hammer_and_wrench: If the error is fixed use the retry
command.
:sos: Can someone from the infra team check in on this? @rust-lang/infra :information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
Sorry about that. A new crater deployment broke a thing. Should be fixed now.
@craterbot retry p=2
:rotating_light: Error: failed to parse the command
:sos: If you have any trouble with Crater please ping @rust-lang/infra
!
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
@craterbot retry @craterbot p=3
:hammer_and_wrench: Experiment pr-81211
queued again.
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
@craterbot p=2
:memo: Configuration of the pr-81211
experiment changed.
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
:construction: Experiment pr-81211
is now running
:information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
:tada: Experiment pr-81211
is completed!
:bar_chart: 18 regressed and 13 fixed (142244 total)
:newspaper: Open the full report.
:warning: If you notice any spurious failure please add them to the blacklist! :information_source: Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more
No one ended up triaging this crater run, but I think the only possible additional breakage is in hlist - https://crater-reports.s3.amazonaws.com/pr-81211/master%23edeb631ad0cd6fdf31e2e31ec90e105d1768be28/reg/typsy-0.1.0/log.txt - and I'd guess the fix would be similar there. Ultimately this is 'just' a form of inference breakage in some sense, so I think it's OK to let this slip. It seems to affect only 2 crates in the crater run, so breakage is very minor.
This is fixed on current nightly. Removing priority and marking as E-needs-test.
I actually added tests, I think, in #81294. Namely:
From reviewing the comment thread here, I know there were lots of various concerns noted, and ideas for enhancements to the language documentation (I've filed https://github.com/rust-lang/reference/issues/1308 for the latter). But I think the issue described here is resolved and has corresponding tests.
I tried this code:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5ce8c7b1f8fdb39762765a7bf1576cfb
I expected it to compile, but it doesn't because the
Debug
macro expands to something likefoo.field("field_name", &value)
, which incorrectly resolves toAccess::field
instead ofDebugTuple::field
(which is an inherent method).error message
```rust error[E0061]: this function takes 0 arguments but 1 argument was supplied --> src/lib.rs:1:10 | 1 | #[derive(Debug)] | ^^^^^ expected 0 arguments 2 | pub struct Foo