Skip to content

Commit bc10a25

Browse files
authored
Merge pull request #20179 from ChayimFriedman2/destructuring-assignment-never
fix: Fix diverging destructuring assignments
2 parents 7fb07c9 + 4aa30b5 commit bc10a25

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -731,9 +731,32 @@ impl InferenceContext<'_> {
731731
&Pat::Expr(expr) => {
732732
Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
733733
}
734-
Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
734+
Pat::Path(path) => {
735+
let resolver_guard =
736+
self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
737+
let resolution = self.resolver.resolve_path_in_value_ns_fully(
738+
self.db,
739+
path,
740+
self.body.pat_path_hygiene(target),
741+
);
742+
self.resolver.reset_to_guard(resolver_guard);
743+
744+
if matches!(
745+
resolution,
746+
Some(
747+
ValueNs::ConstId(_)
748+
| ValueNs::StructId(_)
749+
| ValueNs::EnumVariantId(_)
750+
)
751+
) {
752+
None
753+
} else {
754+
Some(self.infer_expr_path(path, target.into(), tgt_expr))
755+
}
756+
}
735757
_ => None,
736758
};
759+
let is_destructuring_assignment = lhs_ty.is_none();
737760

738761
if let Some(lhs_ty) = lhs_ty {
739762
self.write_pat_ty(target, lhs_ty.clone());
@@ -747,7 +770,15 @@ impl InferenceContext<'_> {
747770
self.inside_assignment = false;
748771
self.resolver.reset_to_guard(resolver_guard);
749772
}
750-
self.result.standard_types.unit.clone()
773+
if is_destructuring_assignment && self.diverges.is_always() {
774+
// Ordinary assignments always return `()`, even when they diverge.
775+
// However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail
776+
// expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring
777+
// assignments into blocks.
778+
self.table.new_maybe_never_var()
779+
} else {
780+
self.result.standard_types.unit.clone()
781+
}
751782
}
752783
Expr::Range { lhs, rhs, range_type } => {
753784
let lhs_ty =

src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,3 +785,31 @@ fn make_up_a_pointer<T>() -> *const T {
785785
"#]],
786786
)
787787
}
788+
789+
#[test]
790+
fn diverging_destructuring_assignment() {
791+
check_infer_with_mismatches(
792+
r#"
793+
fn foo() {
794+
let n = match 42 {
795+
0 => _ = loop {},
796+
_ => 0,
797+
};
798+
}
799+
"#,
800+
expect![[r#"
801+
9..84 '{ ... }; }': ()
802+
19..20 'n': i32
803+
23..81 'match ... }': i32
804+
29..31 '42': i32
805+
42..43 '0': i32
806+
42..43 '0': i32
807+
47..48 '_': !
808+
47..58 '_ = loop {}': i32
809+
51..58 'loop {}': !
810+
56..58 '{}': ()
811+
68..69 '_': i32
812+
73..74 '0': i32
813+
"#]],
814+
);
815+
}

0 commit comments

Comments
 (0)