Skip to content

Fix check_expr_if to point to a more accurate location of the compilation error in some cases#147484

Open
sgasho wants to merge 3 commits intorust-lang:mainfrom
sgasho:fix/141690_check_expr_if
Open

Fix check_expr_if to point to a more accurate location of the compilation error in some cases#147484
sgasho wants to merge 3 commits intorust-lang:mainfrom
sgasho:fix/141690_check_expr_if

Conversation

@sgasho
Copy link
Copy Markdown
Contributor

@sgasho sgasho commented Oct 8, 2025

View all comments

closes #146190

The problem linked on the issue above lays on how Expr::If is processed.

Currently, Expr::If is processed by depth-first like approach, which is natural considering that Expr::If is a nested structure.

e.g.

fn main() {
    let oa = Some(1);
    let oa2 = Some(1);
    let v = if let Some(a) = oa {
        Some(&a)
    } else if let Some(a) = oa2 {
        &Some(a)
    } else {
        None
    };
    println!("{v:?}");
}

Simplified Expr::If structure for this code is like below.

Expr::If {
  cond: "if let Some(a) = oa",
  then: "Some(&a)",
  else: {
    Expr::If {
      cond: "else if let Some(a) = oa2",
      then: "&Some(a)",
      else: {
        cond: "else",
        then: "None",
        else: None,
      }
    }
  }
}

Current check_expr_if processes this Expr::If by depth-first-search like approach, that is why "if and else have incompatible types" occurs between else if let Some(a) = oa2 and else, not between if let Some(a) = oa and if let Some(a) = oa2.

New approach on this PR is first flatten the Expr::If nested nodes, then process forward.

I created _if.rs and moved the check_expr_if becase this new approach requires a bit more lines of codes.

I added a test case that guarantees the problem on the linked issue has been resolved.
tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.rs

Discussion on Zulip: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/Struggling.20to.20fix.20issue.20.23146190.20check_expr_if/with/540960074

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 8, 2025
@sgasho

This comment was marked as outdated.

@rust-log-analyzer

This comment has been minimized.

@sgasho

This comment was marked as outdated.

@sgasho sgasho changed the title needs_help: wip fix 146190 Fix check_expr_if to point to a more accurate location of the compilation error in some cases Oct 10, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from f309ba8 to 98bc078 Compare October 13, 2025 01:55
@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from 98bc078 to c50157b Compare October 13, 2025 02:24
@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from c50157b to b522404 Compare October 13, 2025 06:52
@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Oct 13, 2025
@sgasho sgasho force-pushed the fix/141690_check_expr_if branch 4 times, most recently from ebfaecc to 5c38b51 Compare October 15, 2025 13:39
Comment on lines 12 to 17
Copy link
Copy Markdown
Contributor Author

@sgasho sgasho Oct 15, 2025

Choose a reason for hiding this comment

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

