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

PATCH: Fix nameref errors exposed by Philippe's tests



This addresses several issues that Philippe uncovered when the scope
in which an nameref is assigned is a few levels removed from from the
scope of its declaration:
1) Searching "up" for a nameref declared "typeset -n -u" could find
deeper locals than intended
2) Searching for the parameter part of a subscript reference could
skip to the top level
3) Exiting a function scope could incorrectly change the reference level

For the cases Philippe specifically called out:

% zsh-5.9.0.2-test nr-test.zsh -u 0
g:1: rs=f - ra=f - rs1=f - ra1=f
g:2: rs=f - ra=f - rs1=f - ra1=f
h:1: rs=f - ra=f - rs1=f - ra1=f
h:2: rs=f - ra=f - rs1=f - ra1=f
i:1: rs=f - ra=f - rs1=f - ra1=f
i:2: rs=f - ra=f - rs1=f - ra1=f
j:1: rs=f - ra=f - rs1=f - ra1=f
j:2: rs=f - ra=f - rs1=f - ra1=f
k:1: rs=f - ra=f - rs1=f - ra1=f
j:3: rs=f - ra=f - rs1=f - ra1=f
i:3: rs=f - ra=f - rs1=f - ra1=f
h:3: rs=f - ra=f - rs1=f - ra1=f
g:3: rs=f - ra=f - rs1=f - ra1=f

% zsh-5.9.0.2-test nr-test.zsh "" 0
g:1: rs=f - ra=f - rs1=f - ra1=f
g:2: rs=f - ra=f - rs1=f - ra1=f
h:1: rs=f - ra=f - rs1=f - ra1=f
h:2: rs=f - ra=f - rs1=f - ra1=f
i:1: rs=f - ra=f - rs1=f - ra1=f
i:2: rs=f - ra=f - rs1=f - ra1=f
j:1: rs=f - ra=f - rs1=f - ra1=f
j:2: rs=f - ra=f - rs1=f - ra1=f
k:1: rs=f - ra=f - rs1=f - ra1=f
j:3: rs=f - ra=f - rs1=f - ra1=f
i:3: rs=f - ra=f - rs1=f - ra1=f
h:3: rs=f - ra=f - rs1=f - ra1=f
g:3: rs=f - ra=f - rs1=f - ra1=f

Note that in those above two cases the subscript references match the
simple name reference, as they should have before.

% zsh-5.9.0.2-test nr-test.zsh "" 2
g:1: rs= - ra= - rs1= - ra1=
g:2: rs=g - ra=g - rs1=g - ra1=g
h:1: rs=g - ra=g - rs1=g - ra1=g
h:2: rs=g - ra=g - rs1=g - ra1=g
i:1: rs=g - ra=g - rs1=g - ra1=g
i:2: rs=g - ra=g - rs1=g - ra1=g
j:1: rs=g - ra=g - rs1=g - ra1=g
j:2: rs=g - ra=g - rs1=g - ra1=g
k:1: rs=g - ra=g - rs1=g - ra1=g
j:3: rs=g - ra=g - rs1=g - ra1=g
i:3: rs=g - ra=g - rs1=g - ra1=g
h:3: rs=g - ra=g - rs1=g - ra1=g
g:3: rs=g - ra=g - rs1=g - ra1=g

Just showing that the case that ran as expected, still does.

% zsh-5.9.0.2-test nr-test.zsh "" 4
g:1: rs= - ra= - rs1= - ra1=
g:2: rs= - ra= - rs1= - ra1=
h:1: rs= - ra= - rs1= - ra1=
h:2: rs=h - ra=h - rs1=h - ra1=h
i:1: rs=h - ra=h - rs1=h - ra1=h
i:2: rs=h - ra=h - rs1=h - ra1=h
j:1: rs=h - ra=h - rs1=h - ra1=h
j:2: rs=h - ra=h - rs1=h - ra1=h
k:1: rs=h - ra=h - rs1=h - ra1=h
j:3: rs=h - ra=h - rs1=h - ra1=h
i:3: rs=h - ra=h - rs1=h - ra1=h
h:3: rs=h - ra=h - rs1=h - ra1=h
g:3: rs=g - ra=g - rs1=g - ra1=g

The last line of that one deserves some explanation.  I'll include
test 7 for further example:

