It appears that the same initialized nameref variable may refer to variables in different scopes depending on the context where it is used. When used in an assignment, a nameref will look for the variable it references starting at the scope where the nameref itself was found, which may be below the current scope. In a parameter substitution a nameref will always start looking for its target in the current scope.
This leads to unintuitive behavior where a variable appears to not change its value after an assignment. Example: x=global foo() { declare -n xr=x ; bar; } bar() { local x=bar ; xr=new ; echo "after xr=new xr is $xr"; } foo echo "x at top level is $x" Output: > after xr=new xr is bar > x at top level is new This is with bash-5.1.8 and 5.1.16. It is completely unexpected that "xr=new; echo $xr" may print something other than "new". If this is intentional, I would be curious to know the rationale. It would also help a lot if you spelled out the nameref lookup rules in the man page. On a side note, my personal preference would be to have namerefs remember the scope of the variable that they reference (once that scope is known), and always resolve to that same SHELL_VAR, until unset, instead of looking up a SHELL_VAR by name, as is the current behavior. Among other things, this would solve the dreaded conflict between namerefs and dynamic scoping that has been discussed extensively on this list, most recently in https://lists.gnu.org/archive/html/bug-bash/2020-08/msg00206.html. I use namerefs extensively in a fairly large Bash code base for parameter passing, and I have to use fairly elaborate work-arounds to detect local variables shadowing outer-scope variables that the function operates on via namerefs. -Mark