@@ -95,16 +95,29 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
9595 if let & ty:: FnDef ( did, args) = ty. kind ( ) {
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.
104110 return ;
105111 } ;
106112 }
107113
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+
108121 // Erase regions since tail calls don't care about lifetimes
109122 let callee_sig =
110123 self . tcx . normalize_erasing_late_bound_regions ( self . typing_env , ty. fn_sig ( self . tcx ) ) ;
@@ -280,6 +293,40 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
280293 self . found_errors = Err ( err) ;
281294 }
282295
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+
283330 fn report_abi_mismatch ( & mut self , sp : Span , caller_abi : ExternAbi , callee_abi : ExternAbi ) {
284331 let err = self
285332 . tcx
0 commit comments