Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH] Refactor resolve_nameref() and define getparam(), loadparama(), and resolveparam()



The main aim of this patch was to enhance resolve_nameref() to also return the last followed reference but it ended up defining a mini-API for getting, loading and resolving parameters. The newly introduced getparam(), loadparam(), and resolveparam() functions have names that explicitly state what they are doing and their behavior is well defined and well documented.

In addition of defining getparam(), loadparam(), and resolveparam() and replacing all calls to resolve_nameref() with calls to resolveparam(), the patch also does the following:
Refactor resolve_nameref() and define getparam(), loadparama(), and resolveparam()

Philippe

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index c5995f4e4..8175a9e5d 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -975,8 +975,9 @@ following flags are supported:
 
 startitem()
 item(tt(!))(
-When the parameter being expanded is a named reference, the reference
-itself is examined and thus is em(not) resolved to its referent.
+Use the parameter as is. When the parameter is a named reference,
+don't resolve it to its referent; use the reference itself. When the
+parameter is an autoloaded one, don't load it.
 )
 item(tt(#))(
 Evaluate the resulting words as numeric expressions and interpret
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index e06cbb1de..e1feb2936 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -561,22 +561,11 @@ wrap_private(Eprog prog, FuncWrap w, char *name)
     return 1;
 }
 
-static GetNodeFunc getparamnode;
-
 /**/
 static HashNode
-getprivatenode(HashTable ht, const char *nam)
+getprivatenode2(HashTable ht, const char *nam)
 {
-    /* getparamnode() would follow namerefs, we must not do that here */
-    HashNode hn = gethashnode2(ht, nam);
-    Param pm = (Param) hn;
-
-    /* autoload has precedence over nameref, so getparamnode() */
-    if (pm && (pm->node.flags & PM_AUTOLOAD)) {
-	hn = getparamnode(ht, nam);
-	pm = (Param) hn;
-	/* how would an autoloaded private behave?  return here? */
-    }
+    Param pm = (Param) gethashnode2(ht, nam);
     while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) {
 	if (pm->level == private_wraplevel + 1) {
 	    /* Variable is in the current function scope */
@@ -605,25 +594,6 @@ getprivatenode(HashTable ht, const char *nam)
 #endif
 	pm = pm->old;
     }
-
-    /* resolve nameref after skipping private parameters */
-    if (pm && (pm->node.flags & PM_NAMEREF) &&
-	(pm->u.str || (pm->node.flags & PM_UNSET)))
-	pm = resolve_nameref(pm);
-
-    return (HashNode)pm;
-}
-
-/**/
-static HashNode
-getprivatenode2(HashTable ht, const char *nam)
-{
-    /* getparamnode() would follow autoloads, we must not do that here */
-    HashNode hn = gethashnode2(ht, nam);
-    Param pm = (Param) hn;
-
-    while (!fakelevel && pm && locallevel > pm->level && is_private(pm))
-	pm = pm->old;
     return (HashNode)pm;
 }
 
@@ -672,10 +642,8 @@ setup_(UNUSED(Module m))
     HashNode hn = builtintab->getnode(builtintab, "local");
 
     /* Horrible, horrible hack */
-    getparamnode = realparamtab->getnode;
     save_getnode2 = realparamtab->getnode2;
     save_printnode = realparamtab->printnode;
-    realparamtab->getnode = getprivatenode;
     realparamtab->getnode2 = getprivatenode2;
     realparamtab->printnode = printprivatenode;
 
@@ -720,8 +688,7 @@ cleanup_(Module m)
     *(Builtin)hn = save_local;
 
     removehashnode(reswdtab, "private");
-    
-    realparamtab->getnode = getparamnode;
+
     realparamtab->getnode2 = save_getnode2;
     realparamtab->printnode = save_printnode;
 
diff --git a/Src/builtin.c b/Src/builtin.c
index 64a27511f..794da3062 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2027,22 +2027,24 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	       int on, int off, int roff, Asgment asg, Param altpm,
 	       Options ops, int joinchar)
 {
+    DPUTS(paramtab != realparamtab, "BUG: typeset_single: paramtab != realparamtab");
     int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0;
     char *subscript;
 
     if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) &&
 	(pm->level == locallevel || !(on & PM_LOCAL))) {
-	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)) &&
+	Param lastref;
+	pm = resolveparamref_pm(pm, 1, &lastref);
+	if (!pm && isplaceholderref(lastref) && (pm = lastref) &&
 	    (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? */
 	    zwarnnam(cname, "%s: can't change type of a named reference",
-		     pname);
+		     lastref->node.nam);
 	    return NULL;
 	}
