Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: Fix loading of autoload variable accessed via a reference
- X-seq: zsh-workers 53781
- From: Philippe Altherr <philippe.altherr@xxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Cc: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- Subject: PATCH: Fix loading of autoload variable accessed via a reference
- Date: Fri, 13 Jun 2025 14:14:43 +0200
- Archived-at: <https://zsh.org/workers/53781>
- List-id: <zsh-workers.zsh.org>
When a reference to an autoload variable is defined, the autoload variable should be loaded. The autoload variable should also be loaded whenever such a reference is dereferenced. The latter is currently failing if the reference includes a subscript. The latter is also failing and produces bogus results when the autoload variable is hidden by a local variable. In this case it fails both with and without subscript.
The patch fixes these issues. It introduces a new loadparamnode function, which is a more sensible alternative to the getparamnode_nofollow function introduced in the original patches for setscope (
workers/53688).
Below are 3 examples that exhibit the issues:
Example |
zmodload -u zsh/random echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*} typeset -n ref=SRANDOM[1,20] echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*} echo v=${ref/<->/integer} zmodload -u zsh/random echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*} echo v=${ref/<->/integer} |
Current output | Expected output |
z=SRANDOM (zsh/random) z= v=integer z=SRANDOM (zsh/random) v=zsh/random | z=SRANDOM (zsh/random) z= v=integer z=SRANDOM (zsh/random) v=integer |
Example |
() { typeset -n ref=SRANDOM echo v=${ref/<->/integer} zmodload -u zsh/random echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*} typeset -h SRANDOM=local-variable echo v=${ref/<->/integer} echo NOT REACHED } |
Current output | Expected output |
v=integer z=SRANDOM (zsh/random) v=local-variable NOT REACHED | v=integer z=SRANDOM (zsh/random) <error-message> |
Example |
() { typeset -n ref=SRANDOM[1,20] echo v=${ref/<->/integer} zmodload -u zsh/random echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*} typeset -h SRANDOM=local-variable echo v=${ref/<->/integer} echo NOT REACHED } |
Current output | Expected output |
v=integer z=SRANDOM (zsh/random) v=zsh/random NOT REACHED | v=integer z=SRANDOM (zsh/random) <error-message> |
Philippe
diff --git a/Src/params.c b/Src/params.c
index 7b515515e..70042a417 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -525,18 +525,18 @@ newparamtable(int size, char const *name)
/**/
static HashNode
-getparamnode(HashTable ht, const char *nam)
+loadparamnode(HashTable ht, Param pm, const char *nam)
{
- HashNode hn = gethashnode2(ht, nam);
- Param pm = (Param) hn;
-
if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) {
+ int level = pm->level;
char *mn = dupstring(pm->u.str);
-
- (void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
- nam);
- hn = gethashnode2(ht, nam);
- if (!hn) {
+ (void)ensurefeature(mn, "p:", nam);
+ pm = (Param)gethashnode2(ht, nam);
+ while (pm && pm->level > level)
+ pm = pm->old;
+ if (pm && (pm->level != level || (pm->node.flags & PM_AUTOLOAD)))
+ pm = NULL;
+ if (!pm) {
/*
* This used to be a warning, but surely if we allow
* stuff to go ahead with the autoload stub with
@@ -546,7 +546,14 @@ getparamnode(HashTable ht, const char *nam)
nam);
}
}
+ return (HashNode)pm;
+}
+/**/
+static HashNode
+getparamnode(HashTable ht, const char *nam)
+{
+ HashNode hn = loadparamnode(ht, (Param)gethashnode2(ht, nam), nam);
if (hn && ht == realparamtab && !(hn->flags & PM_UNSET))
hn = resolve_nameref((Param)hn, NULL);
return hn;
@@ -2236,6 +2243,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
((pm->node.flags & PM_UPPER) ? -(pm->base) :
pm->base) : locallevel);
pm = upscope(p1, scope);
+ pm = (Param)loadparamnode(paramtab, pm, ref);
}
if (!(p1 && pm) ||
((pm->node.flags & PM_UNSET) &&
@@ -6313,12 +6321,11 @@ resolve_nameref(Param pm, const Asgment stop)
pm->base) : ((Param)hn)->level);
hn = (HashNode)upscope((Param)hn, scope);
}
+ hn = loadparamnode(paramtab, (Param)hn, seek);
/* user can't tag a nameref, safe for loop detection */
pm->node.flags |= PM_TAGGED;
}
if (hn) {
- if (hn->flags & PM_AUTOLOAD)
- hn = getparamnode(realparamtab, seek);
if (!(hn->flags & PM_UNSET))
hn = resolve_nameref((Param)hn, stop);
}
diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst
index 914eea92b..7cca2bd81 100644
--- a/Test/B02typeset.ztst
+++ b/Test/B02typeset.ztst
@@ -1120,3 +1120,60 @@
1:Regression test for {...} parsing in typeset
?(eval):typeset:2: not valid in this context: {X}
?(eval):typeset:3: not valid in this context: {X}
+
+ zmodload -u zsh/random
+ echo v=${SRANDOM/<->/integer}
+ typeset SRANDOM
+ echo v=${SRANDOM/<->/integer}
+0:Global non -h variable doesn't hide special variable
+>v=integer
+>v=integer
+
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -g SRANDOM
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${SRANDOM/<->/integer}
+0:Global non -h variable doesn't hide autoload variable
+>z=SRANDOM (zsh/random)
+>z=SRANDOM (zsh/random)
+>v=integer
+
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -gh SRANDOM
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${SRANDOM/<->/integer}
+0:Global -h variable doesn't hide autoload variable
+>z=SRANDOM (zsh/random)
+>z=SRANDOM (zsh/random)
+>v=integer
+
+ zmodload -u zsh/random
+ echo v=${SRANDOM/<->/integer}
+ typeset SRANDOM
+ echo v=${SRANDOM/<->/integer}
+0:Local non -h variable doesn't hide special variable
+>v=integer
+>v=integer
+
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset SRANDOM
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${SRANDOM/<->/integer}
+0:Local non -h variable hides autoload variable
+F:This is a bug, the non -h variable should not hide the autoload variable
+>z=SRANDOM (zsh/random)
+>z=
+>v=
+
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -h SRANDOM
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${SRANDOM/<->/integer}
+0:Local -h variable hides autoload variable
+>z=SRANDOM (zsh/random)
+>z=
+>v=
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index 54f0aaf68..52aed0af0 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -1179,4 +1179,62 @@ F:previously this could create an infinite recursion and crash
>typeset PS1=zz
*?*
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -n ref=SRANDOM
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${ref/<->/integer}
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${ref/<->/integer}
+0:Referring and dereferring an autoload variable loads it (direct)
+>z=SRANDOM (zsh/random)
+>z=
+>v=integer
+>z=SRANDOM (zsh/random)
+>v=integer
+
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -n ref=SRANDOM[1,20]
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${ref/<->/integer}
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ echo v=${ref/<->/integer}
+0:Referring and dereferring an autoload variable loads it (subscript)
+>z=SRANDOM (zsh/random)
+>z=
+>v=integer
+>z=SRANDOM (zsh/random)
+>v=integer
+
+ typeset -n ref=SRANDOM
+ echo v=${ref/<->/integer}
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -h SRANDOM=local-variable
+ echo v=${ref/<->/integer}
+ echo NOT REACHED
+1:Dereferring an autoload variable fails to load it if its hidden (direct)
+>v=integer
+>z=SRANDOM (zsh/random)
+?(eval):6: Can't add module parameter `SRANDOM': local parameter exists
+?(eval):zsh/random:6: error when adding parameter `SRANDOM'
+?(eval):6: autoloading module zsh/random failed to define parameter: SRANDOM
+
+ typeset -n ref=SRANDOM[1,20]
+ echo v=${ref/<->/integer}
+ zmodload -u zsh/random
+ echo z=${(M)${(f)${ zmodload -ap}}:#*SRANDOM*}
+ typeset -h SRANDOM=local-variable
+ echo v=${ref/<->/integer}
+ echo NOT REACHED
+1:Dereferring an autoload variable fails to load it if its hidden (subscript)
+>v=integer
+>z=SRANDOM (zsh/random)
+?(eval):6: Can't add module parameter `SRANDOM': local parameter exists
+?(eval):zsh/random:6: error when adding parameter `SRANDOM'
+?(eval):6: autoloading module zsh/random failed to define parameter: SRANDOM
+
%clean
Messages sorted by:
Reverse Date,
Date,
Thread,
Author