xref: /freebsd/contrib/bmake/unit-tests/varname-empty.mk (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1# $NetBSD: varname-empty.mk,v 1.10 2023/11/19 21:47:52 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# 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${:U}+=	appended indirectly
45.if ${:Ufallback} != "fallback"
46.  error
47.endif
48
49.MAKEFLAGS: -d0
50
51# Before 2020-08-22, the simple assignment operator '=' after an empty
52# variable name had an off-by-one bug in Parse_Var.  The code that was
53# supposed to "skip to operator character" started its search _after_ the
54# assignment operator, assuming that the variable name would be at least
55# one character long.  It then looked for the next occurrence of a '=', which
56# could be several lines away or not occur at all.  While looking for the
57# '=', some whitespace was nulled out, leading to out-of-bounds write.
58=	assigned	# undefined behavior until 2020-08-22
59
60# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on.
61# This only works if the variable with the empty name is guaranteed to
62# be undefined.
63.for i in 1 2 3
64NUMBERS+=	${i}
65.endfor
66
67all:
68	@echo out: ${:Ufallback}
69	@echo out: ${NUMBERS}
70