rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.6k stars 12.74k forks source link

Tracking issue for RFC 1940: allow `#[must_use]` on functions #43302

Closed aturon closed 6 years ago

aturon commented 7 years ago

This is a tracking issue for the RFC "allow #[must_use] on functions" (rust-lang/rfcs#1940).

Steps:

zackmdavis commented 7 years ago

I think I should have an implementation shortly.

crumblingstatue commented 7 years ago

I'm just adding a note here that the current implementation of #1940 does not lint for unused ==, which was the main motivation for it.

In fact, the RFC itself states in the motivation

By marking PartialEq #[must_use] the compiler would complain about things like:

   modem_reset_flag == false; //warning
   modem_reset_flag = false; //ok
zackmdavis commented 7 years ago

Yeah, that's a little awkward. I can add the comparison operators in a follow-on PR.

eddyb commented 7 years ago

Also, this issue shouldn't have been closed. Ahhh I may have not checked for a feature gate in the implementation. I'm a bad reviewer.

zackmdavis commented 7 years ago

I have an implementation for linting == and the other comparison operators. (It's based off the feature-gating work, so we'll let that land first.)

nikomatsakis commented 7 years ago

Hi @zackmdavis / @eddyb -- so I've not had time to check on the PRs, but I wanted to make a request. In preparation for the decision to stabilize, would it be possible for you to collect the various tests into a subdirectory (e.g., src/test/ui/rfc_1940_must_use_on_functions/) for easier review? I've found that when deciding whether or not to stabilize, being able to quickly survey how the compiler currently behaves on a bunch of examples is the best way to judge the current state of the feature (and, of course, the number of tests can also give you an idea of how well vetted the implementation is). If tests have already been collected, my apologies!

Either way, can you add a link somewhere (I can copy it into the header of the issue).

Thanks. ❤️

nikomatsakis commented 7 years ago

How are people feeling about this? Shall we consider stabilization? Are we using this for the various iterator methods and other purposes that it was envisioned for?

kennytm commented 7 years ago

It is currently applied on methods in PartialOrd and PartialEq only :S

nikomatsakis commented 7 years ago

@kennytm was there a plan to use it elsewhere? I forget. If so, is there any reason we are not doing so?

kennytm commented 7 years ago

@nikomatsakis There was a plan to add #[must_use] to Result::ok rust-lang/rfcs#886.

rust-lang/rfcs#1940 only suggests applying to eq() and friends (which turns out isn't enough to fulfill the RFC's motivation).

I don't think there were any plans to use it on iterator methods coming from these two RFCs. IIRC there's some discussion to put #[must_use] on collect() to avoid newbies using it to consume the whole iterator.

Ixrec commented 7 years ago

I definitely wouldn't stabilize this if it's not working for the == operator yet, but https://github.com/rust-lang/rust/pull/44103 claims to make it work for == and the playground agrees that it's working, so the RFC's primary motivation seems fulfilled. I don't think we need to block stabilization on deciding what iterator methods should also have it.

scottmcm commented 6 years ago

It looks like linting of { x == y; } is also feature gated right now. Can we at least stabilize that?

oli-obk commented 6 years ago

ping

this seems like it's ready to get stabilized

zackmdavis commented 6 years ago

Yes, this feature is very straightforward; the only thing I'd want to consider before stabilization is whether we should add #[must_use] to any other standard library methods (like .collect()).

(And even then, the only scenarios in which that makes a difference are ones in which we decide to make more functions must-use without stabilizing, and then nightly users point out that we were wrong to do so, but the instability lets us revert without breaking any deny-warnings stable code.)

Give me a few days to find some time to look through the standard library for any other candidate must-use functions, and I'll report back with comments and/or a stabilization PR for 1.25? (Looks like we have until 15 February before the next train leaves.)

zackmdavis commented 6 years ago

(Of course, it would also be possible to immediately stabilize just the often-demanded operator lints—which are only under the fn_must_use feature gate due to the historical contingency of RFC discussion not sufficiently forseeing implementation details—but continue to evaluate must-use for functions. However, I agree with the point that came up at least a couple times in the Rust2018 discussions that we're sitting on too many unshipped features: ungating all the fn_must_use functionality in 1.25 seems reasonable.)

zackmdavis commented 6 years ago

There's a perspective that says everything in the standard library that has no side effects that doesn't already return a must-use type (like Result) should be must-use. But that feels like a weirdly excessive number of attributes (at least dozens, likely hundreds), right?

scottmcm commented 6 years ago

As it's a lint, I'm in favour of just stabilizing and worrying about which functions get it later.

That said, I'd also like to put this on things like clone. And maybe things like to_string, since I saw someone on IRC do x.to_string(); and wonder why x wasn't now a String...

nikomatsakis commented 6 years ago

I agree we should stabilize. I have no strong opinion about which functions get the lint but I feel like it's ok if it's a lot, if that is useful. =)

@zackmdavis would you be up for preparing a stabilization report? Basically, include a brief summary of what the feature does, and what tests exist for each part of that (so we can verify that it is working correctly). Special attention to edge cases that came up during implementation and how they are being tested or what was decided about them (particularly if it deviates from or was not mentioned in the RFC).

The audience would be people who have been following along, so it doesn't have to explain the feature from first principles -- i.e., no need to delve into motivation per se. (Though a bit more never hurts, e.g. for people coming in from This Week In Rust)

zackmdavis commented 6 years ago

(I regret the delay; I'm penciling in some time Saturday to put together a stabilization PR.)

Havvy commented 6 years ago

As it's been exactly four months, what's the status on this?

zackmdavis commented 6 years ago

The status is that I haven't forgotten that I said I was going to put together a stabilization PR; I'm just terrible at managing my time. :cry: (And writing new code is more fun than stabilizing existing code.)

The psychological pressure induced by having been caught breaking my word is probably enough to get me to do it tonight (Pacific time).

zackmdavis commented 6 years ago

So, this is unexpected—and really the sort of thing that we would hope to find out before trying to put together the stabilization PR—but we have a serious bug. Building the getopts crate with the fn-must-use code (either after ripping out the feature gate, or with the feature attribute inserted) panics (!) when qpath_def gives us a Def::Local for this line and we try to take its DefId (which isn't a real thing). I haven't found a comprehensibly reduced test case yet.

zackmdavis commented 6 years ago

Minimized:

#![feature(fn_must_use)]

fn main() {
    let c = || { true };
    c();
}

yields

error: internal compiler error: librustc/hir/def.rs:178: attempted .def_id() on invalid def: Local(NodeId(8))

Sadly, this does make me wonder how effective the "let nightly users find bugs in unstable features" strategy is, if so few people have kicked the tires on #[feature(fn_must_use)] that no one noticed that it (not even using the functionality, just enabling the flag!) breaks programs containing closures that return a non-unit value.

zackmdavis commented 6 years ago

stabilization PR is #48925; separate issue #48926 created for the question of #[must_use] elsewhere in the standard library

nikomatsakis commented 6 years ago

@zackmdavis do you think you can prepare a "stabilization issue" (or just re-use this issue, if there are no further "pieces" to consider stabilizing). It should look something like this:

https://github.com/rust-lang/rust/issues/48453

That is, summarize briefly what is going on, give a few example tests showing the feature in use in various representative ways (and also highlight any changes that have happened since the RFC). Then I can move to FCP.

zackmdavis commented 6 years ago

@nikomatsakis

It it proposed that we stabilize the code currently under the fn_must_use feature gate.

This commits us to the following behaviors:

Changes from the authorizing RFC 1940:

Status changes since implementation (#43728, #43776, #44103):

Documentation status:

Unresolved questions and issues (which need not necessarily block stabilization):

warning: unused return value of `foo` which must be used: further info
  --> $DIR/fn_must_use.rs:67:5
   |
LL |     foo();
   |     ^^^^^^

vs.

warning: unused return value of `foo` which must be used
  --> $DIR/fn_must_use.rs:67:5
   |
LL |     foo();
   |     ^^^^^^
   |
note: further info
SimonSapin commented 6 years ago

Should the compiler warn when #[must_use] appears on a trait implementation, rather than the declaration? (This is currently a no-op, but users might expect it to work and be confused when it doesn't.)

Can we make this attribute a compilation error when used in places where it would be a no-op? This leaves the option of making it work later without changing the meaning of programs that compile.

nikomatsakis commented 6 years ago

@rfcbot fcp merge

I propose that we stabilize must-use lints. @zackmdavis has prepared an excellent summary here.

However, I am also in favor of @SimonSapin's change to report an error if #[must_use] is attached to a method in a trait impl (rather than having that be a no-op), and I'd prefer not to stabilize until we agree to that change as well (though this could be added to the stabilization PR).

rfcbot commented 6 years ago

Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

golddranks commented 6 years ago

Is there any issue that tracks which std APIs are going to have this annotation? I just shot myself into foot with Write::write() – I checked the Result with ?, but ignored the amount of the written bytes it returned. I should have used write_all(); if it warned me about ignoring the return value, I would have noticed it and it would have saved me from a serious bug.

Another thought: is the annotation going to work with APIs that return Result, but where even the happy path value needs not to be ignored? (I.e. write() here) I would imagine that it would be quite possible to do this with calls like write()?, but the general case with all kinds of combinators before returning the error is much much harder.

oli-obk commented 6 years ago

Clippy has a correctness lint for that: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unused_io_amount

I don't think it can be caught by must_use, but a targeted lint can.

zackmdavis commented 6 years ago

Is there any issue that tracks which std APIs are going to have this annotation?

Please, contribute your thoughts on #48926.

rfcbot commented 6 years ago

:bell: This is now entering its final comment period, as per the review above. :bell:

rfcbot commented 6 years ago

The final comment period is now complete.

TimNN commented 6 years ago

@nikomatsakis: It looks like it was decided to stabilize this, can I assume that #48925 is now un-blocked?

nikomatsakis commented 6 years ago

@TimNN yep

zackmdavis commented 6 years ago

@SimonSapin @nikomatsakis

Can we make this attribute a compilation error when used in places where it would be a no-op? This leaves the option of making it work later without changing the meaning of programs that compile.

I'm confused about how exactly how literally we take our stability guarantee. #[must_use] on trait-impls (and other non-ADT locations) is already a no-op on stable; are we allowed to break programs with such attributes by making it an error?

This seems like it ought to be sheerly hypothetical—why would anyone have already written a program with such no-op attributes? But the fn_must_use feature gate ended up emitting a warning rather an error (which took some extra work) for precisely this reason, which warnings turned out to reveal at least one crate in the wild that was trying to use the #[must_use] attributes even though they were no-oping.

SimonSapin commented 6 years ago

#[must_use] on trait-impls (and other non-ADT locations) is already a no-op on stable

This is the situation I was hoping to prevent, I didn’t realize it was already the case.

Havvy commented 6 years ago

We can make it a hard error on the next edition then.

Havvy commented 6 years ago

@aturon The stabilization PR has already gone through. I'm not sure if all the necessary docs are updated yet, although the reference has been.

Centril commented 6 years ago

@Havvy is there anything left here to do?

Havvy commented 6 years ago

I don't know. It's documented in the reference and there's an issue for the API guidelines book. But I don't think it's talked about in TRPL and definitely untouched in Rust By Example, if it should even be in there.

Centril commented 6 years ago

Closing as completed.