Module Name:    src
Committed By:   rillig
Date:           Sun Dec 27 22:29:37 UTC 2020

Modified Files:
        src/usr.bin/make: parse.c
        src/usr.bin/make/unit-tests: var-op-expand.mk

Log Message:
make(1): fix edge case in := with undefined in variable name

Previously, the assignment "VAR${UNDEF} := value" actually assigned to 2
variables.  See var-op-expand.mk for details.


To generate a diff of this commit:
cvs rdiff -u -r1.519 -r1.520 src/usr.bin/make/parse.c
cvs rdiff -u -r1.7 -r1.8 src/usr.bin/make/unit-tests/var-op-expand.mk

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/parse.c
diff -u src/usr.bin/make/parse.c:1.519 src/usr.bin/make/parse.c:1.520
--- src/usr.bin/make/parse.c:1.519	Sun Dec 27 18:22:28 2020
+++ src/usr.bin/make/parse.c	Sun Dec 27 22:29:37 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: parse.c,v 1.519 2020/12/27 18:22:28 rillig Exp $	*/
+/*	$NetBSD: parse.c,v 1.520 2020/12/27 22:29:37 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -117,7 +117,7 @@
 #include "pathnames.h"
 
 /*	"@(#)parse.c	8.3 (Berkeley) 3/19/94"	*/
-MAKE_RCSID("$NetBSD: parse.c,v 1.519 2020/12/27 18:22:28 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.520 2020/12/27 22:29:37 rillig Exp $");
 
 /* types and constants */
 
@@ -1931,17 +1931,6 @@ VarAssign_EvalSubst(const char *name, co
 	char *evalue;
 	Boolean savedPreserveUndefined = preserveUndefined;
 
-	/* TODO: Can this assignment to preserveUndefined be moved further down
-	 * to the actually interesting Var_Subst call, without affecting any
-	 * edge cases?
-	 *
-	 * It might affect the implicit expansion of the variable name in the
-	 * Var_Exists and Var_Set calls, even though it's unlikely that anyone
-	 * cared about this edge case when adding this code.  In addition,
-	 * variable assignments should not refer to any undefined variables in
-	 * the variable name. */
-	preserveUndefined = TRUE;
-
 	/*
 	 * make sure that we set the variable the first time to nothing
 	 * so that it gets substituted!
@@ -1949,9 +1938,11 @@ VarAssign_EvalSubst(const char *name, co
 	if (!Var_Exists(name, ctxt))
 		Var_Set(name, "", ctxt);
 
+	preserveUndefined = TRUE;
 	(void)Var_Subst(uvalue, ctxt, VARE_WANTRES | VARE_KEEP_DOLLAR, &evalue);
-	/* TODO: handle errors */
 	preserveUndefined = savedPreserveUndefined;
+	/* TODO: handle errors */
+
 	avalue = evalue;
 	Var_Set(name, avalue, ctxt);
 

Index: src/usr.bin/make/unit-tests/var-op-expand.mk
diff -u src/usr.bin/make/unit-tests/var-op-expand.mk:1.7 src/usr.bin/make/unit-tests/var-op-expand.mk:1.8
--- src/usr.bin/make/unit-tests/var-op-expand.mk:1.7	Sun Dec 27 21:31:27 2020
+++ src/usr.bin/make/unit-tests/var-op-expand.mk	Sun Dec 27 22:29:37 2020
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-expand.mk,v 1.7 2020/12/27 21:31:27 rillig Exp $
+# $NetBSD: var-op-expand.mk,v 1.8 2020/12/27 22:29:37 rillig Exp $
 #
 # Tests for the := variable assignment operator, which expands its
 # right-hand side.
@@ -116,26 +116,25 @@ VAR:=		top:$$ ${:Unest1\:\$\$} ${:Unest2
 .endif
 
 
-# XXX: edge case: When a variable name refers to an undefined variable, the
-# behavior differs between the '=' and the ':=' assignment operators.
-# This bug exists since var.c 1.42 from 2000-05-11.
-#
-# The '=' operator expands the undefined variable to an empty string, thus
-# assigning to VAR_ASSIGN_.  In the name of variables to be set, it should
-# really be forbidden to refer to undefined variables.
-#
-# The ':=' operator expands the variable name twice.  In one of these
-# expansions, the undefined variable expression is preserved (controlled by
-# preserveUndefined in VarAssign_EvalSubst), in the other expansion it expands
-# to an empty string.  This way, 2 variables are created using a single
-# variable assignment.  It's magic. :-/
+# Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27,
+# if the variable name in a ':=' assignment referred to an undefined variable,
+# there were actually 2 assignments to different variables:
+#
+#	Global["VAR_SUBST_${UNDEF}"] = ""
+#	Global["VAR_SUBST_"] = ""
+#
+# The variable name with the empty value actually included a dollar sign.
+# Variable names with dollars are not used in practice.
+#
+# It might be a good idea to forbid undefined variables on the left-hand side
+# of a variable assignment.
 .undef UNDEF
 VAR_ASSIGN_${UNDEF}=	assigned by '='
 VAR_SUBST_${UNDEF}:=	assigned by ':='
 .if ${VAR_ASSIGN_} != "assigned by '='"
 .  error
 .endif
-.if ${${:UVAR_SUBST_\${UNDEF\}}} != ""
+.if defined(${:UVAR_SUBST_\${UNDEF\}})
 .  error
 .endif
 .if ${VAR_SUBST_} != "assigned by ':='"

Reply via email to