Skip to content

Conversation

joshtriplett
Copy link
Member

@joshtriplett joshtriplett commented Oct 5, 2025

This suppresses warnings on things like Result<(), !>, which helps simplify code using the common pattern of having an Error associated type: code will only have to check the error if there is a possibility of error.

This will, for instance, help with future refactorings of write! in the standard library.

As this is a user-visible change to lint behavior, it'll require a lang FCP.


My proposal, here, is that we handle this simple case to make common patterns more usable. This does not rule out the possibility of adding more cases in the future, including general trait-based cases. However, I don't think we should make this common case wait on the more general cases. In particular, this solution does not close any doors on replacing this special case with a general case.

This would unblock some planned work in the standard library to make write! more usable for infallible cases (e.g. writing into a Vec or String).

This simplifies the initial conditional, and will allow reusing the
variable in subsequent checks.
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 5, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 5, 2025

r? @fmease

rustbot has assigned @fmease.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@joshtriplett joshtriplett added T-lang Relevant to the language team I-lang-nominated Nominated for discussion during a lang team meeting. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. relnotes Marks issues that should be documented in the release notes of the next release. labels Oct 5, 2025
@fmease fmease added S-waiting-on-team DEPRECATED: Use the team-based variants `S-waiting-on-t-lang`, `S-waiting-on-t-compiler`, ... and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 5, 2025
@fmease
Copy link
Member

fmease commented Oct 5, 2025

I'll be waiting for the outcome of T-lang's triage meeting before reviewing this since the proposed rules might very well change drastically (e.g., trait-driven vs. not trait-driven; cc Zulip discussion).

@joshtriplett
Copy link
Member Author

Summarizing the Zulip discussion:

  • What this PR does: We could do this by looking for any uninhabited type. This takes into account types that are not publicly known to be uninhabited, and types that are marked as #[non_exhaustive] (so they may not always be uninhabited).
  • We could do this by hardcoding types like ! or Infallible.
  • We could do this by identifying the types in some other way, such as a dedicated attribute, or a trait.
  • We could require people to use Result::into_ok() (once stabilized). This would be less readable, though.
  • Some discussion about cases like enums with variants that are sometimes configured out.

@joshtriplett
Copy link
Member Author

joshtriplett commented Oct 5, 2025

My proposal, here, is that we handle this simple case to make common patterns more usable. This does not rule out the possibility of adding more cases in the future, including general trait-based cases. However, I don't think we should make this common case wait on the more general cases. In particular, this solution does not close any doors on replacing this special case with a general case.

This would unblock some planned work in the standard library to make write! more usable for infallible cases (e.g. writing into a Vec or String).

@Amanieu
Copy link
Member

Amanieu commented Oct 5, 2025

As a general rule I think this should follow the same rules what let Ok(()) = foo would accept.

This suppresses warnings on things like `Result<(), !>`, which helps
simplify code using the common pattern of having an `Error` associated
type: code will only have to check the error if there is a possibility
of error.
@joshtriplett joshtriplett force-pushed the unused-must-use-ignore-result-unit-uninhabited branch from d9d6c49 to 23a0751 Compare October 6, 2025 05:10
@joshtriplett
Copy link
Member Author

I've updated this to follow the same pattern as other inhabitedness checking, of only caring about non_exhaustive from external crates.

I've also updated it to handle ControlFlow.

@joshtriplett joshtriplett changed the title unused_must_use: Don't warn on Result<(), Uninhabited> unused_must_use: Don't warn on Result<(), Uninhabited> or ControlFlow<Uninhabited, ()> Oct 6, 2025
@niacdoial
Copy link
Contributor

huh...
github doesn't let me choose your branch as a PR destination.
er... anyway, I would like to contribute a test (making sure unresolved type aliases behave like inhabited types), if you'll let me:
https://github.com/niacdoial/rust-contrib-fork/tree/must_use_uninhab

joshtriplett and others added 2 commits October 6, 2025 08:06
This makes it easier to review without cross-referencing each test
function with its invocation.
@joshtriplett
Copy link
Member Author

huh... github doesn't let me choose your branch as a PR destination. er... anyway, I would like to contribute a test (making sure unresolved type aliases behave like inhabited types), if you'll let me: https://github.com/niacdoial/rust-contrib-fork/tree/must_use_uninhab

