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

Properly handle async block and async fn in if exprs without else #120696

Merged
merged 2 commits into from
Feb 13, 2024

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Feb 6, 2024

When encountering a tail expression in the then arm of an if expression without an else arm, account for async fn and async blocks to suggest returning the value and pointing at the return type of the async fn.

We now also account for AFIT when looking for the return type to point at.

Fix #115405.

@rustbot
Copy link
Collaborator

rustbot commented Feb 6, 2024

r? @oli-obk

(rustbot has picked a reviewer for you, use r? to override)

@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 Feb 6, 2024
let ret_ty = ret_coercion.borrow().expected_ty();
rets.push(ret_ty);
}
let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need to do this? If the ret_coercion has a type, then it will have already been coerced with all of these ret_exprs collected below.

Copy link
Member

Choose a reason for hiding this comment

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

Like, the whole point of ret_coercion is to be the type that all of the return expressions in the body are coerced into

Copy link
Contributor Author

@estebank estebank Feb 6, 2024

Choose a reason for hiding this comment

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

ret_coercion is (always?) an inference type. I could likely just remove that first type push entirely and the logic wouldn't change.

Copy link
Member

@compiler-errors compiler-errors Feb 6, 2024

Choose a reason for hiding this comment

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

It's not always an inference type. It may appear to be an inference type if you print it without calling resolve_vars_if_possible on it (which you don't need to do here), but it gets equated with another type as soon as the first return statement is type checked, and can_coerce will handle it properly below.

Applying this diff makes no changes to the UI tests:

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index afe5d0d4880..0a03429b37f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,6 +1,5 @@
 use super::FnCtxt;
 
-use crate::coercion::CollectRetsVisitor;
 use crate::errors;
 use crate::fluent_generated as fluent;
 use crate::fn_ctxt::rustc_span::BytePos;
@@ -17,7 +16,6 @@
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
-use rustc_hir::intravisit::{Map, Visitor};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
     CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
@@ -1082,23 +1080,6 @@ pub(in super::super) fn suggest_missing_break_or_return_expr(
                     let ret_ty = ret_coercion.borrow().expected_ty();
                     rets.push(ret_ty);
                 }
-                let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
-                if let Some(item) = self.tcx.hir().find(id)
-                    && let Node::Expr(expr) = item
-                {
-                    visitor.visit_expr(expr);
-                    for expr in visitor.ret_exprs {
-                        if let Some(ty) = self.typeck_results.borrow().node_type_opt(expr.hir_id) {
-                            rets.push(ty);
-                        }
-                    }
-                    if let hir::ExprKind::Block(hir::Block { expr: Some(expr), .. }, _) = expr.kind
-                    {
-                        if let Some(ty) = self.typeck_results.borrow().node_type_opt(expr.hir_id) {
-                            rets.push(ty);
-                        }
-                    }
-                }
                 rets.into_iter().all(|ty| self.can_coerce(found, ty))
             }
             _ => false,

Unless I am mistaken, pls remove the CollectRetsVisitor usage (and the changes to it).

Copy link
Member

@compiler-errors compiler-errors Feb 6, 2024

Choose a reason for hiding this comment

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

For the record, that diff is to just demonstrate that it works without the usage of CollectRetsVisitor. The code should be simplified into a single can_coerce call.

Copy link
Member

@compiler-errors compiler-errors Feb 6, 2024

Choose a reason for hiding this comment

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

Actually, I think none of your tests aren't actually exercising this behavior of a previous return statement having constrained the ret_coercion?

Anyways, you should also be using .merged_ty() from the ret_coercion instead of .expected_ty(), but you still shouldn't need to look at any of the node_tys of the return expressions. You should write a test that exercises that the lint will not be made if the type is incompatible with other returns.
edit: actually, it's not that big of a deal which of merged-ty or expected-ty you use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to avoid an incorrect suggestion in the face of

pub fn baz() -> Pin<Box<dyn Future<Output = Result<S, ()>> + 'static>> {
    Box::pin(async move {
        if true {
            Some(S) //~ ERROR mismatched types
        }
        Err(())
    })
}

