-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Clone
suggestions
#95115
Clone
suggestions
#95115
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.
help: consider restricting type parameter `T` | ||
| | ||
LL | fn move_clone_only<T: Clone>(t: (T, String)) { | ||
| +++++++ | ||
help: ...and cloning `t` | ||
| | ||
LL | [t.clone(), t]; | ||
| ++++++++ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:16 while 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:10 The 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 👀 |
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 itCopy
Clone
, suggest adding bounds requited to make itClone
and 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.rs
test for examples of broken suggestions that I could find.r? @estebank
@rustbot label +A-diagnostics +A-suggestion-diagnostics +C-enhancement