The distinction here is not where you place the -x, it's whether you specify a shell to use (either via a shebang, or by invoking the script explicitly with bash -x).
According to man bash, PS4:
PS4 The value of this parameter is expanded as with PS1 and the
value is printed before each command bash displays during an
execution trace. The first character of PS4 is replicated mul‐
tiple times, as necessary, to indicate multiple levels of indi‐
rection. The default is ``+ ''.
however it is not forthcoming about what "levels of indirection" means in this context.
When you try to execute a text file without specifying what interpreter to use, the system call to execve fails with ENOEXEC (Exec format error). What happens next depends on the shell from which you are trying to execute it, as described here:
In particular, if the invoking shell is bash, then the script appears to get executed directly by the invoking shell: this appears to be the "indirection" mentioned in the manual:
$ strace -e trace=%process -f bash -c 'echo "Starting $0"; ./hw_no_shebang; echo "Finished."'
execve("/bin/bash", ["bash", "-c", "echo \"Starting $0\"; ./hw_no_sheb"...], 0x7ffd3dadbee8 /* 25 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7f95fe939740) = 0
Starting bash
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f95fe939a10) = 26515
strace: Process 26515 attached
[pid 26514] wait4(-1, <unfinished ...>
[pid 26515] execve("./hw_no_shebang", ["./hw_no_shebang"], 0x555e5058a4f0 /* 25 vars */) = -1 ENOEXEC (Exec format error)
++ echo Hello
Hello
++ echo World
World
++ set +x
[pid 26515] exit_group(0) = ?
[pid 26515] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 26515
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26515, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7fffba5272d0, WNOHANG, NULL) = -1 ECHILD (No child processes)
Finished.
exit_group(0) = ?
+++ exited with 0 +++
Note that the bash -c in the strace call here takes the place of your interactive shell, rather than of the bash -x that you used in your last example.
As soon as we add a shebang, ex.:
$ cat hw_shebang
#!/bin/bash
set -x
echo "Hello"
echo "World"
set +x
then (regardless of whether -x is invoked using set or otherwise), then execve succeeds and the script is executed in its own shell, without "indirection":
$ strace -e trace=%process -f bash -c 'echo "Starting $0"; ./hw_shebang; echo "Finished."'
execve("/bin/bash", ["bash", "-c", "echo \"Starting $0\"; ./hw_shebang"...], 0x7ffccc0af338 /* 25 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7f81b9ed2740) = 0
Starting bash
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f81b9ed2a10) = 26553
strace: Process 26553 attached
[pid 26552] wait4(-1, <unfinished ...>
[pid 26553] execve("./hw_shebang", ["./hw_shebang"], 0x55879d9924f0 /* 25 vars */) = 0
[pid 26553] arch_prctl(ARCH_SET_FS, 0x7fa55de83740) = 0
+ echo Hello
Hello
+ echo World
World
+ set +x
[pid 26553] exit_group(0) = ?
[pid 26553] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 26553
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26553, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffda2983b10, WNOHANG, NULL) = -1 ECHILD (No child processes)
Finished.
exit_group(0) = ?
+++ exited with 0 +++
If you'd invoked the no-shebang script from dash for example, the behavior would be different: after the initial failed execve, it would have execve'd /bin/sh to execute the script:
$ strace -e trace=%process -f dash -c 'echo "Starting $0"; ./hw_no_shebang; echo "Finished."'
execve("/bin/dash", ["dash", "-c", "echo \"Starting $0\"; ./hw_no_sheb"...], 0x7ffc283d8638 /* 25 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7fd2cc787540) = 0
Starting dash
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd2cc787810) = 26590
strace: Process 26590 attached
[pid 26589] wait4(-1, <unfinished ...>
[pid 26590] execve("./hw_no_shebang", ["./hw_no_shebang"], 0x557143e3c8d8 /* 25 vars */) = -1 ENOEXEC (Exec format error)
[pid 26590] execve("/bin/sh", ["/bin/sh", "./hw_no_shebang"], 0x557143e3c8d8 /* 25 vars */) = 0
[pid 26590] arch_prctl(ARCH_SET_FS, 0x7f3b9aa14540) = 0
+ echo Hello
Hello
+ echo World
World
[pid 26590] exit_group(0) = ?
[pid 26590] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 26590
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26590, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Finished.
exit_group(0) = ?
+++ exited with 0 +++