@@ -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+
3237const 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
0 commit comments