Skip to content

Commit f309ba8

Browse files
committed
fix
1 parent 5d287f2 commit f309ba8

File tree

10 files changed

+138
-201
lines changed

10 files changed

+138
-201
lines changed

compiler/rustc_hir_typeck/src/_if.rs

Lines changed: 73 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ struct IfChain<'tcx> {
2929
cond_error: Option<ErrorGuaranteed>,
3030
}
3131

32+
enum IfChainTail<'tcx> {
33+
FinalElse(BranchBody<'tcx>),
34+
Missing(&'tcx hir::Expr<'tcx>),
35+
}
36+
3237
const RECENT_BRANCH_HISTORY_LIMIT: usize = 5;
3338

3439
#[derive(Default)]
@@ -57,99 +62,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5762
pub(crate) fn check_expr_if(
5863
&self,
5964
expr_id: HirId,
60-
cond_expr: &'tcx hir::Expr<'tcx>,
61-
then_expr: &'tcx hir::Expr<'tcx>,
62-
opt_else_expr: Option<&'tcx hir::Expr<'tcx>>,
6365
sp: Span,
6466
orig_expected: Expectation<'tcx>,
6567
) -> Ty<'tcx> {
6668
let root_if_expr = self.tcx.hir_expect_expr(expr_id);
67-
if !self.if_chain_has_final_else(root_if_expr) {
68-
let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp);
69-
return self.evaluate_if_without_final_else(
70-
expr_id,
71-
cond_expr,
72-
then_expr,
73-
opt_else_expr,
74-
sp,
75-
orig_expected,
76-
expected,
77-
);
78-
}
79-
8069
let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp);
81-
self.evaluate_if_chain_with_final_else(expr_id, root_if_expr, sp, orig_expected, expected)
82-
}
83-
84-
fn evaluate_if_without_final_else(
85-
&self,
86-
expr_id: HirId,
87-
cond_expr: &'tcx hir::Expr<'tcx>,
88-
then_expr: &'tcx hir::Expr<'tcx>,
89-
opt_else_expr: Option<&'tcx hir::Expr<'tcx>>,
90-
sp: Span,
91-
orig_expected: Expectation<'tcx>,
92-
expected: Expectation<'tcx>,
93-
) -> Ty<'tcx> {
94-
let (cond_ty, cond_diverges) = self.check_if_condition(cond_expr, then_expr.span);
95-
96-
let BranchBody { ty: then_ty, diverges: then_diverges, .. } =
97-
self.check_branch_body(then_expr, expected);
98-
99-
// We've already taken the expected type's preferences
100-
// into account when typing the `then` branch. To figure
101-
// out the initial shot at a LUB, we thus only consider
102-
// `expected` if it represents a *hard* constraint
103-
// (`only_has_type`); otherwise, we just go with a
104-
// fresh type variable.
105-
let coerce_to_ty = expected.coercion_target_type(self, sp);
106-
let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty);
107-
108-
coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
109-
110-
if let Some(else_expr) = opt_else_expr {
111-
let BranchBody { ty: else_ty, diverges: else_diverges, .. } =
112-
self.check_branch_body(else_expr, expected);
113-
114-
let tail_defines_return_position_impl_trait =
115-
self.return_position_impl_trait_from_match_expectation(orig_expected);
116-
let if_cause =
117-
self.if_cause(expr_id, else_expr, tail_defines_return_position_impl_trait);
118-
119-
coerce.coerce(self, &if_cause, else_expr, else_ty);
120-
121-
// We won't diverge unless both branches do (or the condition does).
122-
self.diverges.set(cond_diverges | then_diverges & else_diverges);
123-
} else {
124-
self.if_fallback_coercion(sp, cond_expr, then_expr, &mut coerce);
125-
126-
// If the condition is false we can't diverge.
127-
self.diverges.set(cond_diverges);
128-
}
129-
130-
let result_ty = coerce.complete(self);
131-
if let Err(guar) = cond_ty.error_reported() {
132-
Ty::new_error(self.tcx, guar)
133-
} else {
134-
result_ty
135-
}
136-
}
137-
138-
fn evaluate_if_chain_with_final_else(
139-
&self,
140-
expr_id: HirId,
141-
root_if_expr: &'tcx hir::Expr<'tcx>,
142-
sp: Span,
143-
orig_expected: Expectation<'tcx>,
144-
expected: Expectation<'tcx>,
145-
) -> Ty<'tcx> {
146-
let mut chain = IfChain::default();
14770

14871
let initial_diverges = self.diverges.get();
149-
let terminal_else = self.collect_if_chain(root_if_expr, expected, &mut chain);
15072

151-
let Some(else_branch) = terminal_else else {
152-
bug!("sequential `if` chain expected a final `else` arm");
73+
let (chain, tail) = self.collect_if_chain(root_if_expr, expected);
74+
75+
let terminal_else_expr = match &tail {
76+
IfChainTail::FinalElse(else_branch) => Some(else_branch.expr),
77+
IfChainTail::Missing(last_if_expr) => Some(*last_if_expr),
15378
};
15479

15580
let coerce_to_ty = expected.coercion_target_type(self, sp);
@@ -166,40 +91,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16691
}
16792

