Conversation
This is continuation on the "copy suggestions". The logic is as follows:
- If the type is already `Clone` - clone it
- If it can be `Copy` - make it copy
- If it can be `Clone` - make it clone and the clone it
- Otherwise feel sad
Note that clone suggestions may be incorrect in many cases, like for
example `S{t}; t` that suggests `S{t.clone()}; t`.
See `use_of_moved_value_clone_suggestions_bad.rs` for more problematic
cases.
There was a problem hiding this comment.
To be clear: these suggestions could be merged together, it just requires some work (we need the suggest_constraining_type_params to allow adding to the same suggestion/changing the text/etc).
This comment has been minimized.
This comment has been minimized.
4084e1b to
4c5de71
Compare
|
The job Click to see the possible cause of the failure (guessed by this bot) |
| help: consider cloning `x.f` | ||
| | | ||
| LL | Foo {f.clone()} => {} | ||
| | ++++++++ |
There was a problem hiding this comment.
This seems to be an incorrect suggestion. We'll need to check that we're not in a pattern, we can only call function in expressions.
There was a problem hiding this comment.
Yes, but I couldn't figure a way to check :')
| }) | ||
| .collect() | ||
| }); | ||
| let copy_did = tcx.lang_items().copy_trait().unwrap(); |
There was a problem hiding this comment.
There's a method, I think called expect_lang_item or similar (don't have the editor open right now to find it) that does that unwrap for you in a non-ICEy way.
|
☔ The latest upstream changes (presumably #95257) made this pull request unmergeable. Please resolve the merge conflicts. |
|
I still need to look at how we can avoid suggesting this in patterns. |
|
I would like to see this land, but don't have enough knowledge of the mir to help here. @JakobDegen I see that you have worked in |
|
As far as I know, MIR currently does not contain enough information to make this distinction. I took a look at the let F { y } = x;The span of the relevant statement is still the pattern and we get the following relevant MIR FakeRead(ForLet(None), _1); // scope 0 at test.rs:6:9: 6:10
StorageDead(_4); // scope 0 at test.rs:6:38: 6:39
StorageLive(_5); // scope 1 at test.rs:8:15: 8:16
_5 = move (_1.0: std::string::String); // scope 1 at test.rs:8:15: 8:16while this version let y = x.f;has the span of the statement on the expression and produces: FakeRead(ForLet(None), _1); // scope 0 at test.rs:6:9: 6:10
StorageDead(_4); // scope 0 at test.rs:6:38: 6:39
StorageLive(_5); // scope 1 at test.rs:8:9: 8:10
_5 = move (_1.0: std::string::String); // scope 1 at test.rs:8:13: 8:16
FakeRead(ForLet(None), _5); // scope 1 at test.rs:8:9: 8:10The There are a number of places in MIR where we already store additional info for the purpose of diagnostics. Possibly this could be done for this scenario too, but I don't quite see a nice way of doing it. In any case, I am also somewhat out of my depth here. cc @oli-obk who might know how to go about this. |
|
I don't know of a good solution, but you can try dumping the |
|
I thought about this more, and adding things to the local decls might be a bit of a challenge. The problem is destructuring assignment: struct Foo {
f: i32,
}
fn touch() {}
pub fn f() {
let x = Foo { f: 10 };
let mut y = 5;
touch(); // Just want to separate basic blocks
y = x.f;
touch();
Foo { f: y } = x;
touch();
}For the regular assignment: bb1: {
StorageDead(_3); // scope 2 at test.rs:11:12: 11:13
StorageLive(_4); // scope 2 at test.rs:13:9: 13:12
_4 = (_1.0: i32); // scope 2 at test.rs:13:9: 13:12
_2 = move _4; // scope 2 at test.rs:13:5: 13:12
StorageDead(_4); // scope 2 at test.rs:13:11: 13:12
StorageLive(_5); // scope 2 at test.rs:15:5: 15:12
_5 = touch() -> [return: bb2, unwind: bb4]; // scope 2 at test.rs:15:5: 15:12
// mir::Constant
// + span: test.rs:15:5: 15:10
// + literal: Const { ty: fn() {touch}, val: Value(Scalar(<ZST>)) }
}And for the destructuring assignment: bb2: {
StorageDead(_5); // scope 2 at test.rs:15:12: 15:13
StorageLive(_6); // scope 2 at test.rs:17:5: 17:21
StorageLive(_7); // scope 2 at test.rs:17:14: 17:15
_7 = (_1.0: i32); // scope 2 at test.rs:17:14: 17:15
StorageLive(_8); // scope 3 at test.rs:17:14: 17:15
_8 = _7; // scope 3 at test.rs:17:14: 17:15
_2 = move _8; // scope 3 at test.rs:17:14: 17:15
StorageDead(_8); // scope 3 at test.rs:17:14: 17:15
_6 = const (); // scope 2 at test.rs:17:5: 17:21
StorageDead(_7); // scope 2 at test.rs:17:20: 17:21
StorageDead(_6); // scope 2 at test.rs:17:21: 17:22
StorageLive(_9); // scope 2 at test.rs:19:5: 19:12
_9 = touch() -> [return: bb3, unwind: bb4]; // scope 2 at test.rs:19:5: 19:12
// mir::Constant
// + span: test.rs:19:5: 19:10
// + literal: Const { ty: fn() {touch}, val: Value(Scalar(<ZST>)) }
}The only real difference is the extra temporary, and that doesn't seem reliable. Possibly we could add some information to the local decl of the temporary though? |
Can we prototype that and see if that would affect perf in any way? |
|
Triage: @rustbot author |
|
@WaffleLapkin @rustbot label: +S-inactive |
|
For what is worth, I really want to see this land eventually, but for that we need to figure out how to avoid the invalid suggestions when involving patterns. |
|
@compiler-errors proposed a possible solution recently, I'm currently investigating if it works 👀 |
|
For posterity sake: this seems to have been implemented in #103908 |
This continues the work from #94375 by also suggestion cloning sometimes, instead of coping. The rules are as follows:
Clone, suggest adding.clone()callsCopy, suggest adding bounds requited to make itCopyClone, suggest adding bounds requited to make itCloneand suggest adding.clone()callsFor example for this code:
we now produce the following diagnostics:
Which is quite nice in my opinion!
However, there are some corner-cases where the suggestion is wrong and I don't know how to fix it. See
use_of_moved_value_clone_suggestions_bad.rstest for examples of broken suggestions that I could find.r? @estebank
@rustbot label +A-diagnostics +A-suggestion-diagnostics +C-enhancement