If an interactive shell line is just a single loop and the body of the loop consists only of a function name, and the function body is not a list, ^C does not cause the loop to break as it otherwise would.
$ f() { cat; } $ while :; do f; done ^C The code in wait_for() checks the values of loop_level and executing_list: jobs.c:5095:55 -> 5095 if (signal_is_trapped (SIGINT) == 0 && (loop_level || (shell_compatibility_level > 32 && executing_list))) 5096 ADDINTERRUPT; but loop_level is reset by execute_function(): execute_cmd.c:5171:5 5170 if (shell_compatibility_level > 43) -> 5171 loop_level = 0; This code in wait_for seems to be the only place where the value of executing_list is tested, so maybe changing that variable to something like executing_list_or_loop and adding in/decrements of it in the loop-related execute_* functions would make sense.