+	if (pm)
+	    pname = pm->node.nam;
     }
 
     /*
@@ -2667,6 +2669,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 mod_export int
 bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 {
+    DPUTS(paramtab != realparamtab, "BUG: bin_typeset: paramtab != realparamtab");
     Param pm;
     Asgment asg;
     Patprog pprog;
@@ -3795,6 +3798,7 @@ mkautofn(Shfunc shf)
 int
 bin_unset(char *name, char **argv, Options ops, int func)
 {
+    DPUTS(paramtab != realparamtab, "BUG: bin_unset: paramtab != realparamtab");
     Param pm, next;
     Patprog pprog;
     char *s;
@@ -3819,8 +3823,7 @@ bin_unset(char *name, char **argv, Options ops, int func)
 			next = (Param) pm->node.next;
 			if (pattry(pprog, pm->node.nam)) {
 			    if (OPT_ISSET(ops,'n') ||
-				((pm = resolve_nameref(pm)) &&
-				 !(pm->node.flags & PM_NAMEREF)))
+				(pm = resolveparam_pm(pm, 0)))
 				unsetparam_pm(pm, 0, 1);
 			    match++;
 			}
@@ -3859,18 +3862,18 @@ bin_unset(char *name, char **argv, Options ops, int func)
 	    returnval = 1;
 	    continue;
 	}
-	pm = (Param) (paramtab == realparamtab ?
-		      /* getnode2() to avoid autoloading */
-		      paramtab->getnode2(paramtab, s) :
-		      paramtab->getnode(paramtab, s));
-	/*
-	 * Unsetting an unset variable is not an error.
-	 * This appears to be reasonably standard behaviour.
-	 */
-	if (!pm)
-	    continue;
-	else if (ss) {
-	    if ((pm->node.flags & PM_NAMEREF) && !(pm = resolve_nameref(pm))) {
+	pm = getparam(s);
+	if (!ss) {
+	    pm = asset_pm(OPT_ISSET(ops,'n') ? pm : resolveparam_pm(pm, 0));
+	    /*
+	     * Unsetting a nonexistent or unset variable is not an error.
+	     * This appears to be reasonably standard behaviour.
+	     */
+	    if (pm && unsetparam_pm(pm, 0, 1))
+		returnval = 1;
+	} else {
+	    *ss = '[';
+	    if (!(pm = asset_pm(resolveparam_pm(pm, 1)))) {
 		/* warning? */
 		continue;
 	    }
@@ -3889,7 +3892,6 @@ bin_unset(char *name, char **argv, Options ops, int func)
 		vbuf.start = 0;
 		vbuf.end = -1;
 		vbuf.arr = 0;
-		*ss = '[';
 		if (getindex(&ss, &vbuf, SCANPM_ASSIGNING) == 0 &&
 		    vbuf.pm && !(vbuf.pm->node.flags & PM_UNSET)) {
 		    if (PM_TYPE(pm->node.flags) == PM_SCALAR) {
@@ -3909,16 +3911,12 @@ bin_unset(char *name, char **argv, Options ops, int func)
 		returnval = errflag;
 		errflag &= ~ERRFLAG_ERROR;
 	    } else {
+		*ss = 0;
 		zerrnam(name, "%s: invalid element for unset", s);
 		returnval = 1;
+		*ss = '[';
 	    }
-	} else if (OPT_ISSET(ops,'n') ||
-		   ((pm = resolve_nameref(pm)) &&
-		    !(pm->node.flags & PM_NAMEREF)))
-	    if (unsetparam_pm(pm, 0, 1))
-		returnval = 1;
-	if (ss)
-	    *ss = '[';
+	}
     }
     unqueue_signals();
     return returnval;
diff --git a/Src/params.c b/Src/params.c
index 6210f973f..c5248a40f 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -564,7 +564,7 @@ newparamtable(int size, char const *name)
     ht->filltable   = NULL;
     ht->cmpnodes    = strcmp;
     ht->addnode     = addhashnode;
-    ht->getnode     = getparamnode;
+    ht->getnode     = gethashnode2; /* getparamnode for realparamtab */
     ht->getnode2    = gethashnode2;
     ht->removenode  = removehashnode;
     ht->disablenode = NULL;
@@ -576,39 +576,205 @@ newparamtable(int size, char const *name)
 }
 
 /**/
