@@ -95,9 +95,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
95
95
// So we have to check for them in this weird way...
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.
@@ -109,6 +115,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
109
115
}
110
116
}
111
117
118
+ let ( ty:: FnDef ( ..) | ty:: FnPtr ( ..) ) = ty. kind ( ) else {
119
+ self . report_nonfn_callee ( fn_span, self . thir [ fun] . span , ty) ;
120
+
121
+ // `fn_sig` below panics otherwise
122
+ return ;
123
+ } ;
124
+
112
125
// Erase regions since tail calls don't care about lifetimes
113
126
let callee_sig =
114
127
self . tcx . normalize_erasing_late_bound_regions ( self . typing_env , ty. fn_sig ( self . tcx ) ) ;
@@ -294,6 +307,40 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
294
307
self . found_errors = Err ( err) ;
295
308
}
296
309
310
+ fn report_nonfn_callee ( & mut self , call_sp : Span , fun_sp : Span , ty : Ty < ' _ > ) {
311
+ let mut err = self
312
+ . tcx
313
+ . dcx ( )
314
+ . struct_span_err (
315
+ call_sp,
316
+ "tail calls can only be performed with function definitions or pointers" ,
317
+ )
318
+ . with_note ( format ! ( "callee has type `{ty}`" ) ) ;
319
+
320
+ let mut ty = ty;
321
+ let mut refs = 0 ;
322
+ while ty. is_box ( ) || ty. is_ref ( ) {
323
+ ty = ty. builtin_deref ( false ) . unwrap ( ) ;
324
+ refs += 1 ;
325
+ }
326
+
327
+ if refs > 0 && ty. is_fn ( ) {
328
+ let thing = if ty. is_fn_ptr ( ) { "pointer" } else { "definition" } ;
329
+
330
+ let derefs =
331
+ std:: iter:: once ( '(' ) . chain ( std:: iter:: repeat_n ( '*' , refs) ) . collect :: < String > ( ) ;
332
+
333
+ err. multipart_suggestion (
334
+ format ! ( "consider dereferencing the expression to get a function {thing}" ) ,
335
+ vec ! [ ( fun_sp. shrink_to_lo( ) , derefs) , ( fun_sp. shrink_to_hi( ) , ")" . to_owned( ) ) ] ,
336
+ Applicability :: MachineApplicable ,
337
+ ) ;
338
+ }
339
+
340
+ let err = err. emit ( ) ;
341
+ self . found_errors = Err ( err) ;
342
+ }
343
+
297
344
fn report_abi_mismatch ( & mut self , sp : Span , caller_abi : ExternAbi , callee_abi : ExternAbi ) {
298
345
let err = self
299
346
. tcx
0 commit comments