xref: /freebsd/contrib/bmake/unit-tests/varname-empty.mk (revision d5e0a182cf153f8993a633b93d9220c99a89e760)
1*d5e0a182SSimon J. Gerraty# $NetBSD: varname-empty.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
22c3632d1SSimon J. Gerraty#
32c3632d1SSimon J. Gerraty# Tests for the special variable with the empty name.
42c3632d1SSimon J. Gerraty#
5956e45f6SSimon J. Gerraty# There is no variable named "" at all, and this fact is used a lot in
6*d5e0a182SSimon J. Gerraty# expressions of the form ${:Ufallback}.  These expressions are
7956e45f6SSimon J. Gerraty# based on the variable named "" and use the :U modifier to assign a
8956e45f6SSimon J. Gerraty# fallback value to the expression (but not to the variable).
9956e45f6SSimon J. Gerraty#
10956e45f6SSimon J. Gerraty# This form of expressions is used to implement value substitution in the
11956e45f6SSimon J. Gerraty# .for loops.  Another use case is in a variable assignment of the form
12956e45f6SSimon J. Gerraty# ${:Uvarname}=value, which allows for characters in the variable name that
13956e45f6SSimon J. Gerraty# would otherwise be interpreted by the parser, such as whitespace, ':',
14956e45f6SSimon J. Gerraty# '=', '$', backslash.
15956e45f6SSimon J. Gerraty#
16956e45f6SSimon J. Gerraty# The only places where a variable is assigned a value are Var_Set and
17956e45f6SSimon J. Gerraty# Var_Append, and these places protect the variable named "" from being
18956e45f6SSimon J. Gerraty# defined.  This is different from read-only variables, as that flag can
19956e45f6SSimon J. Gerraty# only apply to variables that are defined.  The variable named "" must
20956e45f6SSimon J. Gerraty# never be defined though.
21956e45f6SSimon J. Gerraty#
22956e45f6SSimon J. Gerraty# See also:
23956e45f6SSimon J. Gerraty#	The special variables @F or ^D, in var-class-local.mk
242c3632d1SSimon J. Gerraty
252c3632d1SSimon J. Gerraty# Until 2020-08-22 it was possible to assign a value to the variable with
26956e45f6SSimon J. Gerraty# the empty name, leading to all kinds of unexpected effects in .for loops
27956e45f6SSimon J. Gerraty# and other places that assume that ${:Ufallback} expands to "fallback".
28956e45f6SSimon J. Gerraty# The bug in Var_Set was that only expanded variables had been checked for
29956e45f6SSimon J. Gerraty# the empty name, but not the direct assignments with an empty name.
302c3632d1SSimon J. Gerraty?=	default
312c3632d1SSimon J. Gerraty=	assigned	# undefined behavior until 2020-08-22
322c3632d1SSimon J. Gerraty+=	appended
332c3632d1SSimon J. Gerraty:=	subst
342c3632d1SSimon J. Gerraty!=	echo 'shell-output'
35956e45f6SSimon J. Gerraty.if ${:Ufallback} != "fallback"
36956e45f6SSimon J. Gerraty.  error
37956e45f6SSimon J. Gerraty.endif
38956e45f6SSimon J. Gerraty
39956e45f6SSimon J. Gerraty${:U}=	assigned indirectly
40956e45f6SSimon J. Gerraty.if ${:Ufallback} != "fallback"
41956e45f6SSimon J. Gerraty.  error
42956e45f6SSimon J. Gerraty.endif
43956e45f6SSimon J. Gerraty
44dba7b0efSSimon J. Gerraty${:U}+=	appended indirectly
45dba7b0efSSimon J. Gerraty.if ${:Ufallback} != "fallback"
46dba7b0efSSimon J. Gerraty.  error
47dba7b0efSSimon J. Gerraty.endif
48dba7b0efSSimon J. Gerraty
49dba7b0efSSimon J. Gerraty.MAKEFLAGS: -d0
50dba7b0efSSimon J. Gerraty
51956e45f6SSimon J. Gerraty# Before 2020-08-22, the simple assignment operator '=' after an empty
52b0c40a00SSimon J. Gerraty# variable name had an off-by-one bug in Parse_Var.  The code that was
53956e45f6SSimon J. Gerraty# supposed to "skip to operator character" started its search _after_ the
54956e45f6SSimon J. Gerraty# assignment operator, assuming that the variable name would be at least
55956e45f6SSimon J. Gerraty# one character long.  It then looked for the next occurrence of a '=', which
56956e45f6SSimon J. Gerraty# could be several lines away or not occur at all.  While looking for the
57956e45f6SSimon J. Gerraty# '=', some whitespace was nulled out, leading to out-of-bounds write.
58956e45f6SSimon J. Gerraty=	assigned	# undefined behavior until 2020-08-22
592c3632d1SSimon J. Gerraty
602c3632d1SSimon J. Gerraty# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on.
612c3632d1SSimon J. Gerraty# This only works if the variable with the empty name is guaranteed to
622c3632d1SSimon J. Gerraty# be undefined.
632c3632d1SSimon J. Gerraty.for i in 1 2 3
642c3632d1SSimon J. GerratyNUMBERS+=	${i}
652c3632d1SSimon J. Gerraty.endfor
662c3632d1SSimon J. Gerraty
672c3632d1SSimon J. Gerratyall:
682c3632d1SSimon J. Gerraty	@echo out: ${:Ufallback}
692c3632d1SSimon J. Gerraty	@echo out: ${NUMBERS}
70