-static Param
-loadparamnode(HashTable ht, Param pm, const char *nam)
-{
-    if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) {
-	int level = pm->level;
-	char *mn = dupstring(pm->u.str);
-	(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
-	     * no error status we're in for all sorts of mayhem?
-	     */
-	    zerr("autoloading module %s failed to define parameter: %s", mn,
-		 nam);
-	}
-    }
-    return pm;
+static HashNode
+getparamnode(HashTable ht, const char *nam)
+{
+    DPUTS(ht != realparamtab, "BUG: getparamnode: ht != realparamtab");
+    return (HashNode) resolveparam_pm(getparam(nam), 1);
 }
 
+/* Returns whether the parameter is set. */
+
 /**/
-static HashNode
-getparamnode(HashTable ht, const char *nam)
+mod_export int
+isset_pm(Param pm)
+{
+    return pm &&
+	(!(pm->node.flags & PM_UNSET) || (pm->node.flags & PM_DECLARED));
+}
+
+/* Returns the parameter if it is set, otherwise NULL. */
+
+/**/
+mod_export Param
+asset_pm(Param pm)
+{
+    return isset_pm(pm) ? pm : NULL;
+}
+
+/* Returns whether the parameter is a placeholder reference. */
+
+/**/
+mod_export int
+isplaceholderref(Param pm)
+{
+    char *refname;
+    return pm && (pm->node.flags & PM_NAMEREF) &&
+	(!(refname = GETREFNAME(pm)) || !*refname);
+}
+
+/*
+ * Returns the parameter with the given name, or NULL if there is none
+ * with that name. The returned parameter may be one that is not set.
+ */
+
+/**/
+mod_export Param
+getparam(const char *name)
+{
+    DPUTS(paramtab != realparamtab, "BUG: getparam: paramtab != realparamtab");
+    return (Param) paramtab->getnode2(paramtab, name);
+}
+
+/*
+ * Retrieves the parameter with the given name, loads it if it is an
+ * autoload one, and returns it. Returns NULL if there is no parameter
+ * with the given name or if the parameter fails to load. The returned
+ * parameter may be one that is not set.
+ */
+
+/**/
+mod_export Param
+loadparam(const char *name)
 {
-    Param pm = loadparamnode(ht, (Param)gethashnode2(ht, nam), nam);
-    if (pm && ht == realparamtab && !(pm->node.flags & PM_UNSET))
-	pm = resolve_nameref(pm);
-    return (HashNode)pm;
+    return loadparam_pm(getparam(name));
+}
+
+/*
+ * Loads the parameter if it is an autoload one and returns it, or
+ * NULL if the parameter fails to load.
+ */
+
+/**/
+mod_export Param
+loadparam_pm(Param pm)
+{
+    if (!isset_pm(pm) || !(pm->node.flags & PM_AUTOLOAD))
+	return pm;
+    char *module = dupstring(pm->u.str);
+    char *name = dupstring(pm->node.nam);
+    int level = pm->level;
+    ensurefeature(module, "p:", name);
+    pm = getparam(name);
+    while (pm && pm->level > level)
+	pm = pm->old;
+    if (pm && pm->level == level && !(pm->node.flags & PM_AUTOLOAD))
+	return pm;
+    zerr("autoloading module %s failed to define parameter: %s", module, name);
+    return NULL;
+}
+
+
+/*
+ * Retrieves the parameter with the given name, and returns the
+ * parameter it refers to as detailed in resolveparam_pm(). Returns
+ * NULL if there is no parameter with the given name.
+ */
+
+/**/
+mod_export Param
+resolveparam(const char *name, int load)
+{
+    return resolveparam_pm(getparam(name), load);
+}
+
+/*
+ * Returns the parameter referred to by the provided one, or NULL if
+ * there is no such parameter. Returns the parameter itself if it is
+ * not a reference. Returns a parameter, possibly a reference, that is
+ * not set if the provided parameter is or refers to such a parameter.
+ * Returns NULL if and only if the provided parameter is NULL, is or
+ * refers to a placeholder reference or a reference that refers to a
+ * nonexistent parameter, or, if load is non-zero, is or refers to an
+ * autoload parameter that fails to load.
+ *
+ * If load is non-zero, loads any autoload parameter encountered
+ * during the resolution, including the provided one.
+ *
+ * The returned parameter is guaranteed to NOT be a set reference.
+ * Though, it may be an unset one, or, if load is zero, an autoload
+ * parameter that turns into a set reference upon loading.
+ */
+
+/**/
+mod_export Param
+resolveparam_pm(Param pm, int load)
+{
+    return resolveparamref_rec(pm, load, NULL, NULL);
+}
+
+/*
+ * Same as resolveparam() but additionally assigns lastref with the
+ * last reference followed during the resolution, or NULL if there was
+ * none.
+ */
+
+/**/
+mod_export Param
+resolveparamref(const char *name, int load, Param *lastref)
+{
+    return resolveparamref_pm(getparam(name), load, lastref);
+}
+
+/*
+ * Same as resolveparam_pm() but additionally assigns lastref with the
+ * last reference followed during the resolution, or NULL if there was
+ * none.
+ */
+
+/**/
+mod_export Param
+resolveparamref_pm(Param pm, int load, Param *lastref)
+{
+    *lastref = NULL;
+    return resolveparamref_rec(pm, load, lastref, NULL);
+}
+
+/*
+ * Same as resolveparamref_pm() but does NOT assign lastref with NULL
+ * if no references are followed and returns stop if any reference
+ * followed refers to it.
+ */
+
+/**/
+static Param
+resolveparamref_rec(Param pm, int load, Param *lastref, const Param stop)
+{
+    DPUTS(paramtab != realparamtab, "BUG: resolveparam_rec: paramtab != realparamtab");
+    Param ref;
+    char *refname;
+    if (load)
+	pm = loadparam_pm(pm);
+    if (!isset_pm(pm) || !(pm->node.flags & PM_NAMEREF))
+	return pm;
+    ref = pm;
+    if (lastref)
+	*lastref = ref;
+    if (!(refname = GETREFNAME(ref)) || !*refname)
+	return NULL;
+    if (idigit(*refname)) {
+	int ppar = zstrtol(refname, NULL, 10);
+	queue_signals();
+	if (ppar >= argnparams_size) {
+	    size_t old_size = argnparams_size;
+	    size_t new_size = argnparams_size = maximum(2 * old_size, ppar + 1);
+	    argnparams = zrealloc(argnparams, new_size * sizeof(Param));
+	    memset(argnparams + old_size, 0,
+		   (new_size - old_size) * sizeof(Param));
+	}
+	if (!(pm = argnparams[ppar])) {
+	    pm = argnparams[ppar] = zshcalloc(sizeof(*pm));
+	    pm->node.nam = zalloc(snprintf(NULL, 0, "%d", ppar) + 1);
+	    sprintf(pm->node.nam, "%d", ppar);
+	    pm->node.flags = PM_SCALAR | PM_SPECIAL;
+	    pm->u.val = ppar;
+	    pm->gsu.s = &argn_gsu;
+	}
+	unqueue_signals();
+    } else if ((pm = (Param)gethashnode2(realparamtab, refname)) &&
+	       (pm = upscope(pm, ref)) && pm != stop)
+	pm = resolveparamref_rec(pm, load, lastref, stop);
+    return pm;
 }
 
 /* Copy a parameter hash table */