Now, looking into it, not only the logic is now wrong (after doing some changes cleaning up for publication), but the type for the returning expressions isn't actually available. For some reason, I had gotten a value for them, but now with the code as is now. Given that, yes, the entire visitor thing is useless and can be removed. I was hoping to avoid nonsensical suggestions like the one above, but oh well.

Copy link
Member

Choose a reason for hiding this comment

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

Keeping around a single can_coerce call for the ret_coercion.expected_ty() should avoid such a nonsensical suggestion, at least according to local testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I know why I had something that seemed to be working earlier. I tried using check_expr at some point, which "worked", but trying it again now it does cause multiple issues that make it more of a pain in the neck than we want to deal with.

@@ -92,14 +92,16 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {

type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;

struct CollectRetsVisitor<'tcx> {
ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
pub struct CollectRetsVisitor<'tcx> {
Copy link
Member

Choose a reason for hiding this comment

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

this isn't needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, shit, had in flight changes... let me clean up the mess first.

@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 7, 2024
@bors
Copy link
Contributor

bors commented Feb 8, 2024

☔ The latest upstream changes (presumably #120767) made this pull request unmergeable. Please resolve the merge conflicts.

@estebank estebank added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 8, 2024
@rust-log-analyzer

This comment has been minimized.

@oli-obk
Copy link
Contributor

oli-obk commented Feb 8, 2024

@bor@ r+

@bors
Copy link
Contributor

bors commented Feb 12, 2024

☔ The latest upstream changes (presumably #120951) made this pull request unmergeable. Please resolve the merge conflicts.

@oli-obk
Copy link
Contributor

oli-obk commented Feb 12, 2024

@bor@ r+

uhm... mobile keyboard failure

r=me after rebase

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 12, 2024
When encountering a tail expression in the then arm of an `if` expression
without an `else` arm, account for `async fn` and `async` blocks to
suggest `return`ing the value and pointing at the return type of the
`async fn`.

We now also account for AFIT when looking for the return type to point at.

Fix rust-lang#115405.
@estebank
Copy link
Contributor Author

@bors r=oli-obk

@bors
Copy link
Contributor

bors commented Feb 12, 2024

📌 Commit d07195f has been approved by oli-obk

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 12, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 13, 2024
…iaskrgr

Rollup of 10 pull requests

Successful merges:

 - rust-lang#120696 (Properly handle `async` block and `async fn` in `if` exprs without `else`)
 - rust-lang#120751 (Provide more suggestions on invalid equality where bounds)
 - rust-lang#120802 (Bail out of drop elaboration when encountering error types)
 - rust-lang#120967 (docs: mention round-to-even in precision formatting)
 - rust-lang#120973 (allow static_mut_ref in some tests that specifically test mutable statics)
 - rust-lang#120974 (llvm-wrapper: adapt for LLVM API change: Add support for EXPORTAS name types)
 - rust-lang#120986 (iterator.rs: remove "Basic usage" text)
 - rust-lang#120987 (remove redundant logic)
 - rust-lang#120988 (fix comment)
 - rust-lang#120995 (PassWrapper: adapt for llvm/llvm-project@93cdd1b5cfa3735c)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 020e846 into rust-lang:master Feb 13, 2024
11 checks passed
@rustbot rustbot added this to the 1.78.0 milestone Feb 13, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Feb 13, 2024
Rollup merge of rust-lang#120696 - estebank:issue-115405, r=oli-obk

Properly handle `async` block and `async fn` in `if` exprs without `else`

When encountering a tail expression in the then arm of an `if` expression without an `else` arm, account for `async fn` and `async` blocks to suggest `return`ing the value and pointing at the return type of the `async fn`.

We now also account for AFIT when looking for the return type to point at.

Fix rust-lang#115405.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

^^^^^^^ expected (), found Result<ServiceResponse<B>, _> ??? not related not informative ..
6 participants