16893
let branch_body = &branch.body;
94+
let skip_coercion = matches!(tail, IfChainTail::Missing(_))
95+
&& chain.branches.len() > 1
96+
&& idx == chain.branches.len() - 1;
97+
if skip_coercion {
98+
recent_branch_types.record(branch_body.ty, branch_body.span);
99+
continue;
100+
}
169101
let next_else_expr =
170-
chain.branches.get(idx + 1).map(|next| next.if_expr).unwrap_or(else_branch.expr);
171-
let mut branch_cause = self.if_cause(
172-
branch.if_expr.hir_id,
173-
next_else_expr,
174-
tail_defines_return_position_impl_trait,
175-
);
102+
chain.branches.get(idx + 1).map(|next| next.if_expr).or(terminal_else_expr);
103+
let mut branch_cause = if let Some(next_else_expr) = next_else_expr {
104+
let prev_branch = chain.branches.get(idx - 1).unwrap_or(branch);
105+
self.if_cause(
106+
prev_branch.if_expr.hir_id,
107+
next_else_expr,
108+
tail_defines_return_position_impl_trait,
109+
)
110+
} else {
111+
self.misc(branch_body.span)
112+
};
176113
let diag_info = recent_branch_types.diagnostic_snapshot();
114+
let cause_span =
115+
if idx == 0 { Some(root_if_expr.span) } else { Some(branch_body.span) };
116+
177117
self.coerce_if_arm(
178118
&mut coerce,
179119
&mut branch_cause,
180120
branch_body.expr,
181121
branch_body.ty,
122+
cause_span,
182123
branch_body.span,
183124
diag_info,
184125
);
185126

186127
recent_branch_types.record(branch_body.ty, branch_body.span);
187128
}
188129

189-
let mut else_cause =
190-
self.if_cause(expr_id, else_branch.expr, tail_defines_return_position_impl_trait);
191-
let diag_info = recent_branch_types.diagnostic_snapshot();
192-
self.coerce_if_arm(
193-
&mut coerce,
194-
&mut else_cause,
195-
else_branch.expr,
196-
else_branch.ty,
197-
else_branch.span,
198-
diag_info,
199-
);
200-
recent_branch_types.record(else_branch.ty, else_branch.span);
130+
let mut tail_diverges = match tail {
131+
IfChainTail::FinalElse(else_branch) => {
132+
let mut else_cause = self.if_cause(
133+
expr_id,
134+
else_branch.expr,
135+
tail_defines_return_position_impl_trait,
136+
);
137+
let diag_info = recent_branch_types.diagnostic_snapshot();
138+
self.coerce_if_arm(
139+
&mut coerce,
140+
&mut else_cause,
141+
else_branch.expr,
142+
else_branch.ty,
143+
None,
144+
else_branch.span,
145+
diag_info,
146+
);
147+
recent_branch_types.record(else_branch.ty, else_branch.span);
148+
149+
else_branch.diverges
150+
}
151+
IfChainTail::Missing(last_if_expr) => {
152+
let hir::ExprKind::If(tail_cond, tail_then, _) = last_if_expr.kind else {
153+
bug!("expected `if` expression, found {:#?}", last_if_expr);
154+
};
155+
self.if_fallback_coercion(last_if_expr.span, tail_cond, tail_then, &mut coerce);
156+
Diverges::Maybe
157+
}
158+
};
201159

202-
let mut tail_diverges = else_branch.diverges;
203160
for branch in chain.branches.iter().rev() {
204161
tail_diverges = branch.cond_diverges | (branch.body.diverges & tail_diverges);
205162
}
@@ -229,16 +186,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
229186
cause: &mut traits::ObligationCause<'tcx>,
230187
expr: &'tcx hir::Expr<'tcx>,
231188
ty: Ty<'tcx>,
232-
span: Span,
189+
cause_span: Option<Span>,
190+
body_span: Span,
233191
prior_branches: SmallVec<[(Ty<'tcx>, Span); 4]>,
234192
) {
235-
cause.span = span;
193+
if let Some(span) = cause_span {
194+
cause.span = span;
195+
}
236196
coerce.coerce_inner(
237197
self,
238198
cause,
239199
Some(expr),
240200
ty,
241-
move |err| self.explain_if_branch_mismatch(err, span, &prior_branches),
201+
move |err| self.explain_if_branch_mismatch(err, body_span, &prior_branches),
242202
false,
243203
);
244204
}
@@ -258,35 +218,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
258218
(cond_ty, cond_diverges)
259219
}
260220