@@ -869,6 +1035,7 @@ createparamtable(void)
 #endif
 
     paramtab = realparamtab = newparamtable(151, "paramtab");
+    paramtab->getnode = getparamnode;
 
     /* Add the special parameters to the hash table */
     for (ip = special_params; ip->node.nam; ip++)
@@ -1080,6 +1247,7 @@ createparam(char *name, int flags)
 			 paramtab->getnode(paramtab, name));
 
 	if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) {
+	    DPUTS(paramtab != realparamtab, "BUG: createparam/ro-by-design: paramtab != realparamtab");
 	    if (!(flags & PM_LOCAL)) {
 		/* Must call the API for namerefs and specials to work */
 		pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam);
@@ -1098,47 +1266,47 @@ 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_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
-	     * 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_rec(oldpm, NULL, 1);
+	if (isset_pm(oldpm) &&
+	    (oldpm->level == locallevel || !(flags & PM_LOCAL)) &&
+	    !(flags & PM_NAMEREF) && (oldpm->node.flags & PM_NAMEREF)) {
+	    DPUTS(paramtab != realparamtab, "BUG: createparam/nameref: paramtab != realparamtab");
+	    /* The reference oldpm should either refer to a paramater
+	     * that was unset or that does not yet exist, or it should
+	     * be or refer to a placeholder reference. If it refers to
+	     * an existing parameter, then createparam should most
+	     * likely not have been called. */
+	    Param lastref, lastpm = resolveparamref_pm(oldpm, 0, &lastref);
+	    char *refname;
 	    if (lastpm) {
-		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 & ~PM_LOCAL) {
-				/* Only plain scalar assignment allowed */
-				zerr("%s: can't change type of named reference",
-				     name);	/* Differs from ksh93u+ */
-				return NULL;
-			    }
-			}
-			return lastpm;
-		    }
-		} else {
-		    /* nameref pointing to an unset local */
-		    DPUTS(!(lastpm->node.flags & PM_UNSET),
-			  "BUG: local parameter is not unset");
-		    oldpm = lastpm;
+		DPUTS(isset_pm(lastpm), "BUG: parameter is not unset");
+		/* oldpm refers to the unset parameter lastpm */
+		oldpm = lastpm;
+	    } else if (!(refname = GETREFNAME(lastref)) || !*refname) {
+		/* oldpm is or refers to the placeholder reference lastref */
+		/* TODO: Is this section dead? At least, the error
+		 * seems impossible to trigger. */
+		if (!(lastref->node.flags & PM_READONLY) &&
+		    (flags & ~PM_LOCAL)) {
+		    /* Only plain scalar assignment allowed */
+		    /* Differs from ksh93u+ */
+		    zerr("%s: can't change type of named reference", name);
+		    return NULL;
 		}
+		return lastref;
+	    } else if (!getparam(refname)) {
+		/* oldpm refers to a nonexistent parameter named refname */
+		name = refname;
+		oldpm = NULL;
+	    } else {
+		/* oldpm refers to a parameter named refname that only
+		 * exists in scopes too deeply nested to be seen by
+		 * lastref. The creation of a hidden parameter is
+		 * needed, which is currently not supported. */
+		/* TODO: Should this case be explicitly handled here?
+		 * Should it trigger an error or a warning?  For now,
+		 * probably more by accident than by design, oldpm and
+		 * name are left unchanged, which leads, apparently
+		 * always, to returning NULL with no error message. */
 	    }
 	}
 