Added, thanks!

@Urgau Urgau added S-waiting-on-t-lang Status: Awaiting decision from T-lang and removed S-waiting-on-team DEPRECATED: Use the team-based variants `S-waiting-on-t-lang`, `S-waiting-on-t-compiler`, ... labels Oct 6, 2025
@traviscross traviscross added the P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang label Oct 8, 2025
@scottmcm
Copy link
Member

scottmcm commented Oct 8, 2025

Is this the same as "don't must_use warn on things that are ZSTs"?

Thus it could be phrased as "change the ()-specific rule to any ZST, not just the unit ZST".

@joshtriplett
Copy link
Member Author

@scottmcm I don't want to generalize that here yet, because someone might e.g. have some #[must_use] ZST that represents "capture this token and pass it to some other function to prove you got an Ok", and I wouldn't want to override that. Some more general design could argue for that, but () is the case that I'm confident any more general solution will encompass.

@traviscross traviscross removed the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Oct 8, 2025
@traviscross
Copy link
Contributor

This makes sense to me, in particular how this helps with write!, etc., and the test cases seem right.

@rfcbot fcp merge

@rust-rfcbot
Copy link
Collaborator

rust-rfcbot commented Oct 8, 2025

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

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Oct 8, 2025
Comment on lines +309 to +313
&& args.type_at(1).is_unit()
&& !args.type_at(0).is_inhabited_from(
cx.tcx,
parent_mod_did,
cx.typing_env(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels weird to me. ControlFlow is all about not giving a particular "human intent" to one or the other (just getting the ? polarity you want), so I would expect ControlFlow<(), !> and ControlFlow<!, ()> to either both lint or both not lint.

(Not supporting Result<!, ()> I understand since we generally tell people not to use () there in the first place, but for ControlFlow both ways are plausible.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottmcm I don't have any strong opinions about ControlFlow here. My own impression was that it'd be confusing to do that, but I'd love to hear feedback from other users of ControlFlow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottmcm
Copy link
Member

scottmcm commented Oct 8, 2025

Absolutely support the intent here, so
@rfcbot reviewed

I do have a non-quite-concern about the current implementation for ControlFlow that I left above. (If we had niko's new framework it'd be a "let's talk if you disagree", not a "need an FCP to override" kind of thing.)

@rust-rfcbot rust-rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Oct 8, 2025
@rust-rfcbot
Copy link
Collaborator

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

@nikomatsakis
Copy link
Contributor

@rfcbot reviewed

Seems sensible.

@tmandry
Copy link
Member

tmandry commented Oct 8, 2025

@rfcbot reviewed

I think this is a positive change for the language. I'd be happy to see a generalization down the line so that other types can make use of it. Maybe in the form of a diagnostic attribute on a type parameter, e.g.

#[must_use]
pub enum Result<T, #[diagnostic::may_discard_if_uninhabited] E> {
  Ok(T),
  Err(E),
}

@scottmcm
Copy link
Member

scottmcm commented Oct 8, 2025

I feel like if we're generalizing we might as well go to a trait-based solution so that, for example, impl<T: MustUse> MustUse for Option<T> becomes possible. (Maybe even things like impl !MustUse for () and such too.)

Otherwise we're recreating all these systems in diagnostics instead...

@tmandry
Copy link
Member

tmandry commented Oct 8, 2025

Interesting. That would be a nice direction too. The biggest design question I can think of there is whether removing a MustUse impl (or changing its bounds, as this PR would be doing) is considered semver-breaking. Probably it shouldn't be.

@joshtriplett

This comment was marked as outdated.

@joshtriplett
Copy link
Member Author

@tmandry Ideally it'd be impossible to reference the trait outside of impl MustUse for blocks.

Also, it's not clear how to implement the exceptions to MustUse with the structure of our trait system (e.g. Result<T, E> is MustUse except if...).

All that said, I'd love to see a design proposal for a more general version, and I'm also glad we're shipping this version. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-nominated Nominated for discussion during a lang team meeting. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-t-lang Status: Awaiting decision from T-lang T-lang Relevant to the language team
Projects
None yet
Development

Successfully merging this pull request may close these issues.