@@ -95,16 +95,29 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
95
95
if let & ty:: FnDef ( did, args) = ty. kind ( ) {
96
96
let parent = self . tcx . parent ( did) ;
97
97
if self . tcx . fn_trait_kind_from_def_id ( parent) . is_some ( )
98
- && args. first ( ) . and_then ( |arg| arg. as_type ( ) ) . is_some_and ( Ty :: is_closure)
98
+ && let Some ( this) = args. first ( )
99
+ && let Some ( this) = this. as_type ( )
99
100
{
100
- self . report_calling_closure ( & self . thir [ fun] , args[ 1 ] . as_type ( ) . unwrap ( ) , expr) ;
101
+ if this. is_closure ( ) {
102
+ self . report_calling_closure ( & self . thir [ fun] , args[ 1 ] . as_type ( ) . unwrap ( ) , expr) ;
103
+ } else {
104
+ // This can happen when tail calling `Box` that wraps a function
105
+ self . report_nonfn_callee ( fn_span, self . thir [ fun] . span , this) ;
106
+ }
101
107
102
108
// Tail calling is likely to cause unrelated errors (ABI, argument mismatches),
103
109
// skip them, producing an error about calling a closure is enough.
104
110
return ;
105
111
} ;
106
112
}
107
113
114
+ let ( ty:: FnDef ( ..) | ty:: FnPtr ( ..) ) = ty. kind ( ) else {
115
+ self . report_nonfn_callee ( fn_span, self . thir [ fun] . span , ty) ;
116
+
117
+ // `fn_sig` below panics otherwise
118
+ return ;
119
+ } ;
120
+
108
121
// Erase regions since tail calls don't care about lifetimes
109
122
let callee_sig =
110
123
self . tcx . normalize_erasing_late_bound_regions ( self . typing_env , ty. fn_sig ( self . tcx ) ) ;
@@ -280,6 +293,40 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
280
293
self . found_errors = Err ( err) ;
281
294
}
282
295
296
+ fn report_nonfn_callee ( & mut self , call_sp : Span , fun_sp : Span , ty : Ty < ' _ > ) {
297
+ let mut err = self
298
+ . tcx
299
+ . dcx ( )
300
+ . struct_span_err (
301
+ call_sp,
302
+ "tail calls can only be performed with function definitions or pointers" ,
303
+ )
304
+ . with_note ( format ! ( "callee has type `{ty}`" ) ) ;
305
+
306
+ let mut ty = ty;
307
+ let mut refs = 0 ;
308
+ while ty. is_box ( ) || ty. is_ref ( ) {
309
+ ty = ty. builtin_deref ( false ) . unwrap ( ) ;
310
+ refs += 1 ;
311
+ }
312
+
313
+ if refs > 0 && ty. is_fn ( ) {
314
+ let thing = if ty. is_fn_ptr ( ) { "pointer" } else { "definition" } ;
315
+
316
+ let derefs =
317
+ std:: iter:: once ( '(' ) . chain ( std:: iter:: repeat_n ( '*' , refs) ) . collect :: < String > ( ) ;
318
+
319
+ err. multipart_suggestion (
320
+ format ! ( "consider dereferencing the expression to get a function {thing}" ) ,
321
+ vec ! [ ( fun_sp. shrink_to_lo( ) , derefs) , ( fun_sp. shrink_to_hi( ) , ")" . to_owned( ) ) ] ,
322
+ Applicability :: MachineApplicable ,
323
+ ) ;
324
+ }
325
+
326
+ let err = err. emit ( ) ;
327
+ self . found_errors = Err ( err) ;
328
+ }
329
+
283
330
fn report_abi_mismatch ( & mut self , sp : Span , caller_abi : ExternAbi , callee_abi : ExternAbi ) {
284
331
let err = self
285
332
. tcx
0 commit comments