1
1
use crate :: check:: coercion:: CoerceMany ;
2
2
use crate :: check:: { Diverges , Expectation , FnCtxt , Needs } ;
3
- use rustc_hir as hir;
4
- use rustc_hir:: ExprKind ;
3
+ use rustc_hir:: { self as hir, ExprKind } ;
5
4
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
6
- use rustc_middle:: ty:: Ty ;
5
+ use rustc_infer:: traits:: Obligation ;
6
+ use rustc_middle:: ty:: { self , ToPredicate , Ty } ;
7
7
use rustc_span:: Span ;
8
- use rustc_trait_selection:: traits:: ObligationCauseCode ;
9
- use rustc_trait_selection:: traits:: { IfExpressionCause , MatchExpressionArmCause , ObligationCause } ;
8
+ use rustc_trait_selection:: opaque_types:: InferCtxtExt as _;
9
+ use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
10
+ use rustc_trait_selection:: traits:: {
11
+ IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
12
+ } ;
10
13
11
14
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
12
15
pub fn check_match (
13
16
& self ,
14
17
expr : & ' tcx hir:: Expr < ' tcx > ,
15
18
scrut : & ' tcx hir:: Expr < ' tcx > ,
16
19
arms : & ' tcx [ hir:: Arm < ' tcx > ] ,
17
- expected : Expectation < ' tcx > ,
20
+ orig_expected : Expectation < ' tcx > ,
18
21
match_src : hir:: MatchSource ,
19
22
) -> Ty < ' tcx > {
20
23
let tcx = self . tcx ;
21
24
22
25
use hir:: MatchSource :: * ;
23
26
let ( source_if, if_no_else, force_scrutinee_bool) = match match_src {
24
27
IfDesugar { contains_else_clause } => ( true , !contains_else_clause, true ) ,
25
- IfLetDesugar { contains_else_clause } => ( true , !contains_else_clause, false ) ,
28
+ IfLetDesugar { contains_else_clause, .. } => ( true , !contains_else_clause, false ) ,
26
29
WhileDesugar => ( false , false , true ) ,
27
30
_ => ( false , false , false ) ,
28
31
} ;
@@ -69,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
69
72
// type in that case)
70
73
let mut all_arms_diverge = Diverges :: WarnedAlways ;
71
74
72
- let expected = expected . adjust_for_branches ( self ) ;
75
+ let expected = orig_expected . adjust_for_branches ( self ) ;
73
76
74
77
let mut coercion = {
75
78
let coerce_first = match expected {
@@ -112,14 +115,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
112
115
self . check_expr_with_expectation ( & arm. body , expected)
113
116
} ;
114
117
all_arms_diverge &= self . diverges . get ( ) ;
118
+
119
+ // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
120
+ // we check if the different arms would work with boxed trait objects instead and
121
+ // provide a structured suggestion in that case.
122
+ let opt_suggest_box_span = match (
123
+ orig_expected,
124
+ self . ret_coercion_impl_trait . map ( |ty| ( self . body_id . owner , ty) ) ,
125
+ ) {
126
+ ( Expectation :: ExpectHasType ( expected) , Some ( ( id, ty) ) )
127
+ if self . in_tail_expr && self . can_coerce ( arm_ty, expected) =>
128
+ {
129
+ let impl_trait_ret_ty = self . infcx . instantiate_opaque_types (
130
+ id,
131
+ self . body_id ,
132
+ self . param_env ,
133
+ & ty,
134
+ arm. body . span ,
135
+ ) ;
136
+ let mut suggest_box = !impl_trait_ret_ty. obligations . is_empty ( ) ;
137
+ for o in impl_trait_ret_ty. obligations {
138
+ match o. predicate . skip_binders_unchecked ( ) {
139
+ ty:: PredicateAtom :: Trait ( t, constness) => {
140
+ let pred = ty:: PredicateAtom :: Trait (
141
+ ty:: TraitPredicate {
142
+ trait_ref : ty:: TraitRef {
143
+ def_id : t. def_id ( ) ,
144
+ substs : self . infcx . tcx . mk_substs_trait ( arm_ty, & [ ] ) ,
145
+ } ,
146
+ } ,
147
+ constness,
148
+ ) ;
149
+ let obl = Obligation :: new (
150
+ o. cause . clone ( ) ,
151
+ self . param_env ,
152
+ pred. to_predicate ( self . infcx . tcx ) ,
153
+ ) ;
154
+ suggest_box &= self . infcx . predicate_must_hold_modulo_regions ( & obl) ;
155
+ if !suggest_box {
156
+ // We've encountered some obligation that didn't hold, so the
157
+ // return expression can't just be boxed. We don't need to
158
+ // evaluate the rest of the obligations.
159
+ break ;
160
+ }
161
+ }
162
+ _ => { }
163
+ }
164
+ }
165
+ // If all the obligations hold (or there are no obligations) the tail expression
166
+ // we can suggest to return a boxed trait object instead of an opaque type.
167
+ if suggest_box { self . ret_type_span . clone ( ) } else { None }
168
+ }
169
+ _ => None ,
170
+ } ;
171
+
115
172
if source_if {
116
173
let then_expr = & arms[ 0 ] . body ;
117
174
match ( i, if_no_else) {
118
175
( 0 , _) => coercion. coerce ( self , & self . misc ( expr. span ) , & arm. body , arm_ty) ,
119
176
( _, true ) => { } // Handled above to avoid duplicated type errors (#60254).
120
177
( _, _) => {
121
178
let then_ty = prior_arm_ty. unwrap ( ) ;
122
- let cause = self . if_cause ( expr. span , then_expr, & arm. body , then_ty, arm_ty) ;
179
+ let cause = self . if_cause (
180
+ expr. span ,
181
+ then_expr,
182
+ & arm. body ,
183
+ then_ty,
184
+ arm_ty,
185
+ opt_suggest_box_span,
186
+ ) ;
123
187
coercion. coerce ( self , & cause, & arm. body , arm_ty) ;
124
188
}
125
189
}
@@ -142,6 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
142
206
prior_arms : other_arms. clone ( ) ,
143
207
last_ty : prior_arm_ty. unwrap ( ) ,
144
208
scrut_hir_id : scrut. hir_id ,
209
+ opt_suggest_box_span,
145
210
} ) ,
146
211
) ,
147
212
} ;
@@ -266,6 +331,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
266
331
else_expr : & ' tcx hir:: Expr < ' tcx > ,
267
332
then_ty : Ty < ' tcx > ,
268
333
else_ty : Ty < ' tcx > ,
334
+ opt_suggest_box_span : Option < Span > ,
269
335
) -> ObligationCause < ' tcx > {
270
336
let mut outer_sp = if self . tcx . sess . source_map ( ) . is_multiline ( span) {
271
337
// The `if`/`else` isn't in one line in the output, include some context to make it
@@ -353,8 +419,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
353
419
error_sp,
354
420
ObligationCauseCode :: IfExpression ( box IfExpressionCause {
355
421
then : then_sp,
422
+ else_sp : error_sp,
356
423
outer : outer_sp,
357
424
semicolon : remove_semicolon,
425
+ opt_suggest_box_span,
358
426
} ) ,
359
427
)
360
428
}
0 commit comments