Skip to content

Commit d8813e1

Browse files
committed
needs_help: wip fix 141690
1 parent 9642c0e commit d8813e1

31 files changed

+443
-130
lines changed
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
use rustc_errors::Diag;
2+
use rustc_hir::{self as hir, HirId};
3+
use rustc_infer::traits;
4+
use rustc_middle::ty::{Ty, TypeVisitableExt};
5+
use rustc_span::{ErrorGuaranteed, Span};
6+
use smallvec::SmallVec;
7+
8+
use crate::coercion::{CoerceMany, DynamicCoerceMany};
9+
use crate::{Diverges, Expectation, FnCtxt, bug};
10+
11+
#[derive(Clone, Debug)]
12+
struct BranchBody<'tcx> {
13+
expr: &'tcx hir::Expr<'tcx>,
14+
ty: Ty<'tcx>,
15+
diverges: Diverges,
16+
span: Span,
17+
}
18+
19+
#[derive(Clone, Debug)]
20+
struct IfBranch<'tcx> {
21+
if_expr: &'tcx hir::Expr<'tcx>,
22+
cond_diverges: Diverges,
23+
body: BranchBody<'tcx>,
24+
}
25+
26+
#[derive(Default, Debug)]
27+
struct IfChain<'tcx> {
28+
branches: SmallVec<[IfBranch<'tcx>; 4]>,
29+
cond_error: Option<ErrorGuaranteed>,
30+
}
31+
32+
const RECENT_BRANCH_HISTORY_LIMIT: usize = 5;
33+
34+
#[derive(Default)]
35+
struct RecentBranchTypeHistory<'tcx> {
36+
entries: SmallVec<[(Ty<'tcx>, Span); 4]>,
37+
}
38+
39+
impl<'tcx> RecentBranchTypeHistory<'tcx> {
40+
fn diagnostic_snapshot(&self) -> SmallVec<[(Ty<'tcx>, Span); 4]> {
41+
self.entries.clone()
42+
}
43+
44+
fn record(&mut self, ty: Ty<'tcx>, span: Span) {
45+
if ty.is_never() {
46+
return;
47+
}
48+
49+
self.entries.push((ty, span));
50+
if self.entries.len() > RECENT_BRANCH_HISTORY_LIMIT {
51+
self.entries.remove(0);
52+
}
53+
}
54+
}
55+
56+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
57+
pub(crate) fn check_expr_if(
58+
&self,
59+
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>>,
63+
sp: Span,
64+
orig_expected: Expectation<'tcx>,
65+
) -> Ty<'tcx> {
66+
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+
80+
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();
147+
148+
let initial_diverges = self.diverges.get();
149+
let terminal_else = self.collect_if_chain(root_if_expr, expected, &mut chain);
150+
151+
let Some(else_branch) = terminal_else else {
152+
bug!("sequential `if` chain expected a final `else` arm");
153+
};
154+
155+
let coerce_to_ty = expected.coercion_target_type(self, sp);
156+
let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty);
157+
158+
let tail_defines_return_position_impl_trait =
159+
self.return_position_impl_trait_from_match_expectation(orig_expected);
160+
let mut recent_branch_types = RecentBranchTypeHistory::default();
161+
162+
for (idx, branch) in chain.branches.iter().enumerate() {
163+
if idx > 0 {
164+
let merged_ty = coerce.merged_ty();
165+
self.ensure_if_branch_type(branch.if_expr.hir_id, merged_ty);
166+
}
167+
168+
let branch_body = &branch.body;
169+
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+
);
176+
let diag_info = recent_branch_types.diagnostic_snapshot();
177+
self.coerce_if_arm(
178+
&mut coerce,
179+
&mut branch_cause,
180+
branch_body.expr,
181+
branch_body.ty,
182+
branch_body.span,
183+
diag_info,
184+
);
185+
186+
recent_branch_types.record(branch_body.ty, branch_body.span);
187+
}
188+
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);
201+
202+
let mut tail_diverges = else_branch.diverges;
203+
for branch in chain.branches.iter().rev() {
204+
tail_diverges = branch.cond_diverges | (branch.body.diverges & tail_diverges);
205+
}
206+
self.diverges.set(initial_diverges | tail_diverges);
207+
208+
let result_ty = coerce.complete(self);
209+
210+
let final_ty = if let Some(guar) = chain.cond_error {
211+
Ty::new_error(self.tcx, guar)
212+
} else {
213+
result_ty
214+
};
215+
216+
for branch in chain.branches.iter().skip(1) {
217+
self.overwrite_if_branch_type(branch.if_expr.hir_id, final_ty);
218+
}
219+
if let Err(guar) = final_ty.error_reported() {
220+
self.set_tainted_by_errors(guar);
221+
}
222+
223+
final_ty
224+
}
225+
226+
fn coerce_if_arm(
227+
&self,
228+
coerce: &mut DynamicCoerceMany<'tcx>,
229+
cause: &mut traits::ObligationCause<'tcx>,
230+
expr: &'tcx hir::Expr<'tcx>,
231+
ty: Ty<'tcx>,
232+
span: Span,
233+
prior_branches: SmallVec<[(Ty<'tcx>, Span); 4]>,
234+
) {
235+
cause.span = span;
236+
coerce.coerce_inner(
237+
self,
238+
cause,
239+
Some(expr),
240+
ty,
241+
move |err| self.explain_if_branch_mismatch(err, span, &prior_branches),
242+
false,
243+
);
244+
}
245+
246+
fn check_if_condition(
247+
&self,
248+
cond_expr: &'tcx hir::Expr<'tcx>,
249+
then_span: Span,
250+
) -> (Ty<'tcx>, Diverges) {
251+
let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {});
252+
self.warn_if_unreachable(
253+
cond_expr.hir_id,
254+
then_span,
255+
"block in `if` or `while` expression",
256+
);
257+
let cond_diverges = self.take_diverges();
258+
(cond_ty, cond_diverges)
259+
}
260+
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+
273+
fn collect_if_chain(
274+
&self,
275+
mut current_if: &'tcx hir::Expr<'tcx>,
276+
expected: Expectation<'tcx>,
277+
chain: &mut IfChain<'tcx>,
278+
) -> Option<BranchBody<'tcx>> {
279+
loop {
280+
let Some(else_expr) = self.collect_if_branch(current_if, expected, chain) else {
281+
return None;
282+
};
283+
284+
if let hir::ExprKind::If(..) = else_expr.kind {
285+
current_if = else_expr;
286+
continue;
287+
}
288+
289+
return Some(self.collect_final_else(else_expr, expected));
290+
}
291+
}
292+
293+
fn collect_if_branch(
294+
&self,
295+
if_expr: &'tcx hir::Expr<'tcx>,
296+
expected: Expectation<'tcx>,
297+
chain: &mut IfChain<'tcx>,
298+
) -> Option<&'tcx hir::Expr<'tcx>> {
299+
let hir::ExprKind::If(cond_expr, then_expr, opt_else_expr) = if_expr.kind else {
300+
bug!("expected `if` expression, found {:#?}", if_expr);
301+
};
302+
303+
let (cond_ty, cond_diverges) = self.check_if_condition(cond_expr, then_expr.span);
304+
if let Err(guar) = cond_ty.error_reported() {
305+
chain.cond_error.get_or_insert(guar);
306+
}
307+
let branch_body = self.check_branch_body(then_expr, expected);
308+
309+
chain.branches.push(IfBranch { if_expr, cond_diverges, body: branch_body });
310+
311+
opt_else_expr
312+
}
313+
314+
fn collect_final_else(
315+
&self,
316+
else_expr: &'tcx hir::Expr<'tcx>,
317+
expected: Expectation<'tcx>,
318+
) -> BranchBody<'tcx> {
319+
self.check_branch_body(else_expr, expected)
320+
}
321+
322+
fn reset_diverges_to_maybe(&self) {
323+
self.diverges.set(Diverges::Maybe);
324+
}
325+
326+
fn take_diverges(&self) -> Diverges {
327+
let diverges = self.diverges.get();
328+
self.reset_diverges_to_maybe();
329+
diverges
330+
}
331+
332+
fn ensure_if_branch_type(&self, hir_id: HirId, ty: Ty<'tcx>) {
333+
let mut typeck = self.typeck_results.borrow_mut();
334+
let mut node_ty = typeck.node_types_mut();
335+
node_ty.entry(hir_id).or_insert(ty);
336+
}
337+
338+
fn overwrite_if_branch_type(&self, hir_id: HirId, ty: Ty<'tcx>) {
339+
let mut typeck = self.typeck_results.borrow_mut();
340+
let mut node_ty = typeck.node_types_mut();
341+
node_ty.insert(hir_id, ty);
342+
}
343+
344+
fn check_branch_body(
345+
&self,
346+
expr: &'tcx hir::Expr<'tcx>,
347+
expected: Expectation<'tcx>,
348+
) -> BranchBody<'tcx> {
349+
self.reset_diverges_to_maybe();
350+
let ty = self.check_expr_with_expectation(expr, expected);
351+
let diverges = self.take_diverges();
352+
let span = self.find_block_span_from_hir_id(expr.hir_id);
353+
BranchBody { expr, ty, diverges, span }
354+
}
355+
356+
fn explain_if_branch_mismatch(
357+
&self,
358+
err: &mut Diag<'_>,
359+
branch_span: Span,
360+
prior_branches: &[(Ty<'tcx>, Span)],
361+
) {
362+
let Some(&(prior_ty, prior_span)) =
363+
prior_branches.iter().rev().find(|&&(_, span)| span != branch_span)
364+
else {
365+
return;
366+
};
367+
368+
let expected_ty = self.resolve_vars_if_possible(prior_ty);
369+
err.span_label(
370+
prior_span,
371+
format!("expected `{}` because of this", self.ty_to_string(expected_ty)),
372+
);
373+
}
374+
}

0 commit comments

Comments
 (0)