261-
fn if_chain_has_final_else(&self, mut current: &'tcx hir::Expr<'tcx>) -> bool {
262-
loop {
263-
match current.kind {
264-
hir::ExprKind::If(_, _, Some(else_expr)) => match else_expr.kind {
265-
hir::ExprKind::If(..) => current = else_expr,
266-
_ => return true,
267-
},
268-
_ => return false,
269-
}
270-
}
271-
}
272-
273221
fn collect_if_chain(
274222
&self,
275223
mut current_if: &'tcx hir::Expr<'tcx>,
276224
expected: Expectation<'tcx>,
277-
chain: &mut IfChain<'tcx>,
278-
) -> Option<BranchBody<'tcx>> {
225+
) -> (IfChain<'tcx>, IfChainTail<'tcx>) {
226+
let mut chain: IfChain<'tcx> = IfChain::default();
227+
279228
loop {
280-
let Some(else_expr) = self.collect_if_branch(current_if, expected, chain) else {
281-
return None;
229+
let Some(else_expr) = self.collect_if_branch(current_if, expected, &mut chain) else {
230+
return (chain, IfChainTail::Missing(current_if));
282231
};
283232

284233
if let hir::ExprKind::If(..) = else_expr.kind {
285234
current_if = else_expr;
286235
continue;
287236
}
288237

289-
return Some(self.collect_final_else(else_expr, expected));
238+
return (chain, IfChainTail::FinalElse(self.collect_final_else(else_expr, expected)));
290239
}
291240
}
292241

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,9 +584,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
584584
self.demand_eqtype(e.span, ascribed_ty, ty);
585585
ascribed_ty
586586
}
587-
ExprKind::If(cond, then_expr, opt_else_expr) => {
588-
self.check_expr_if(expr.hir_id, cond, then_expr, opt_else_expr, expr.span, expected)
589-
}
587+
ExprKind::If(..) => self.check_expr_if(expr.hir_id, expr.span, expected),
590588
ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected),
591589
ExprKind::Array(args) => self.check_expr_array(args, expected, expr),
592590
ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected),

tests/ui/expr/if/if-else-chain-missing-else.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
enum Cause { Cause1, Cause2 }
2-
struct MyErr { x: Cause }
1+
enum Cause {
2+
Cause1,
3+
Cause2,
4+
}
5+
struct MyErr {
6+
x: Cause,
7+
}
38

49
fn main() {
510
_ = f();
@@ -9,7 +14,7 @@ fn f() -> Result<i32, MyErr> {
914
let res = could_fail();
1015
let x = if let Ok(x) = res {
1116
x
12-
} else if let Err(e) = res { //~ ERROR `if` and `else`
17+
} else if let Err(e) = res { //~ ERROR `if` may be missing an `else` clause
1318
return Err(e);
1419
};
1520
Ok(x)
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
error[E0308]: `if` and `else` have incompatible types
2-
--> $DIR/if-else-chain-missing-else.rs:12:12
1+
error[E0317]: `if` may be missing an `else` clause
2+
--> $DIR/if-else-chain-missing-else.rs:17:12
33
|
4-
LL | let x = if let Ok(x) = res {
5-
| ------------------ `if` and `else` have incompatible types
6-
LL | x
74
LL | } else if let Err(e) = res {
85
| ____________^
96
LL | | return Err(e);
107
LL | | };
118
| |_____^ expected `i32`, found `()`
129
|
1310
= note: `if` expressions without `else` evaluate to `()`
14-
= note: consider adding an `else` block that evaluates to the expected type
11+
= help: consider adding an `else` block that evaluates to the expected type
1512

1613
error: aborting due to 1 previous error
1714

18-
For more information about this error, try `rustc --explain E0308`.
15+
For more information about this error, try `rustc --explain E0317`.

tests/ui/inference/deref-suggestion.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
macro_rules! borrow {
2-
($x:expr) => { &$x }
2+
($x:expr) => {
3+
&$x
4+
};
35
}
46

57
fn foo(_: String) {}
@@ -39,15 +41,14 @@ fn main() {
3941
let u = 3;
4042
let s = S { u };
4143
//~^ ERROR mismatched types
42-
let s = S { u: u };
44+
let s = S { u };
4345
//~^ ERROR mismatched types
4446
let i = &4;
4547
let r = R { i };
4648
//~^ ERROR mismatched types
47-
let r = R { i: i };
49+
let r = R { i };
4850
//~^ ERROR mismatched types
4951

50-
5152
let a = &1;
5253
let b = &2;
5354
let val: i32 = if true {
@@ -67,8 +68,8 @@ fn main() {
6768
let val = if true {
6869
*a
6970
} else if true {
70-
//~^ ERROR incompatible types
7171
b
72+
//~^ ERROR incompatible types
7273
} else {
7374
&0
7475
};
@@ -79,6 +80,6 @@ fn main() {
7980
let bar = &Foo;
8081

8182
if foo == bar {
82-
//~^ ERROR mismatched types
83+
//~^ ERROR mismatched types
8384
}
8485
}

0 commit comments

Comments
 (0)