@@ -95,9 +95,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
9595 // So we have to check for them in this weird way...
9696 let parent = self . tcx . parent ( did) ;
9797 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 ( )
99100 {
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+ }
101107
102108 // Tail calling is likely to cause unrelated errors (ABI, argument mismatches),
103109 // skip them, producing an error about calling a closure is enough.
@@ -109,6 +115,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
109115 }
110116 }
111117
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+
112125 // Erase regions since tail calls don't care about lifetimes
113126 let callee_sig =
114127 self . tcx . normalize_erasing_late_bound_regions ( self . typing_env , ty. fn_sig ( self . tcx ) ) ;
@@ -294,6 +307,40 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
294307 self . found_errors = Err ( err) ;
295308 }
296309
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+
297344 fn report_abi_mismatch ( & mut self , sp : Span , caller_abi : ExternAbi , callee_abi : ExternAbi ) {
298345 let err = self
299346 . tcx
0 commit comments