Re: delcare -a on a nameref in a function modifies nameref instead of target

2018-07-29 Thread Grisha Levit
I think there's a related regression in the latest devel commit. Creating a
local variable with the same name as a higher-scoped nameref pointing to an
unset variable creates a local variable with the name of the target of the
nameref.

Starting with:

declare -n ref=var
f() { local ref=Y; declare -p ref var; local; }

This still works fine in both latest and previous:

$ var=X; f
declare -- ref="Y"
declare -- var="X"
ref=Y

But the behavior in the unset target case changed from the expected:

$ unset var; f
declare -- ref="Y"
bash: declare: var: not found
ref=Y

to:

$ unset var; f
declare -n ref="var"
declare -- var="Y"
var=Y



segfault with ${var[@]@A}

2018-07-29 Thread Grisha Levit
If `var' is declared but not set, performing an @A or @a
transformation on double-quoted `var[@]' or `var[*]' will trigger a
segfault in dequote_string.

This is present in 4.4 and devel.

bash -c 'declare var; echo "${var[@]@A}"'
Segmentation fault: 11

This seems like the easy fix:

diff --git a/subst.c b/subst.c
index dbb38fae..2ae4a1ec 100644
--- a/subst.c
+++ b/subst.c
@@ -7320,7 +7320,7 @@ get_var_and_type (varname, value, ind, quoted,
flags, varp, valp)
  vtype = VT_VARIABLE;
  *varp = v;
  if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
-   *valp = dequote_string (value);
+   *valp = value ? dequote_string (value) : savestring("");
  else
*valp = dequote_escapes (value);
}



Re: segfault w/ localvar_inherit and associative array insert

2018-07-29 Thread Grisha Levit
Though my motivation in reporting this was to point out the segfault, I'm a
bit confused by why compound assignment should be an error here.

> Bash is consistent in defaulting to indexed array variables when you don't
> specify -A and assign a value using the compound assignment syntax.

Since localvar_inherit is new behavior anyway, I'm not sure why the above must
be the controlling default. I think a less surprising-to-the-user default
might be "when this option is set, Bash consistently inherits the
previous-scope variable's value/attributes before performing any new
value/attribute modifications".

In the alternative, it seems that Bash should be consistent in also
defaulting to indexed arrays in the case of `array[subscript]' assignments
with no -A specified?

i.e. if the following should be an error:

$ declare -A A; f() { local A=([Y]=Y); declare -p A; }; f
Segmentation fault: 11

should the following produce a local indexed array/an error as well (instead
of the associative array assignment it currently does)?

$ declare -A A; f() { local A[Y]=Y; declare -p A; }; f
declare -A A=([Y]="Y" )



> So you create a local indexed array variable, and when you attempt to
> inherit the value from a global associative array before performing any
> specified assignment, you should either ignore the inherited value or
> throw an error.

I feel like this doesn't seem very apparent from the documentation (though
it's not strictly contradictory, the step where we first check the RHS to see
if it is a compound array assignment seems surprising):

  localvar_inherit
If set, local variables inherit the value and attributes of a variable
of the same name that exists at a previous scope *before any new value
is assigned*.



There's also an issue that only a value of a matching type is inherited and
the usual scalar-to-array[0] conversion is not performed. I'm not sure if
this is intentional but I do want to point it out as potentially surprising:

$ unset a s; a=(X) s=X
$ shopt -s localvar_inherit

$ f() { local -a a s; declare -p a s; }; f
declare -a a=([0]="X")
declare -a s=()

$ f() { local a+=(Y) s+=(Y); declare -p a s; }; f
declare -a a=([0]="X" [1]="Y")
declare -a s=([0]="Y")

$ f() { local -a a+=Y s+=Y; declare -p a s; }; f
declare -a a=([0]="XY")
declare -a s=([0]="Y")



Maybe I'm missing the intended use of the option but based on the description
in the man page, I guess I'm expecting `declare var' with localvar_inherit to
be roughly the same as `eval $(declare -p var 2>/dev/null); declare var'
without it (except for namerefs). Is there maybe a use case for the
differences in behavior?

As a side note: I wonder if this functionality, regardless or edge case
handling, would be more useful as an option supplied to `declare' and so
applied explicitly so specific variables, rather than a global shell option.