Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: Simplify resolve_nameref and setscope
- X-seq: zsh-workers 53740
- From: Philippe Altherr <philippe.altherr@xxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: PATCH: Simplify resolve_nameref and setscope
- Date: Sat, 7 Jun 2025 18:05:04 +0200
- Archived-at: <https://zsh.org/workers/53740>
- List-id: <zsh-workers.zsh.org>
The attached patch significantly decreases the size and complexity of resolve_nameref and setscope (and fixes a few small bugs). It was derived mainly by deleting dead and/or redundant code from these two functions. The many steps can be observed
here.
The patch is built on top of a number of my
other patches and thus can only be applied once those have been applied.
Philippe
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index 044617190..f2f5f4857 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -606,7 +606,7 @@ getprivatenode(HashTable ht, const char *nam)
/* resolve nameref after skipping private parameters */
if (pm && (pm->node.flags & PM_NAMEREF) &&
(pm->u.str || (pm->node.flags & PM_UNSET)))
- pm = (Param) resolve_nameref(pm, NULL);
+ pm = resolve_nameref(pm, NULL, false);
return (HashNode)pm;
}
diff --git a/Src/builtin.c b/Src/builtin.c
index a39e7f33b..640d9e302 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2032,7 +2032,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) &&
(pm->level == locallevel || !(on & PM_LOCAL))) {
- if ((pm = (Param)resolve_nameref(pm, NULL)))
+ if ((pm = resolve_nameref(pm, NULL, false)))
pname = pm->node.nam;
if (pm && (pm->node.flags & PM_NAMEREF) &&
(on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) {
@@ -3913,7 +3913,8 @@ bin_unset(char *name, char **argv, Options ops, int func)
returnval = 1;
} else if (ss) {
if ((pm->node.flags & PM_NAMEREF) &&
- (!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) {
+ (!(pm = resolve_nameref(pm, NULL, false)) ||
+ pm->width)) {
/* warning? */
continue;
}
@@ -3957,7 +3958,7 @@ bin_unset(char *name, char **argv, Options ops, int func)
} else {
if (!OPT_ISSET(ops,'n')) {
int ref = (pm->node.flags & PM_NAMEREF);
- if (!(pm = (Param)resolve_nameref(pm, NULL)))
+ if (!(pm = resolve_nameref(pm, NULL, false)))
continue;
if (ref && pm->level < locallevel &&
!(pm->node.flags & PM_READONLY)) {
diff --git a/Src/params.c b/Src/params.c
index 46e790771..c093f34b7 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -556,7 +556,7 @@ getparamnode(HashTable ht, const char *nam)
{
HashNode hn = getparamnode_nofollow(ht, nam);
if (hn && ht == realparamtab && !(hn->flags & PM_UNSET))
- hn = resolve_nameref((Param)hn, NULL);
+ hn = (HashNode)resolve_nameref((Param)hn, NULL, false);
return hn;
}
@@ -1046,12 +1046,14 @@ createparam(char *name, int flags)
(oldpm->level == locallevel ?
!(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) &&
(oldpm->node.flags & PM_NAMEREF)) {
- Param lastpm;
- struct asgment stop;
- stop.flags = PM_NAMEREF | (flags & PM_LOCAL);
- stop.name = "";
- stop.value.scalar = NULL;
- lastpm = (Param)resolve_nameref(oldpm, &stop);
+ /**
+ * Here we only have to deal with namerefs that refer to
+ * not-yet-defined or unset variable. All other namerefs
+ * have already been taken care of by the resolve_nameref
+ * in typeset_single. It's unclear why these can't be
+ * handled there too.
+ **/
+ Param lastpm = resolve_nameref(oldpm, NULL, true);
if (lastpm) {
if (lastpm->node.flags & PM_NAMEREF) {
char *refname = GETREFNAME(lastpm);
@@ -3325,11 +3327,7 @@ assignsparam(char *s, char *val, int flags)
}
}
- if (v->pm->node.flags & PM_NAMEREF)
- v->pm->node.flags |= PM_NEWREF;
assignstrvalue(v, val, flags);
- if (v->pm->node.flags & PM_NAMEREF)
- v->pm->node.flags &= ~PM_NEWREF;
unqueue_signals();
return v->pm;
}
@@ -6271,79 +6269,45 @@ printparamnode(HashNode hn, int printflags)
}
/**/
-mod_export HashNode
-resolve_nameref(Param pm, const Asgment stop)
+mod_export Param
+resolve_nameref(Param pm, const Param stop, const int keep_nameref)
{
- HashNode hn = (HashNode)pm;
- const char *seek = stop ? stop->value.scalar : NULL;
-
- if (pm && (pm->node.flags & PM_NAMEREF)) {
- char *refname = GETREFNAME(pm);
- if (pm->node.flags & (PM_UNSET|PM_TAGGED)) {
- /* Semaphore with createparam() */
- pm->node.flags &= ~PM_UNSET;
- if (pm->node.flags & PM_NEWREF) /* See setloopvar() */
- return NULL;
- if (refname && *refname && (pm->node.flags & PM_TAGGED))
- pm->node.flags |= PM_SELFREF; /* See setscope() */
- return (HashNode) pm;
- } else if (refname) {
- if ((pm->node.flags & PM_TAGGED) ||
- (stop && strcmp(refname, stop->name) == 0)) {
- /* zwarnnam(refname, "invalid self reference"); */
- return stop ? (HashNode)pm : NULL;
- }
- if (*refname)
- seek = refname;
- }
- }
- else if (pm) {
- if (!(stop && (stop->flags & PM_NAMEREF)))
- return (HashNode)pm;
- if (!(pm->node.flags & PM_NAMEREF))
- return (pm->level < locallevel ? NULL : (HashNode)pm);
- }
- if (seek) {
- queue_signals();
- /* pm->width is the offset of any subscript */
- if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) {
- if (stop) {
- if (stop->flags & PM_NAMEREF)
- hn = (HashNode)pm;
- else
- hn = NULL;
- } else {
- /* this has to be the end of any chain */
- hn = (HashNode)pm; /* see fetchvalue() */
- }
- } else if ((hn = gethashnode2(realparamtab, seek))) {
- if (pm) {
- if (!(stop && (stop->flags & (PM_LOCAL)))) {
- if ((pm->node.flags & PM_NAMEREF) &&
- (pm->node.flags & PM_UPPER))
- hn = (HashNode)upscope_upper((Param)hn, pm->level - 1);
- else
- hn = (HashNode)upscope((Param)hn,
- (pm->node.flags & PM_NAMEREF) ?
- (pm->base) : ((Param)hn)->level);
- }
- /* 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);
- }
- if (pm)
- pm->node.flags &= ~PM_TAGGED;
- } else if (stop && (stop->flags & PM_NAMEREF))
- hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm;
- unqueue_signals();
+ if (!(pm->node.flags & PM_NAMEREF))
+ return (HashNode)pm;
+ if ((pm->node.flags & (PM_UNSET|PM_TAGGED))) {
+ /* Semaphore with createparam() */
+ pm->node.flags &= ~PM_UNSET;
+ return pm;
}
- return hn;
+ char *refname = GETREFNAME(pm);
+ if (!refname || !*refname)
+ return pm;
+
+ HashNode hn;
+ queue_signals();
+ /* pm->width is the offset of any subscript */
+ if (pm->width) {
+ /* this has to be the end of any chain */
+ hn = (HashNode)pm; /* see fetchvalue() */
+ } else if ((hn = gethashnode2(realparamtab, refname))) {
+ if ((pm->node.flags & PM_UPPER))
+ hn = (HashNode)upscope_upper((Param)hn, pm->level - 1);
+ else
+ hn = (HashNode)upscope((Param)hn, pm->base);
+ if (hn && hn != (HashNode)stop) {
+ /* user can't tag a nameref, safe for loop detection */
+ pm->node.flags |= PM_TAGGED;
+ if (hn->flags & PM_AUTOLOAD)
+ hn = getparamnode(realparamtab, refname);
+ if (!(hn->flags & PM_UNSET))
+ hn = (HashNode)resolve_nameref((Param)hn, stop, keep_nameref);
+ pm->node.flags &= ~PM_TAGGED;
+ }
+ } else if (keep_nameref)
+ hn = (HashNode)pm;
+ unqueue_signals();
+ return (Param)hn;
}
/**/
@@ -6361,10 +6325,7 @@ setloopvar(char *name, char *value)
pm->base = pm->width = 0;
SETREFNAME(pm, ztrdup(value));
pm->node.flags &= ~PM_UNSET;
- pm->node.flags |= PM_NEWREF;
setscope(pm);
- if (!errflag)
- pm->node.flags &= ~PM_NEWREF;
} else
setsparam(name, ztrdup(value));
}
@@ -6374,9 +6335,8 @@ static void
setscope(Param pm)
{
queue_signals();
- if (pm->node.flags & PM_NAMEREF) do {
+ if (pm->node.flags & PM_NAMEREF) {
Param basepm;
- struct asgment stop;
char *refname = GETREFNAME(pm);
char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL;
int q = queue_signal_level();
@@ -6396,7 +6356,7 @@ setscope(Param pm)
/* Compute pm->base */
if (!(pm->node.flags & PM_UPPER) && refname &&
(basepm = (Param)getparamnode_nofollow(realparamtab, refname)) &&
- (!(basepm->node.flags & PM_NEWREF) || (basepm = basepm->old))) {
+ (!(basepm->old) || (basepm != pm) || (basepm = basepm->old))) {
pm->base = basepm->level;
}
if (pm->base > pm->level) {
@@ -6410,49 +6370,18 @@ setscope(Param pm)
}
/* Check for self references */
- stop.name = pm->node.nam;
- stop.value.scalar = NULL;
- stop.flags = PM_NAMEREF;
- if (locallevel && !(pm->node.flags & PM_UPPER))
- stop.flags |= PM_LOCAL;
- dont_queue_signals(); /* Prevent unkillable loops */
- basepm = (Param)resolve_nameref(pm, &stop);
- restore_queue_signals(q);
- if (basepm) {
- if (basepm->node.flags & PM_NAMEREF) {
- if (pm == basepm) {
- if (pm->node.flags & PM_SELFREF) {
- /* Loop signalled by resolve_nameref() */
- if (upscope(pm, pm->base) == pm) {
- zerr("%s: invalid self reference", refname);
- unsetparam_pm(pm, 0, 1);
- break;
- }
- pm->node.flags &= ~PM_SELFREF;
- } else if (pm->base == pm->level) {
- if (refname && *refname &&
- strcmp(pm->node.nam, refname) == 0) {
- zerr("%s: invalid self reference", refname);
- unsetparam_pm(pm, 0, 1);
- break;
- }
- }
- } else if ((t = GETREFNAME(basepm))) {
- if (basepm->base <= basepm->level &&
- strcmp(pm->node.nam, t) == 0) {
- zerr("%s: invalid self reference", refname);
- unsetparam_pm(pm, 0, 1);
- break;
- }
- }
+ if (refname && *refname) {
+ if (basepm != pm && !(pm->width)) {
+ dont_queue_signals(); /* Prevent unkillable loops */
+ basepm = resolve_nameref(pm, pm, false);
+ restore_queue_signals(q);
+ }
+ if (basepm == pm) {
+ zerr("%s: invalid self reference", refname);
+ unsetparam_pm(pm, 0, 1);
}
}
- if (refname && upscope(pm, pm->base) == pm &&
- strcmp(pm->node.nam, refname) == 0) {
- zerr("%s: invalid self reference", refname);
- unsetparam_pm(pm, 0, 1);
- }
- } while (0);
+ };
unqueue_signals();
}
diff --git a/Src/zsh.h b/Src/zsh.h
index 4e5c02980..aa10145d2 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1935,9 +1935,6 @@ struct tieddata {
#define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */
#define PM_NAMEREF (1<<30) /* pointer to a different parameter */
-#define PM_SELFREF PM_UNIQUE /* Overload when namerefs resolved */
-#define PM_NEWREF PM_SINGLE /* Overload in for-loop namerefs */
-
/* The option string corresponds to the first of the variables above */
#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT"
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index 584c25773..795b9e084 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -448,6 +448,18 @@ F:unexpected side-effects of previous tests
ptr1=ptr2
ptr2=ptr1
1:looping assignment not allowed
+*?*invalid self reference
+
+ typeset var=foo
+ typeset var=foo
+ typeset -n ptr1=var
+ () {
+ typeset -n ptr1
+ typeset -n ptr2=ptr1
+ ptr1=ptr2
+ echo ptr1=$ptr1
+ }
+1:regression: reference loop with same name enclosing variable
*?*invalid self reference
typeset ptr1=not-a-ref
@@ -939,6 +951,17 @@ F:Checking for a bug in zmodload that affects later tests
>typeset -n ref=two
>typeset -n ref=var
+ typeset var=foo
+ typeset -n ref=var
+ () {
+ typeset -n ref
+ ref=""
+ ref=ref
+ echo $ref
+ }
+0:reference always prefers enclosing variable over itself
+>foo
+
typeset -g .K01.scalar='RW'
typeset -gA .K01.assoc=(x y)
typeset -ga .K01.array=(z)
Messages sorted by:
Reverse Date,
Date,
Thread,
Author