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

Tracking Issue for must_not_suspend lint (RFC #3014) #83310

Open
1 of 4 tasks
Tracked by #84 ...
nikomatsakis opened this issue Mar 19, 2021 · 23 comments
Open
1 of 4 tasks
Tracked by #84 ...

Tracking Issue for must_not_suspend lint (RFC #3014) #83310

nikomatsakis opened this issue Mar 19, 2021 · 23 comments
Assignees
Labels
A-async-await Area: Async & Await A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC

Comments

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Mar 19, 2021

This is a tracking issue for the RFC 3014 (rust-lang/rfcs#3014).
The feature gate for the issue is #![feature(must_not_await_lint)].

Status: This is waiting on more precise generator captures to reduce false positives before stabilizing and turning the lint on by default.

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Steps

Unresolved Questions

Implementation history

@nikomatsakis nikomatsakis added B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC labels Mar 19, 2021
@jonas-schievink jonas-schievink added A-async-await Area: Async & Await A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. labels Mar 19, 2021
@bIgBV
Copy link

bIgBV commented Mar 22, 2021

I am fairly busy right now, but I can pick this up in a few weeks' time. While researching the lint I dug into what changes would be required to the compiler and I'm fairly certain I have those written down somewhere. I would definitely appreciate any other pointers though.

@nikomatsakis nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Mar 26, 2021
@bIgBV
Copy link

bIgBV commented Mar 26, 2021

@rustbot claim

@bIgBV
Copy link

bIgBV commented Apr 29, 2021

@nikomatsakis @tmandry I've begun working on this implementation, but I would really appreciate if you or someone you can point to can drop some mentoring instructions to help me get started. The general idea I have so far is this:

  1. Create a new TypeFlag for tracking if a type has the #[must_not_suspend] attribute present on it
  2. During type construction (when the interned Ty is constructed?) check for the presence of this flag, and apply it any compound types (structs, tuples and enums. Am I missing anything?)
  3. During the computation of the types a generator captures, check whether the type has the new TypeFlag and emit a warning.
    • Create a new lint for this purpose.

Am I missing something? Right now my main questions are where the a type is constructed and where the propagation of the flag would fit in.

@bIgBV
Copy link

bIgBV commented Apr 29, 2021

As a note, I think made an implicit assumption of the propagation behavior in my previous comment. I think we should propagate the lint to nested types. I would like to do the work to apply this to the #[must_use] lint as well.

@bIgBV
Copy link

bIgBV commented Apr 29, 2021

The other approach I was thinking of was provide a new Query which answers the presence of a flag or the presence of an attribute on a type. I think this approach would allow us to apply this behavior to the #[must_use] attribute as well.

@nikomatsakis
Copy link
Contributor Author

@bIgBV

Happy to help! Here are some bits of code to investigate to get started.

First, you might want to look a bit at the must_use lint, although I think that this lint wants to be implemented somewhat differently. (I wouldn't use a lint pass, for example.) That said, the logic that must_use uses to decide if a type is MustUse is probably roughly what we want. That code doesn't use a TypeFlags. I'd avoid those, they seem a bit like overkill for your purposes, but instead has a function that, given a Ty<'tcx>, checks whether the type is "must-use":

https://github.com/rust-lang/rust/blob/4dc3316557522277db403954286a3f55c6ac8d16/compiler/rustc_lint/src/unused.rs#L173-L181

Generators already have some logic to figure out what types outlive an await. We also have some logic to track why the type appears in that list, which we use for giving better error messages.

Here is the code that adds a type into that set and tracks various spans associated with it:

https://github.com/rust-lang/rust/blob/4dc3316557522277db403954286a3f55c6ac8d16/compiler/rustc_typeck/src/check/generator_interior.rs#L108-L114

I think we could issue this lint exactly at this point -- we could basically analyze that type and determine whether it is "must not suspend". If so, we could issue the lint. Issuing a lint is done with some code roughly like this (from the check_unused lint):

https://github.com/rust-lang/rust/blob/4dc3316557522277db403954286a3f55c6ac8d16/compiler/rustc_typeck/src/check_unused.rs#L128-L144

@bIgBV
Copy link

bIgBV commented Apr 29, 2021

@nikomatsakis thanks for the pointers! My initial plan was actually to follow the implementation of the must_use lint, but @tmandry suggested to go down the TypeFlags route if we wanted to propagate the attribute to nested types as it would be more performant.

Also, does my suggestion of using a query make sense? I was coming it at from a forward looking perspective, as there seem to be many other must_* lints being suggested (must_not_drop, may_blockcome to mind) and a query for checking for the presence of such an attribute on a type might come in handy (from my extremely rudimentary understanding of the new compiler architecture).

@Aaron1011
Copy link
Member

Bikeshed - using must in the attribute name seems too strong for something that triggers an #[allow]-able lint. I think something like #[should_not_suspend] would better reflect the fact there may be legitimate reasons to suspend with such a type in scope, and than the annotated type itself can not assume that consumers will never suspend.

While there is precedent in the form of #[must_use], I think that 'use' has a broader meaning than 'suspend', so it's less confusing to see a type 'used' via let _ = create_must_use(), or #[allow]-ed.

@nikomatsakis
Copy link
Contributor Author

@bIgBV well, so type-flags can help to propagate through the types that are "visible". So for example Vec<Foo> would combine the type flags of Vec and Foo. But if you are going to look at the felds defined in Vec, TypeFlags isn't really what you want. This kind of thing is always a touch tricky to do.

I guess there's a question of which part to work on first. I personally would work on the code to create the lint first and have it fire in simpler cases, then come back and experiment with the best ways for it to fire.

I have to go re-read the RFC: i still feel pretty strongly that we ought to make #[must_use] and this lint work in the same way, whatever that is. I think i'm being slowly convinced about a more "nested" approach but I'm not sure.

@bIgBV
Copy link

bIgBV commented May 4, 2021

@nikomatsakis ah, that's fair. And yes, I agree, I think we need to solve the problem of propagating attributes for both this and the #[must_use] attributes. I've begun working on a basic implementation based on the notes that you've provided, but I'd love to come up with ideas on how to unify the behavior for both these lints, but also for the future ones. I think a syntax like #[must(not_suspend, use, not_block)] might be something worth exploring.

@nikomatsakis
Copy link
Contributor Author

@bIgBV oh, interesting thought

@guswynn
Copy link
Contributor

guswynn commented Sep 2, 2021

@bIgBV let me know if you are still working on this! I may start looking at it this weekend

@Kobzol
Copy link
Contributor

Kobzol commented Sep 16, 2021

Just wondering, will this attribute be added to selected std items like Ref (corresponding to the await_holding_refcell_ref clippy lint), or will it be left up to users to use newtypes with the attribute on top of these types to enable the warning? (sorry if this is the wrong place to ask)

@estebank
Copy link
Contributor

@Kobzol, appropriate std items will be annotated with this attribute, likely on individual PRs independent of the lint's implementation.

bors added a commit to rust-lang-ci/rust that referenced this issue Sep 22, 2021
Implement `#[must_not_suspend]`

implements rust-lang#83310

Some notes on the impl:

1. The code that searches for the attribute on the ADT is basically copied from the `must_use` lint. It's not shared, as the logic did diverge
2. The RFC does specify that the attribute can be placed on fn's (and fn-like objects), like `must_use`. I think this is a direct copy from the `must_use` reference definition. This implementation does NOT support this, as I felt that ADT's (+ `impl Trait` + `dyn Trait`) cover the usecase's people actually want on the RFC, and adding an imp for the fn call case would be significantly harder. The `must_use` impl can do a single check at fn call stmt time, but `must_not_suspend` would need to answer the question: "for some value X with type T, find any fn call that COULD have produced this value". That would require significant changes to `generator_interior.rs`, and I would need mentorship on that. `@eholk` and I are discussing it.
3. `@estebank` do you know a way I can make the user-provided `reason` note pop out? right now it seems quite hidden

Also, I am not sure if we should run perf on this

r? `@nikomatsakis`
@dtolnay
Copy link
Member

dtolnay commented Sep 29, 2021

#89303 adds #[must_not_suspend] to MutexGuard, RwLockReadGuard, RwLockWriteGuard, and the two RefCell guards Ref and RefMut.

@camelid
Copy link
Member

camelid commented Oct 11, 2021

The #[must_not_suspend] attribute is unstable, but the lint itself is insta-stable. For example, #![warn(must_not_suspend)] compiles successfully on nightly with no feature flags. Is this intentional?

@tmandry
Copy link
Member

tmandry commented Oct 12, 2021

I think that's how lints usually work.

@Mark-Simulacrum
Copy link
Member

No, lints can definitely be landed as unstable first, it's what I would expect here. There's not much risk since they're mostly just "a name", but we should still avoid accidental stabilizations.

@camelid
Copy link
Member

camelid commented Oct 12, 2021

Ok, I opened an issue: #89798.

@guswynn
Copy link
Contributor

guswynn commented Oct 12, 2021

No, lints can definitely be landed as unstable first, it's what I would expect here. There's not much risk since they're mostly just "a name", but we should still avoid accidental stabilizations.

interesting, I did not know this

@yvt
Copy link
Contributor

yvt commented Oct 12, 2021

I'm wondering: can unsafe code in a macro rely on #[forbid(must_not_suspend)] to guarantee that a local variable is never placed outside a stack? It does seem to be true at a glance, but I'm worried that there might be a loophole, considering that it's just a lint.

@guswynn
Copy link
Contributor

guswynn commented Oct 12, 2021

@yvt themust_not_suspend lint is just that: a lint. I can be skipped, can fail analysis, etc, and rustc will continue to compile the code. Unsafe should not rely on it. Are you looking for something like: https://docs.rs/futures/0.3.17/futures/macro.pin_mut.html?

@yvt
Copy link
Contributor

yvt commented Oct 13, 2021

@guswynn Thank you for the answer. Actually I'm looking for something stronger than pinning, a guarantee that Drop::drop is called before any of the containing references are invalidated. In synchronous code, this can be easily accomplished by pin_mut!, which I short-sightedly assumed would also hold in async code when it, in fact, doesn't. So I was hoping that there might be some way to mitigate the resulting soundness problem.

palfrey added a commit to palfrey/serial_test that referenced this issue Nov 12, 2021
lilymara-onesignal added a commit to OneSignal/rust-clippy that referenced this issue Apr 15, 2022
changelog: [`await_holding_invalid_type`]

This lint allows users to create a denylist of types which are not allowed to be
held across await points. This is essentially a re-implementation of the
language-level [`must_not_suspend`
lint](rust-lang/rust#83310). That lint has a lot of
work still to be done before it will reach Rust stable, and in the meantime
there are a lot of types which can trip up developers if they are used
improperly.
lilymara-onesignal added a commit to OneSignal/rust-clippy that referenced this issue Apr 15, 2022
changelog: [`await_holding_invalid_type`]

This lint allows users to create a denylist of types which are not allowed to be
held across await points. This is essentially a re-implementation of the
language-level [`must_not_suspend`
lint](rust-lang/rust#83310). That lint has a lot of
work still to be done before it will reach Rust stable, and in the meantime
there are a lot of types which can trip up developers if they are used
improperly.
bors added a commit to rust-lang/rust-clippy that referenced this issue Apr 18, 2022
Add `await_holding_invalid_type` lint

changelog: [`await_holding_invalid_type`]

This lint allows users to create a denylist of types which are not allowed to be
held across await points. This is essentially a re-implementation of the
language-level [`must_not_suspend`
lint](rust-lang/rust#83310). That lint has a lot of
work still to be done before it will reach Rust stable, and in the meantime
there are a lot of types which can trip up developers if they are used
improperly.

I originally implemented this specifically for `tracing::span::Entered`, until I discovered #8434 and read the commentary on that PR. Given this implementation is fully user configurable, doesn't tie clippy to any one particular crate, and introduces no additional dependencies, it seems more appropriate.
@tmandry tmandry moved this to In progress (current sprint) in wg-async work Dec 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC
Projects
Status: In progress (current sprint)
Development

No branches or pull requests