The part of
bash's manual which applies to your case is this:
SIMPLE COMMAND EXPANSION
When a simple command is executed, the shell performs the following
expansions, assignments, and redirections, from left to right.
The words that the parser has marked as variable assignments
(those preceding the command name) and redirections are saved
for later processing.
The words that are not variable assignments or redirections are
expanded. If any words remain after expansion, the first word
is taken to be the name of the command and the remaining words
are the arguments.
Redirections are performed as described above under REDIRECTION.
The text after the = in each variable assignment undergoes tilde
expansion, parameter expansion, command substitution, arithmetic
expansion, and quote removal before being assigned to the variable.
$(echo "echo hi > /tmp/hi") command substitution will be expanded at point 2, and since the
> redirection couldn't have been marked for later processing at point 1 (since it only appeared as the result of the command substitution), it will not be performed as per point 3.
If the redirections weren't pre-processed before the expansions, then something like
to='>'; echo foo $to bar
foo into the file
bar, instead of just echoing
foo > bar as it does.
bash is different from other shells in the fact that it's also performing split + globbing on the word following a redirection operator (and die with "ambiguous redirect" if it expands to multiple words). For instance, this
var='a b'; echo > $var
will error out in
bash instead of clobbering a file named
a b (as do other shells).