@@ -1160,6 +1328,7 @@ createparam(char *name, int flags)
 		}
 		oldpm->node.flags &= ~PM_UNSET;
 		if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
+		    DPUTS(paramtab != realparamtab, "BUG: createparam/ename: paramtab != realparamtab");
 		    Param altpm =
 			(Param) paramtab->getnode(paramtab, oldpm->ename);
 		    if (altpm)
@@ -2217,6 +2386,7 @@ getvalue(Value v, char **pptr, int bracks)
 mod_export Value
 fetchvalue(Value v, char **pptr, int bracks, int scanflags)
 {
+    DPUTS(paramtab != realparamtab, "BUG: fetchvalue: paramtab != realparamtab");
     char *s, *t, *ie;
     char sav, c;
     int ppar = 0;
@@ -2262,14 +2432,15 @@ fetchvalue(Value v, char **pptr, int bracks, int scanflags)
 	if (sav)
 	    *s = sav;
     } else {
-	Param pm;
-	int isvarat;
-
-        isvarat = (t[0] == '@' && !t[1]);
-	if (scanflags & SCANPM_NONAMEREF)
-	    pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t);
-	else
-	    pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
+	int isvarat = (t[0] == '@' && !t[1]);
+	Param pm = getparam(*t == '0' ? "0" : t);
+	if (!(scanflags & SCANPM_NONAMEREF)) {
+	    Param lastref;
+	    pm = resolveparamref_pm(pm, 1, &lastref);
+	    /* TODO: Do the following only for assignments. */
+	    if (!pm && isplaceholderref(lastref))
+		pm = lastref;
+	}
 	if (!pm && *t == '.' && !isident(t)) {
 	    /* badly formed namespace reference */
 	    if (sav)
@@ -2278,8 +2449,7 @@ fetchvalue(Value v, char **pptr, int bracks, int scanflags)
 	} else if (sav)
 	    *s = sav;
 	*pptr = s;
