Skip to content

Semicolon after diverging try operator shouldn't matter #106357

Closed as not planned
Closed as not planned

Description

@clarfonthey

Originally mentioned in this comment: #96373 (comment)

Essentially, semicolons after other diverging expressions are allowed. For example:

fn works() -> Result<bool, i32> {
    return Err(1);
}

Here, the compiler is clever enough to note that because return Err(1) always diverges, adding a semicolon after doesn't matter. However, the below code which is equivalent to this does not work properly:

fn breaks() -> Result<bool, i32> {
    Err(1)?;
}

Here's a playground link containing the above two examples: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=af894bab48348897a421a9402856b71f

The exact compiler error:

error[[E0308]](https://doc.rust-lang.org/nightly/error-index.html#E0308): mismatched types
 --> src/lib.rs:5:16
  |
5 | fn breaks() -> Result<bool, i32> {
  |    ------      ^^^^^^^^^^^^^^^^^ expected enum `Result`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
6 |     Err(1)?;
  |            - help: remove this semicolon to return this value
  |
  = note:   expected enum `Result<bool, i32>`
          found unit type `()`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Activity

  1. andersk commented on Jan 1, 2023

    @andersk
    Contributor

    My understanding is that this is related to the fallback issues that have been preventing stabilization of the ! type. See

  2. clarfonthey commented on Jan 2, 2023

    @clarfonthey
    ContributorAuthor

    I was kind of afraid that's the case.

    If this seems too separate from the existing issues for those, feel free to close it! Although I guess that this is another example that might be useful to test for.

  3. aDotInTheVoid commented on Jan 2, 2023

    @aDotInTheVoid
    Member

    I'm not sure this is a bug,

    fn breaks() -> Result<bool, i32> {
        Err(1)?;
    }

    gets desugared to

    fn breaks() -> Result<bool, i32> {
        match Err(1) {
             Ok(x) => x,
             Err(e) => return Err(e.into())
        };
    }

    Unless you are relying on the compiler to eliminate the Ok branch as unreachable, which isn't guaranteed to happen, the whole expression isn't diverging (in the mind of the type system).

    I don't think the compiler should be required to do this elimination, as involving optimizations in exausiveness checks seems like it could get unintuitive fast, and expose many people to accidental breakage.

  4. added
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Jan 2, 2023
  5. clarfonthey commented on Jan 2, 2023

    @clarfonthey
    ContributorAuthor

    So, I would agree with this, except for the fact that the compiler could absolutely infer the type of Err(1) to be Result<!, i32> and thus treat the Ok branch as diverging. But that leads into the ! fallback problems that were mentioned.

  6. est31 commented on Jan 4, 2023

    @est31
    Member
  7. kadiwa4 commented on Jan 5, 2023

    @kadiwa4
    Contributor

    Also related: feature exhaustive_patterns #51085

  8. added
    A-never-typeArea: ! type. For issues not specific to !, use the A-uninhabited-types label.
    on Jun 10, 2024
  9. WaffleLapkin commented on Jun 13, 2024

    @WaffleLapkin
    Member

    So. Mrgh. This issue is indeed with the fallback.

    fn breaks() -> Result<bool, i32> {
        match Err(1) {
             Ok(x) => x,
             Err(e) => return Err(e.into())
        };
    }

    This desugaring causes the type of the match to be inferred to (), due to the fallback issue. I would expect that this will start "working" in 2024 edition.

    Note however that I would highly advice against writing Err(x)?. The type of Ok variant should no be inferred from the ?. IMO it's a bug in ?'s desugaring. Similarly, allowing ; after diverging expressions is somewhat confusing. I was discussing with T-lang if we can break both of those things, but it did not cut the 2024 edition (and not yet decided that we actually want to break these things).

  10. added
    C-discussionCategory: Discussion or questions that doesn't represent real issues.
    and removed
    C-bugCategory: This is a bug.
    on Jun 13, 2024
  11. traviscross commented on Jun 14, 2024

    @traviscross
    Contributor
  12. WaffleLapkin commented on Oct 18, 2025

    @WaffleLapkin
    Member

    This issue is more complicated than I gave it credit in the comment above. Even with fallback to ! this code example still doesn't compile:

    fn breaks() -> Result<(), i32> {
        match Err(1) {
             Ok(x) => x,
             Err(e) => return Err(e.into())
        };
        //~^ error: mismatched types expected `Result<(), i32>`, found `()`
    }

    Even though, the type of x (and thus the whole match expression) is !. The difference with the return example:

    fn works() -> Result<(), i32> {
        return Err(1);
    }

    Is that with match/? we only know that it's of type ! after doing the never type fallback. However, that's too late -- we check if blocks diverge while type checking, way before fallback.

    I'm tempted to close this as not planned, I don't think it's reasonable to require compiler to detect Err(..)? as diverging.,

  13. clarfonthey commented on Oct 18, 2025

    @clarfonthey
    ContributorAuthor

    This may be another potential reason to prefer yeeting…

  14. added
    I-lang-radarItems that are on lang's radar and will need eventual work or consideration.
    T-langRelevant to the language team
    on Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-never-typeArea: ! type. For issues not specific to !, use the A-uninhabited-types label.C-discussionCategory: Discussion or questions that doesn't represent real issues.I-lang-radarItems that are on lang's radar and will need eventual work or consideration.T-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions