mirror of
https://git.code.sf.net/p/zsh/code
synced 2026-04-18 06:53:35 -04:00
54261: unset -n removes named-reference type as well as removes referent
This commit is contained in:
committed by
Bart Schaefer
parent
aaff5eda66
commit
d8d74ef682
@@ -1,3 +1,9 @@
|
||||
2026-03-31 Bart Schaefer <schaefer@zsh.org>
|
||||
|
||||
* Philippe: 54261: Src/builtin.c, Src/params.c, Src/zsh.h,
|
||||
Test/K01nameref.ztst, Test/V10private.ztst: `unset -n` removes
|
||||
named-reference-ness of a parameter when removing the referent.
|
||||
|
||||
2026-03-30 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
|
||||
|
||||
* 54252: Src/Zle/compcore.c: allow single dot as a valid (yet
|
||||
|
||||
@@ -2038,6 +2038,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
|
||||
if ((pm = resolve_nameref(pm)))
|
||||
pname = pm->node.nam;
|
||||
if (pm && (pm->node.flags & PM_NAMEREF) &&
|
||||
(!(pm->node.flags & PM_UNSET) || (pm->node.flags & PM_DECLARED)) &&
|
||||
(on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) {
|
||||
/* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. *
|
||||
* Should this be a fatal error as well, rather than warning? */
|
||||
|
||||
63
Src/params.c
63
Src/params.c
@@ -1044,7 +1044,9 @@ createparam(char *name, int flags)
|
||||
if (oldpm && !(flags & PM_NAMEREF) &&
|
||||
(oldpm->level == locallevel ?
|
||||
!(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) &&
|
||||
(oldpm->node.flags & PM_NAMEREF)) {
|
||||
(oldpm->node.flags & PM_NAMEREF) &&
|
||||
(!(oldpm->node.flags & PM_UNSET) ||
|
||||
(oldpm->node.flags & PM_DECLARED))) {
|
||||
/**
|
||||
* Here we only have to deal with namerefs that refer to
|
||||
* not-yet-defined or unset variable. All other namerefs
|
||||
@@ -1054,14 +1056,18 @@ createparam(char *name, int flags)
|
||||
**/
|
||||
Param lastpm = resolve_nameref_rec(oldpm, NULL, 1);
|
||||
if (lastpm) {
|
||||
if (lastpm->node.flags & PM_NAMEREF) {
|
||||
if (lastpm->node.flags & PM_NAMEREF &&
|
||||
(!(lastpm->node.flags & PM_UNSET) ||
|
||||
(lastpm->node.flags & PM_DECLARED))) {
|
||||
char *refname = GETREFNAME(lastpm);
|
||||
if (refname && *refname) {
|
||||
/* nameref pointing to a not-yet-defined variable */
|
||||
name = refname;
|
||||
oldpm = NULL;
|
||||
} else {
|
||||
/* nameref pointing to an uninitialized nameref */
|
||||
if (!(lastpm->node.flags & PM_READONLY)) {
|
||||
if (flags) {
|
||||
if (flags & ~PM_LOCAL) {
|
||||
/* Only plain scalar assignment allowed */
|
||||
zerr("%s: can't change type of named reference",
|
||||
name); /* Differs from ksh93u+ */
|
||||
@@ -3386,6 +3392,10 @@ assignaparam(char *s, char **val, int flags)
|
||||
if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
|
||||
createparam(t, PM_ARRAY);
|
||||
created = 1;
|
||||
} else if (v->pm->node.flags & PM_NAMEREF) {
|
||||
zwarn("%s: can't change type of a named reference", t);
|
||||
unqueue_signals();
|
||||
return NULL;
|
||||
} else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
|
||||
!(v->valflags & VALFLAG_REFSLICE) &&
|
||||
!(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
|
||||
@@ -6321,35 +6331,30 @@ resolve_nameref(Param pm)
|
||||
static Param
|
||||
resolve_nameref_rec(Param pm, const Param stop, int keep_lastref)
|
||||
{
|
||||
Param hn = pm;
|
||||
if (pm && (pm->node.flags & PM_NAMEREF)) {
|
||||
char *refname = GETREFNAME(pm);
|
||||
if (pm->node.flags & PM_TAGGED) {
|
||||
zerr("%s: invalid self reference", pm->node.nam);
|
||||
return NULL;
|
||||
} else if (pm->node.flags & PM_UNSET) {
|
||||
/* Semaphore with createparam() */
|
||||
pm->node.flags &= ~PM_UNSET;
|
||||
return pm;
|
||||
}
|
||||
Param ref = pm;
|
||||
char *refname;
|
||||
if (!pm || !(pm->node.flags & PM_NAMEREF) || (pm->node.flags & PM_UNSET)
|
||||
/* pm->width is the offset of any subscript */
|
||||
/* If present, it has to be the end of any chain, see fetchvalue() */
|
||||
if (refname && *refname && !pm->width) {
|
||||
queue_signals();
|
||||
if ((hn = (Param)gethashnode2(realparamtab, refname))) {
|
||||
if ((hn = loadparamnode(paramtab, upscope(hn, pm), refname)) &&
|
||||
hn != stop && !(hn->node.flags & PM_UNSET)) {
|
||||
/* user can't tag a nameref, safe for loop detection */
|
||||
pm->node.flags |= PM_TAGGED;
|
||||
hn = resolve_nameref_rec(hn, stop, keep_lastref);
|
||||
pm->node.flags &= ~PM_TAGGED;
|
||||
}
|
||||
} else if (keep_lastref)
|
||||
hn = pm;
|
||||
unqueue_signals();
|
||||
}
|
||||
|| pm->width || !(refname = GETREFNAME(pm)) || !*refname)
|
||||
return pm;
|
||||
if (pm->node.flags & PM_TAGGED) {
|
||||
zerr("%s: invalid self reference", pm->node.nam);
|
||||
return NULL;
|
||||
}
|
||||
return hn;
|
||||
queue_signals();
|
||||
if ((pm = (Param)gethashnode2(realparamtab, refname))) {
|
||||
if ((pm = loadparamnode(paramtab, upscope(pm, ref), refname)) &&
|
||||
pm != stop && !(pm->node.flags & PM_UNSET)) {
|
||||
/* user can't tag a nameref, safe for loop detection */
|
||||
ref->node.flags |= PM_TAGGED;
|
||||
pm = resolve_nameref_rec(pm, stop, keep_lastref);
|
||||
ref->node.flags &= ~PM_TAGGED;
|
||||
}
|
||||
} else if (keep_lastref)
|
||||
pm = ref;
|
||||
unqueue_signals();
|
||||
return pm;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
@@ -1927,7 +1927,10 @@ struct tieddata {
|
||||
#define PM_DONTIMPORT (1<<22) /* do not import this variable */
|
||||
#define PM_DECLARED (1<<22) /* explicitly named with typeset */
|
||||
#define PM_RESTRICTED (1<<23) /* cannot be changed in restricted mode */
|
||||
#define PM_UNSET (1<<24) /* has null value */
|
||||
#define PM_UNSET (1<<24) /* If PM_DECLARED is also present, parameter
|
||||
* has null value. Otherwise, parameter was
|
||||
* unset.
|
||||
*/
|
||||
#define PM_DEFAULTED (PM_DECLARED|PM_UNSET)
|
||||
#define PM_REMOVABLE (1<<25) /* special can be removed from paramtab */
|
||||
#define PM_AUTOLOAD (1<<26) /* autoloaded from module */
|
||||
|
||||
@@ -132,6 +132,68 @@
|
||||
echo "$0: ref1=$ref1 ref2=$ref2 ref3=$ref3";
|
||||
}
|
||||
|
||||
# Helper function to test assigning and typesetting named references
|
||||
# in different contexts.
|
||||
#
|
||||
# Runs the code passed in "$1" for all combinations of the following
|
||||
# variables:
|
||||
#
|
||||
# - K: Kind of target reference. The assigned/typeset reference is
|
||||
# one of the following kind:
|
||||
# - "i": unInitialized reference:
|
||||
# e.g., "ref0" in "typeset -n ref0"
|
||||
# - "s": unSet reference:
|
||||
# e.g., "ref0" in "typeset -n ref0; unset ref0"
|
||||
# - "d": reference to not-yet-Defined variable:
|
||||
# e.g., "ref0" in "typeset -n ref0=var" where "var" isn't defined
|
||||
#
|
||||
# - G: Global vs local references
|
||||
# - "-g": All test variables/references are global ones.
|
||||
# - "" : All test variables/references are local ones.
|
||||
#
|
||||
# - N: direct vs indirect references
|
||||
# - "0": The target reference "ref0" is directly assigned/typeset.
|
||||
# - "1": The target reference "ref0" is assigned/typeset via a
|
||||
# named reference "ref1" that refers to "ref0".
|
||||
test-setting-ref() {
|
||||
local K G N;
|
||||
for K in i s d; do
|
||||
for G in -g ""; do
|
||||
for N in 0 1; do
|
||||
test-setting-ref-aux $1
|
||||
done
|
||||
done
|
||||
done
|
||||
unset -n ref0 ref1 var
|
||||
}
|
||||
# Helper function for the function test-setting-ref defined above.
|
||||
#
|
||||
# Runs the code passed in "$1" for the context specified by the
|
||||
# variables "K", "G", and "N".
|
||||
test-setting-ref-aux() {
|
||||
unset -n ref0 ref1 var
|
||||
# In several contexts different code paths are involved depending
|
||||
# on whether the target reference "ref0" is assigned/typeset
|
||||
# directly or via a named reference that refers to it. However, it
|
||||
# doesn't seem to make a difference whether the chain of references
|
||||
# is short or long. Therefore only chains of one reference are
|
||||
# tested. One can manually double check that longer chains work the
|
||||
# same by replacing the following line by one of the commented one
|
||||
# just below and checking that all tests still pass.
|
||||
((N < 1)) || typeset $G -n ref1=ref0
|
||||
# ((N < 1)) || typeset $G -n refX=ref0 ref1=refX
|
||||
# ((N < 1)) || typeset $G -n refX=ref0 refY=refX ref1=refY
|
||||
local kind print
|
||||
case $K in
|
||||
i) kind="uninitialized reference" ; check=ref0; typeset $G -n ref0;;
|
||||
s) kind="unset reference" ; check=ref0; typeset $G -n ref0; unset -n ref0;;
|
||||
d) kind="reference to not-yet-defined"; check=var ; typeset $G -n ref0=var;;
|
||||
*) echo "Unrecognized case: $case"; return;;
|
||||
esac
|
||||
echo "# $K:$kind - ${${G:-local}/-g/global} - ref$N"
|
||||
{ eval ${(e)1}; typeset -p $check } always { TRY_BLOCK_ERROR=0 } 2>&1
|
||||
}
|
||||
|
||||
%test
|
||||
|
||||
typeset -n ptr
|
||||
@@ -187,11 +249,13 @@
|
||||
>typeset -n ptr=gvar
|
||||
|
||||
typeset -n ptr
|
||||
typeset -p ptr
|
||||
typeset -t ptr
|
||||
typeset -p ptr
|
||||
0:change type of a placeholder
|
||||
F:Other type changes are fatal errors, should this also be?
|
||||
>typeset -n ptr=''
|
||||
>typeset -n ptr
|
||||
>typeset -n ptr
|
||||
*?*ptr: can't change type of a named reference
|
||||
|
||||
typeset -n ptr=var
|
||||
@@ -1659,4 +1723,166 @@ F:converting from association/array to string should work here too
|
||||
1:self reference via upper reference
|
||||
?(anon):3: ref1: invalid self reference
|
||||
|
||||
# The call to "test-setting-ref 'ref$N=str'" expands to the
|
||||
# equivalent of the following:
|
||||
#
|
||||
# # Test uninitialized references
|
||||
# typeset -g -n ref0 ; ref0=str; typeset -p ref0; unset -n ref0
|
||||
# typeset -g -n ref0 ref1=ref0; ref1=str; typeset -p ref0; unset -n ref0 ref1
|
||||
# typeset -n ref0 ; ref0=str; typeset -p ref0; unset -n ref0
|
||||
# typeset -n ref0 ref1=ref0; ref1=str; typeset -p ref0; unset -n ref0 ref1
|
||||
# # Test unset references
|
||||
# typeset -g -n ref0 ; unset -n ref0; ref0=str; typeset -p ref0; unset -n ref0
|
||||
# ...
|
||||
# # Test references to not-yet-defined variables
|
||||
# typeset -g -n ref0=var ; ref0=str; typeset -p var ; unset -n ref0 var
|
||||
# ...
|
||||
test-setting-ref 'ref$N=str'
|
||||
0:assign not fully defined reference with string
|
||||
># i:uninitialized reference - global - ref0
|
||||
>typeset -g -n ref0=str
|
||||
># i:uninitialized reference - global - ref1
|
||||
>typeset -g -n ref0=str
|
||||
># i:uninitialized reference - local - ref0
|
||||
>typeset -n ref0=str
|
||||
># i:uninitialized reference - local - ref1
|
||||
>typeset -n ref0=str
|
||||
># s:unset reference - global - ref0
|
||||
>typeset -g ref0=str
|
||||
># s:unset reference - global - ref1
|
||||
>typeset -g ref0=str
|
||||
># s:unset reference - local - ref0
|
||||
>typeset ref0=str
|
||||
># s:unset reference - local - ref1
|
||||
>typeset ref0=str
|
||||
># d:reference to not-yet-defined - global - ref0
|
||||
>typeset -g var=str
|
||||
># d:reference to not-yet-defined - global - ref1
|
||||
>typeset -g var=str
|
||||
># d:reference to not-yet-defined - local - ref0
|
||||
>typeset -g var=str
|
||||
># d:reference to not-yet-defined - local - ref1
|
||||
>typeset -g var=str
|
||||
|
||||
test-setting-ref 'ref$N=( foo bar )'
|
||||
0:assign not fully defined reference with array
|
||||
># i:uninitialized reference - global - ref0
|
||||
>(eval):1: ref0: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - global - ref1
|
||||
>(eval):1: ref1: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - local - ref0
|
||||
>(eval):1: ref0: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># i:uninitialized reference - local - ref1
|
||||
>(eval):1: ref1: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># s:unset reference - global - ref0
|
||||
>typeset -g -a ref0=( foo bar )
|
||||
># s:unset reference - global - ref1
|
||||
>typeset -g -a ref0=( foo bar )
|
||||
># s:unset reference - local - ref0
|
||||
>typeset -a ref0=( foo bar )
|
||||
># s:unset reference - local - ref1
|
||||
>typeset -a ref0=( foo bar )
|
||||
># d:reference to not-yet-defined - global - ref0
|
||||
>typeset -g -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - global - ref1
|
||||
>typeset -g -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - local - ref0
|
||||
>typeset -g -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - local - ref1
|
||||
>typeset -g -a var=( foo bar )
|
||||
|
||||
test-setting-ref 'typeset $G ref$N=str'
|
||||
0:typeset not fully defined reference with string
|
||||
># i:uninitialized reference - global - ref0
|
||||
>typeset -g -n ref0=str
|
||||
># i:uninitialized reference - global - ref1
|
||||
>typeset -g -n ref0=str
|
||||
># i:uninitialized reference - local - ref0
|
||||
>typeset -n ref0=str
|
||||
># i:uninitialized reference - local - ref1
|
||||
>typeset -n ref0=str
|
||||
># s:unset reference - global - ref0
|
||||
>typeset -g ref0=str
|
||||
># s:unset reference - global - ref1
|
||||
>typeset -g ref0=str
|
||||
># s:unset reference - local - ref0
|
||||
>typeset ref0=str
|
||||
># s:unset reference - local - ref1
|
||||
>typeset ref0=str
|
||||
># d:reference to not-yet-defined - global - ref0
|
||||
>typeset -g var=str
|
||||
># d:reference to not-yet-defined - global - ref1
|
||||
>typeset -g var=str
|
||||
># d:reference to not-yet-defined - local - ref0
|
||||
>typeset var=str
|
||||
># d:reference to not-yet-defined - local - ref1
|
||||
>typeset var=str
|
||||
|
||||
test-setting-ref 'typeset $G -a ref$N=( foo bar )'
|
||||
0:typeset not fully defined reference with array
|
||||
># i:uninitialized reference - global - ref0
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - global - ref1
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - local - ref0
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># i:uninitialized reference - local - ref1
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># s:unset reference - global - ref0
|
||||
>typeset -g -a ref0=( foo bar )
|
||||
># s:unset reference - global - ref1
|
||||
>typeset -g -a ref0=( foo bar )
|
||||
># s:unset reference - local - ref0
|
||||
>typeset -a ref0=( foo bar )
|
||||
># s:unset reference - local - ref1
|
||||
>typeset -a ref0=( foo bar )
|
||||
># d:reference to not-yet-defined - global - ref0
|
||||
>typeset -g -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - global - ref1
|
||||
>typeset -g -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - local - ref0
|
||||
>typeset -a var=( foo bar )
|
||||
># d:reference to not-yet-defined - local - ref1
|
||||
>typeset -a var=( foo bar )
|
||||
|
||||
typeset i42=42
|
||||
test-setting-ref 'typeset $G -i ref$N=i42'
|
||||
0:typeset not fully defined reference with integer
|
||||
># i:uninitialized reference - global - ref0
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - global - ref1
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -g -n ref0
|
||||
># i:uninitialized reference - local - ref0
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># i:uninitialized reference - local - ref1
|
||||
>(eval):typeset:1: ref0: can't change type of a named reference
|
||||
>typeset -n ref0
|
||||
># s:unset reference - global - ref0
|
||||
>typeset -g -i ref0=42
|
||||
># s:unset reference - global - ref1
|
||||
>typeset -g -i ref0=42
|
||||
># s:unset reference - local - ref0
|
||||
>typeset -i ref0=42
|
||||
># s:unset reference - local - ref1
|
||||
>typeset -i ref0=42
|
||||
># d:reference to not-yet-defined - global - ref0
|
||||
>typeset -g -i var=42
|
||||
># d:reference to not-yet-defined - global - ref1
|
||||
>typeset -g -i var=42
|
||||
># d:reference to not-yet-defined - local - ref0
|
||||
>typeset -i var=42
|
||||
># d:reference to not-yet-defined - local - ref1
|
||||
>typeset -i var=42
|
||||
|
||||
%clean
|
||||
|
||||
@@ -370,7 +370,7 @@ F:See K01nameref.ztst up-reference part 5
|
||||
F:Here ptr1 finds private ptr2 by scope mismatch
|
||||
>typeset -n ptr1=ptr2
|
||||
>typeset -hn ptr2
|
||||
*?*read-only variable: ptr2
|
||||
?(anon):1: read-only variable: ptr2
|
||||
|
||||
() {
|
||||
typeset -n ptr1=ptr2
|
||||
@@ -378,7 +378,7 @@ F:Here ptr1 finds private ptr2 by scope mismatch
|
||||
typeset -p ptr1 ptr2
|
||||
typeset val=LOCAL
|
||||
() {
|
||||
ptr1=val || print -u2 ptr1: assignment failed
|
||||
ptr1=val # Test dies here as ptr2 is private and uninitialized
|
||||
typeset -n
|
||||
printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2"
|
||||
}
|
||||
@@ -390,13 +390,7 @@ F:See K01nameref.ztst up-reference part 5
|
||||
F:Here ptr1 finds private ptr2 by scope mismatch
|
||||
>typeset -n ptr1=ptr2
|
||||
>typeset -hn ptr2=''
|
||||
>ptr1=ptr2
|
||||
>ptr1=
|
||||
>ptr2=
|
||||
>typeset -n ptr1=ptr2
|
||||
>typeset -hn ptr2=''
|
||||
*?*ptr1: assignment failed
|
||||
*?*no such variable: ptr2
|
||||
?(anon):1: ptr1: can't modify read-only parameter
|
||||
|
||||
typeset ptr2
|
||||
() {
|
||||
|
||||
Reference in New Issue
Block a user