-	if (!pm || ((pm->node.flags & PM_UNSET) &&
-		    !(pm->node.flags & PM_DECLARED)))
+	if (!isset_pm(pm))
 	    return NULL;
 	if (!v)
 	    v = (Value) zhalloc(sizeof *v);
@@ -2790,6 +2960,8 @@ assignstrvalue(Value v, char *val, int flags)
 	    new[newlen] = '\0';
 	    v->pm->gsu.s->setfn(v->pm, new);
 	}
+	if (v->pm->node.flags & PM_NAMEREF)
+	    setscope(v->pm);
 	break;
     case PM_INTEGER:
 	if (val) {
@@ -2849,7 +3021,6 @@ assignstrvalue(Value v, char *val, int flags)
         }
 	break;
     }
-    setscope(v->pm);
     if (errflag ||
 	((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
 	  !(isset(ALLEXPORT) &&
@@ -3153,6 +3324,7 @@ check_warn_pm(Param pm, const char *pmtype, int created,
 mod_export Param
 assignsparam(char *s, char *val, int flags)
 {
+    DPUTS(paramtab != realparamtab, "BUG: assignsparam: paramtab != realparamtab");
     struct value vbuf;
     Value v;
     char *t = s;
@@ -3317,6 +3489,7 @@ setsparam(char *s, char *val)
 mod_export Param
 assignaparam(char *s, char **val, int flags)
 {
+    DPUTS(paramtab != realparamtab, "BUG: assignaparam: paramtab != realparamtab");
     struct value vbuf;
     Value v;
     char *t = s;
@@ -3562,6 +3735,7 @@ setaparam(char *s, char **aval)
 mod_export Param
 sethparam(char *s, char **val)
 {
+    DPUTS(paramtab != realparamtab, "BUG: sethparam: paramtab != realparamtab");
     struct value vbuf;
     Value v;
     char *t = s;
@@ -3623,6 +3797,7 @@ sethparam(char *s, char **val)
 static Param
 assignnparam(char *s, mnumber val, int flags)
 {
+    DPUTS(paramtab != realparamtab, "BUG: assignnparam: paramtab != realparamtab");
     struct value vbuf;
     Value v;
     char *t = s, *ss;
@@ -3755,6 +3930,7 @@ setiparam_no_convert(char *s, zlong val)
 mod_export int
 resetparam(Param pm, int flags)
 {
+    DPUTS(paramtab != realparamtab, "BUG: resetparam: paramtab != realparamtab");
     char *s = pm->node.nam;
     queue_signals();
     if (pm != (Param)(paramtab == realparamtab ?
@@ -3823,6 +3999,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
 
     /* remove it under its alternate name if necessary */
     if (altremove) {
+	DPUTS(paramtab != realparamtab, "BUG: unsetparam_pm/ename: paramtab != realparamtab");
 	altpm = (Param) paramtab->getnode(paramtab, altremove);
 	/* tied parameters are at the same local level as each other */
 	oldpm = NULL;
@@ -4329,6 +4506,7 @@ tiedarrgetfn(Param pm)
 void
 tiedarrsetfn(Param pm, char *x)
 {
+    DPUTS(paramtab != realparamtab, "BUG: tiedarrsetfn: paramtab != realparamtab");
     struct tieddata *dptr = (struct tieddata *)pm->u.data;
 
     if (*dptr->arrptr)
@@ -5300,6 +5478,7 @@ pipestatsetfn(UNUSED(Param pm), char **x)
 void
 arrfixenv(char *s, char **t)
 {
+    DPUTS(paramtab != realparamtab, "BUG: arrfixenv: paramtab != realparamtab");
     Param pm;
     int joinchar;
 
@@ -6293,6 +6472,7 @@ printparamnode(HashNode hn, int printflags)
 	     * typeset -T SCALAR array=('')
 	     * (same for (a b:c)...)
 	     */
+	    DPUTS(paramtab != realparamtab, "BUG: printparamnode_pm/ename: paramtab != realparamtab");
 	    Param tmp = (Param) paramtab->getnode(paramtab, p->ename);
 
 	    /*
@@ -6345,55 +6525,11 @@ printparamnode(HashNode hn, int printflags)
 	putchar('\n');
 }
 
-/**/
-mod_export Param
-resolve_nameref(Param pm)
-{
-    return resolve_nameref_rec(pm, NULL, 0);
-}
-
-/**/
-static Param
-resolve_nameref_rec(Param pm, const Param stop, int keep_lastref)
-{
-    Param ref = pm;
-    char *refname;
-    if (!pm || !(pm->node.flags & PM_NAMEREF) || (pm->node.flags & PM_UNSET)
-	|| !(refname = GETREFNAME(pm)) || !*refname)
-	return pm;
-    queue_signals();
-    if ((pm = (Param)gethashnode2(realparamtab, refname))) {
-	if ((pm = loadparamnode(paramtab, upscope(pm, ref), refname)) &&
-	    pm != stop && !(pm->node.flags & PM_UNSET))
-	    pm = resolve_nameref_rec(pm, stop, keep_lastref);
-    } else if (idigit(*refname)) {
-	int ppar = zstrtol(refname, NULL, 10);
-	if (ppar >= argnparams_size) {
-	    size_t old_size = argnparams_size;
-	    size_t new_size = argnparams_size = maximum(2 * old_size, ppar);
-	    argnparams = zrealloc(argnparams, new_size * sizeof(Param));
-	    memset(argnparams + old_size, 0,
-		   (new_size - old_size) * sizeof(Param));
-	}
-	if (!(pm = argnparams[ppar])) {
-	    pm = argnparams[ppar] = zshcalloc(sizeof(*pm));
-	    pm->node.nam = zalloc(snprintf(NULL, 0, "%d", ppar) + 1);
-	    sprintf(pm->node.nam, "%d", ppar);
-	    pm->node.flags = PM_SCALAR | PM_SPECIAL;
-	    pm->u.val = ppar;
-	    pm->gsu.s = &argn_gsu;
-	}
-    } else if (keep_lastref)
-	pm = ref;
-    unqueue_signals();
-    return pm;
-}
-
 /**/
 mod_export void
 setloopvar(char *name, char *value)
 {
-  Param pm = (Param) gethashnode2(realparamtab, name);
+  Param pm = getparam(name);
 
   if (pm && (pm->node.flags & PM_NAMEREF)) {
       if (pm->node.flags & PM_READONLY) {
@@ -6417,39 +6553,27 @@ setloopvar(char *name, char *value)
 static void
 setscope(Param pm)
 {
+    Param basepm = NULL;
+    char *refname = GETREFNAME(pm);
     queue_signals();
-    if (pm->node.flags & PM_NAMEREF) {
-	Param basepm = NULL;
-	char *refname = GETREFNAME(pm);
-	int q = queue_signal_level();
-
-	/* Compute pm->base */
-	if (!(pm->node.flags & PM_UPPER) && refname &&
-	    (basepm = (Param)gethashnode2(realparamtab, refname)) &&
-	    (basepm = (Param)loadparamnode(realparamtab, basepm, refname)) &&
-	    (basepm != pm || !basepm->old || (basepm = basepm->old))) {
-	    setscope_base(pm, basepm->level);
-	}
-	if (pm->base > pm->level) {
-	    if (EMULATION(EMULATE_KSH)) {
-		zerr("%s: global reference cannot refer to local variable",
-		      pm->node.nam);
-		unsetparam_pm(pm, 0, 1);
-	    } else if (isset(WARNNESTEDVAR))
-		zwarn("reference %s in enclosing scope set to local variable %s",
-		      pm->node.nam, refname);
-	}
-
-	/* Check for self references */
-	if (refname && *refname && basepm != pm) {
-	    dont_queue_signals();	/* Prevent unkillable loops */
-	    basepm = resolve_nameref_rec(pm, pm, 0);
-	    restore_queue_signals(q);
-	}
-	if (pm == basepm) {
-	    zerr("%s: invalid self reference", refname);
+    /* Compute pm->base */
+    if (!(pm->node.flags & PM_UPPER) && refname && *refname &&
+	(basepm = (Param)gethashnode2(realparamtab, refname)) &&
+	(basepm != pm || !basepm->old || (basepm = basepm->old))) {
+	setscope_base(pm, basepm->level);
+    }
+    if (pm->base > pm->level) {
+	if (EMULATION(EMULATE_KSH)) {
+	    zerr("%s: global reference cannot refer to local variable",
+		 pm->node.nam);
 	    unsetparam_pm(pm, 0, 1);
-	}
+	} else if (isset(WARNNESTEDVAR))
+	    zwarn("reference %s in enclosing scope set to local variable %s",
+		  pm->node.nam, refname);
+    }
+    if (basepm == pm || resolveparamref_rec(pm, 0, NULL, pm) == pm) {
+	zerr("%s: invalid self reference", refname);
+	unsetparam_pm(pm, 0, 1);
     }
     unqueue_signals();
 }
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index a0706ec0f..e490aab0f 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -827,6 +827,7 @@ F:Same test, should part 5 output look like this?
  () {
    zmodload -u zsh/parameter
    typeset -n myself=parameters
+   print -r -- $myself[myself]
    local -h parameters
    print -r -- $myself[myself]
    typeset -p parameters
@@ -835,6 +836,7 @@ F:Same test, should part 5 output look like this?
  fi
 0:up-reference part 9, autoloading with hidden special
 >nameref-local-association-readonly-hide-hideval-special
+>nameref-local-association-readonly-hide-hideval-special
 >typeset -h parameters
 
  (
@@ -1632,15 +1634,12 @@ F:previously this could create an infinite recursion and crash
  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
+0:Referring an autoload variable doesn't load it but dereferring it does
 >z=SRANDOM (zsh/random)
->z=
->v=integer
 >z=SRANDOM (zsh/random)
 >v=integer
+>z=
 
  typeset -n ref=SRANDOM
  echo v=${ref/<->/integer}
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index f3ad7b6b9..d2e87b403 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -301,20 +301,28 @@ F:future revision will create a global with this assignment
 *>*
 *>*
 
- typeset top=TOP
- () {
-  local -P -n test=top
-  print $test
-  test=top
-  print $test
-  () { print UP: $test }
- }
- print $top
+ typeset out=OUT
+ () {
+   typeset lcl=LCL
+   local -P -n ref1=out ref2=lcl ref3=undef ref4=""
+   print "A: ref1=$ref1 ref2=$ref2 ref3=$ref3 ref4=$ref4 out=$out lcl=$lcl undef=$undef"
+   ref1=Out ref2=Lcl ref3=Glb ref4=out
+   print "B: ref1=$ref1 ref2=$ref2 ref3=$ref3 ref4=$ref4 out=$out lcl=$lcl undef=$undef"
+   () { print "C: ref1=$ref1 ref2=$ref2 ref3=$ref3 ref4=$ref4 out=$out lcl=$lcl undef=$undef" }
+   typeset -p ref1 ref2 ref3 ref4 undef
+ }
+ print "D: out=$out undef=$undef"
+ unset undef
 0:nameref can be declared private
->TOP
->top
->UP:
->top
+>A: ref1=OUT ref2=LCL ref3= ref4= out=OUT lcl=LCL undef=
+>B: ref1=Out ref2=Lcl ref3=Glb ref4=Out out=Out lcl=Lcl undef=Glb
+>C: ref1= ref2= ref3= ref4= out=Out lcl=Lcl undef=Glb
+>typeset -hn ref1=out
+>typeset -hn ref2=lcl
+>typeset -hn ref3=undef
+>typeset -hn ref4=out
+>typeset -g undef=Glb
+>D: out=Out undef=Glb
 
  () {
    typeset -a ary


Messages sorted by: Reverse Date, Date, Thread, Author