% zsh-5.9.0.2-test nr-test.zsh "" 7
g:1: rs= - ra= - rs1= - ra1=
g:2: rs= - ra= - rs1= - ra1=
h:1: rs= - ra= - rs1= - ra1=
h:2: rs= - ra= - rs1= - ra1=
i:1: rs= - ra= - rs1= - ra1=
i:2: rs= - ra= - rs1= - ra1=
VALUES 7
j:1: rs=i - ra=i - rs1=i - ra1=i
j:2: rs=i - ra=i - rs1=i - ra1=i
k:1: rs=i - ra=i - rs1=i - ra1=i
j:3: rs=i - ra=i - rs1=i - ra1=i
i:3: rs=i - ra=i - rs1=i - ra1=i
h:3: rs=h - ra=h - rs1=h - ra1=h
g:3: rs=g - ra=g - rs1=g - ra1=g

When the reference is finally assigned, it refers to the same variable
until that variable goes out of scope.  However, that does not change
the assigned value in the calling function, so when the reference is
accessed again in the calling scope it finds the corresponding name
there.

This behavior is required by K01 test "Global variable is a reference,
warning" and it's not clear that it's wrong -- if this were an
ordinary parameter, it would remain set in the calling function unless
explicitly unset.  However, an alternate behavior, such as making
lines h:3: and g:3: behave the same as g:2: and h:2:, is possible.
diff --git a/Src/params.c b/Src/params.c
index c10236a0d..cb1f6d4b0 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2231,7 +2231,13 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 		    *ss = 0;
 		}
 		Param p1 = (Param)gethashnode2(paramtab, ref);
-		if (!(p1 && (pm = upscope(p1, pm->base))) ||
+		if (p1) {
+		    int scope = ((pm->node.flags & PM_NAMEREF) ?
+				 ((pm->node.flags & PM_UPPER) ? -(pm->base) :
+				  pm->base) : locallevel);
+		    pm = upscope(p1, scope);
+		}
+		if (!(p1 && pm) ||
 		    ((pm->node.flags & PM_UNSET) &&
 		     !(pm->node.flags & PM_DECLARED)))
 		    return NULL;
@@ -5885,7 +5891,8 @@ scanendscope(HashNode hn, UNUSED(int flags))
 		export_param(pm);
 	} else
 	    unsetparam_pm(pm, 0, 0);
-    } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level)
+    } else if ((pm->node.flags & PM_NAMEREF) &&
+	       pm->base > pm->level && pm->base > locallevel)
 	pm->base = locallevel;
 }
 
@@ -6291,7 +6298,7 @@ resolve_nameref(Param pm, const Asgment stop)
 	    if (pm) {
 		if (!(stop && (stop->flags & (PM_LOCAL)))) {
 		    int scope = ((pm->node.flags & PM_NAMEREF) ?
-				 ((pm->node.flags & PM_UPPER) ? -1 :
+				 ((pm->node.flags & PM_UPPER) ? -(pm->base) :
 				  pm->base) : ((Param)hn)->level);
 		    hn = (HashNode)upscope((Param)hn, scope);
 		}
@@ -6365,6 +6372,7 @@ setscope(Param pm)
 	if (t) {
 	    pm->width = t - refname;
 	    *t = '[';
+	    refname = dupstrpfx(refname, pm->width);
 	}
 	if (basepm) {
 	    if (basepm->node.flags & PM_NAMEREF) {
@@ -6393,11 +6401,19 @@ setscope(Param pm)
 			break;
 		    }
 		}
-	    } else
+	    } else if (!pm->base) {
 		pm->base = basepm->level;
+		if ((pm->node.flags & PM_UPPER) &&
+		    (basepm = upscope(basepm, -(pm->level))))
+		    pm->base = basepm->level;
+	    }
 	} else if (pm->base < locallevel && refname &&
-		   (basepm = (Param)getparamnode(realparamtab, refname)))
+		   (basepm = (Param)getparamnode(realparamtab, refname))) {
 	    pm->base = basepm->level;
+	    if ((pm->node.flags & PM_UPPER) &&
+		(basepm = upscope(basepm, -(pm->level))))
+		pm->base = basepm->level;
+	}
 	if (pm->base > pm->level) {
 	    if (EMULATION(EMULATE_KSH)) {
 		zerr("%s: global reference cannot refer to local variable",
@@ -6422,7 +6438,7 @@ upscope(Param pm, int reflevel)
 {
     Param up = pm->old;
     while (up && up->level >= reflevel) {
-	if (reflevel < 0 && up->level < locallevel)
+	if (reflevel < 0 && up->level < -(reflevel))
 	    break;
 	pm = up;
 	up = up->old;


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