Was "if and else (have incompatible types) " appropriate for this case?
I think this else if branch has expression of type ! and it is compatible for any type. (source: "Expressions of type ! can be coerced into any other type." at https://doc.rust-lang.org/reference/types/never.html ).

I could be wrong but my understanding is showing "if may be missing an else clause" for this case (all if-else-if have compatible types and the only problem is missing else branch, which would be evaluated to ())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So,

Hmm, well, I wouldn't expect the incompatible types error. But I also wouldn't expect to see an expected .. found ... here.

I guess, the found () is possibly the type of the entire if/else if expression.

Copy link
Copy Markdown
Contributor Author

@sgasho sgasho Nov 15, 2025

Choose a reason for hiding this comment

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

The expected … found … label in this case isn’t new with this PR. I saw that current nightly already emits expected integer, found () for error[E0317]: if may be missing an else clause. e.g. tests/ui/expr/if/issue-4201.rs

Should this be discussed or fixed in another issue?

@sgasho sgasho marked this pull request as ready for review October 15, 2025 15:06
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 15, 2025
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Oct 15, 2025
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Oct 15, 2025

r? @SparrowLii

rustbot has assigned @SparrowLii.
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

@rustbot

This comment has been minimized.

@fmease
Copy link
Copy Markdown
Member

fmease commented Oct 19, 2025

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Oct 19, 2025
@rustbot rustbot assigned jackh726 and unassigned SparrowLii Oct 19, 2025
@jackh726
Copy link
Copy Markdown
Member

Sorry for the delay here. Before I do an actual review here, I want to check perf.

@bors2 try @rust-timer build

@sgasho sgasho requested a review from jackh726 November 15, 2025 12:29
@rustbot rustbot 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 Nov 15, 2025
Copy link
Copy Markdown
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

Some more review comments. Looking better, but still have some questions/thoughts.

View changes since this review

Comment on lines +2711 to +2716
if let hir::ExprKind::Block(block, _) = &expr.kind
&& block.expr.is_none()
{
return None;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why did you add this? Is there a test difference that you can comment and link to?

Comment thread compiler/rustc_hir_typeck/src/_if.rs Outdated
&self,
expr_id: HirId,
sp: Span,
parts: &IfExprParts<'tcx>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here, and elsewhere in the file, these are Copy and so shouldn't be passed by ref

Comment thread compiler/rustc_hir_typeck/src/_if.rs Outdated
parts: &IfExprParts<'tcx>,
orig_expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let root_if_expr = self.tcx.hir_expect_expr(expr_id);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could pass this in instead of expr_id and sp

Comment thread compiler/rustc_hir_typeck/src/_if.rs Outdated
Comment on lines +107 to +109
.get(idx + 1)
.map(|next| next.expr_with_parts.expr)
.or(chain.last_expr().into());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm, this doesn't seem right (or at least, is too subtle for my liking): when idx is len-1, chain.last_expr() is called and there is no final else, uses the current expr (since it is the last one).

Comment thread compiler/rustc_hir_typeck/src/_if.rs Outdated
self.misc(branch_body.span)
};
let cause_span =
if idx == 0 { Some(root_if_expr.span) } else { Some(branch_body.span) };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If idx == 0, isn't branch_body.span == root_if_expr.span?

.typeck_results
.as_ref()
.expect("if expression only expected inside FnCtxt")
.expr_ty(else_expr);
if let hir::ExprKind::If(_cond, _then, None) = else_expr.kind
.expr_ty_opt(else_expr);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm, so...is this ever going to return Some?

@jackh726
Copy link
Copy Markdown
Member

@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 Nov 25, 2025
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Nov 25, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@jackh726
Copy link
Copy Markdown
Member

Ping @sgasho. Are you still working on this?

@sgasho
Copy link
Copy Markdown
Contributor Author

sgasho commented Mar 31, 2026

@jackh726

Ping @sgasho. Are you still working on this?

Sorry, Currently No. I've been working on around autodiff and infra issues for several months and had no time to working on this.
But I can resume it around April. If this is urgent, another person can continue it.

@jackh726
Copy link
Copy Markdown
Member

No worries - this can wait. (If anyone else wants to pick this up, let me know.)

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from fdd59f6 to 71919dc Compare May 4, 2026 10:18
@rustbot

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from 71919dc to da8de8f Compare May 4, 2026 10:23
@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from da8de8f to dddffd2 Compare May 4, 2026 10:25
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 4, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from dddffd2 to 1f3fe1e Compare May 4, 2026 10:33
@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from 1f3fe1e to 57eeb19 Compare May 4, 2026 10:45
@sgasho
Copy link
Copy Markdown
Contributor Author

sgasho commented May 4, 2026

@jackh726

Changed direction. The 7 old commits are squashed + reverted in history so both designs are reviewable.

Keeping the code architecture clean with the flattening approach turned out to be harder than I
expected, so I rethought it.

The new approach keeps the nested Expr::If and passes parent ty to children to coerce them.

Also, I added a test case for an if-chain nested inside another if-chain at tests/ui/typeck/if-else-type-mismatch-chain-nested.rs.

FWIW, I made some simple illustrations of the before/after approaches.

Before

スクリーンショット 2026-05-04 19 55 21

After

スクリーンショット 2026-05-04 20 17 35

@rust-log-analyzer

This comment has been minimized.

@sgasho sgasho force-pushed the fix/141690_check_expr_if branch from 57eeb19 to 7d6060c Compare May 4, 2026 13:29
@sgasho
Copy link
Copy Markdown
Contributor Author

sgasho commented May 4, 2026

@rustbot ready

@rustbot rustbot 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 May 4, 2026
@sgasho sgasho requested a review from jackh726 May 4, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The compilation error prompt points to the wrong location

7 participants