xref: /freebsd/contrib/bmake/unit-tests/varname-empty.mk (revision a521f2116473fbd8c09db395518f060a27d02334)
1# $NetBSD: varname-empty.mk,v 1.7 2020/10/23 17:53:01 rillig Exp $
2#
3# Tests for the special variable with the empty name.
4#
5# There is no variable named "" at all, and this fact is used a lot in
6# variable expressions of the form ${:Ufallback}.  These expressions are
7# based on the variable named "" and use the :U modifier to assign a
8# fallback value to the expression (but not to the variable).
9#
10# This form of expressions is used to implement value substitution in the
11# .for loops.  Another use case is in a variable assignment of the form
12# ${:Uvarname}=value, which allows for characters in the variable name that
13# would otherwise be interpreted by the parser, such as whitespace, ':',
14# '=', '$', backslash.
15#
16# The only places where a variable is assigned a value are Var_Set and
17# Var_Append, and these places protect the variable named "" from being
18# defined.  This is different from read-only variables, as that flag can
19# only apply to variables that are defined.  The variable named "" must
20# never be defined though.
21#
22# See also:
23#	The special variables @F or ^D, in var-class-local.mk
24
25# Until 2020-08-22 it was possible to assign a value to the variable with
26# the empty name, leading to all kinds of unexpected effects in .for loops
27# and other places that assume that ${:Ufallback} expands to "fallback".
28# The bug in Var_Set was that only expanded variables had been checked for
29# the empty name, but not the direct assignments with an empty name.
30?=	default
31=	assigned	# undefined behavior until 2020-08-22
32+=	appended
33:=	subst
34!=	echo 'shell-output'
35.if ${:Ufallback} != "fallback"
36.  error
37.endif
38
39${:U}=	assigned indirectly
40.if ${:Ufallback} != "fallback"
41.  error
42.endif
43
44# Before 2020-08-22, the simple assignment operator '=' after an empty
45# variable name had an off-by-one bug in Parse_DoVar.  The code that was
46# supposed to "skip to operator character" started its search _after_ the
47# assignment operator, assuming that the variable name would be at least
48# one character long.  It then looked for the next occurrence of a '=', which
49# could be several lines away or not occur at all.  While looking for the
50# '=', some whitespace was nulled out, leading to out-of-bounds write.
51=	assigned	# undefined behavior until 2020-08-22
52
53# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on.
54# This only works if the variable with the empty name is guaranteed to
55# be undefined.
56.for i in 1 2 3
57NUMBERS+=	${i}
58.endfor
59
60all:
61	@echo out: ${:Ufallback}
62	@echo out: ${NUMBERS}
63