# difficult part is the proper expansion of macros when they are
# m4_require'd.
#
-# The implementation is based on two ideas, (i) using diversions to
+# The implementation is based on three ideas, (i) using diversions to
# prepare the expansion of the macro and its dependencies (by Franc,ois
-# Pinard), and (ii) expand the most recently m4_require'd macros _after_
-# the previous macros (by Axel Thimm).
+# Pinard), (ii) expand the most recently m4_require'd macros _after_
+# the previous macros (by Axel Thimm), and (iii) track instances of
+# provide before require (by Eric Blake).
#
#
# The first idea: why use diversions?
# GROW:
# BODY: TEST2a; TEST3; TEST2b: TEST1
#
-# The idea is simple, but the implementation is a bit evolved. If you
-# are like me, you will want to see the actual functioning of this
+# The idea is simple, but the implementation is a bit involved. If
+# you are like me, you will want to see the actual functioning of this
# implementation to be convinced. The next section gives the full
# details.
#
# BODY: empty
# GROW - 1: TEST2a
# diversions: GROW - 1, GROW, BODY |-
-# Than the content of the temporary diversion is moved to DUMP and the
+# Then the content of the temporary diversion is moved to DUMP and the
# temporary diversion is popped.
# DUMP: BODY
# BODY: TEST2a
# BODY: TEST2a
# GROW - 2: TEST3
# diversions: GROW - 2, GROW - 1, GROW, BODY |-
-# Than the diversion is appended to DUMP, and popped.
+# Then the diversion is appended to DUMP, and popped.
# DUMP: BODY
# BODY: TEST2a; TEST3
# diversions: GROW - 1, GROW, BODY |-
# diversions: BODY |-
#
#
+# The third idea: track macros provided before they were required
+# ---------------------------------------------------------------
+#
+# Using just the first two ideas, Autoconf 2.50 through 2.63 still had
+# a subtle bug for more than seven years. Let's consider the
+# following example to explain the bug:
+#
+# | m4_defun([TEST1], [1])
+# | m4_defun([TEST2], [2[]REQUIRE([TEST1])])
+# | m4_defun([TEST3], [3 TEST1 REQUIRE([TEST2])])
+# | TEST3
+#
+# After the prologue of TEST3, we are collecting text in GROW with the
+# intent of dumping it in BODY during the epilogue. Next, we
+# encounter the direct invocation of TEST1, which provides the macro
+# in place in GROW. From there, we encounter a requirement for TEST2,
+# which must be collected in a new diversion. While expanding TEST2,
+# we encounter a requirement for TEST1, but since it has already been
+# expanded, the Axel Thimm algorithm states that we can treat it as a
+# no-op. But that would lead to an end result of `2 3 1', meaning
+# that we have once again output a macro (TEST2) prior to its
+# requirements (TEST1).
+#
+# The problem can only occur if a single defun'd macro first provides,
+# then later indirectly requires, the same macro. Note that directly
+# expanding then requiring a macro is okay, since the requirement will
+# be a no-op; the problem is only present if the requirement is nested
+# inside a context that will be hoisted in front of the outermost
+# defun'd macro. In other words, we must be careful not to warn on:
+#
+# | m4_defun([TEST1], [1])
+# | m4_defun([TEST2], [TEST1 REQUIRE([TEST1])])
+#
+# The implementation of the warning involves tracking the set of
+# macros which have been provided since the start of the outermost
+# defun'd macro (the set is named _m4_provide). When starting an
+# outermost macro, the set is emptied; when a macro is provided, it is
+# added to the set; when require expands the body of a macro, it is
+# removed from the set; and when a macro is indirectly required (that
+# is, when m4_require detects a nested call), the set is checked. If
+# a macro is in the set, then it has been provided before it was
+# required, and a warning is issued because the output will be out of
+# order and the user must fix her macros to reflect the true
+# dependencies.
+#
+#
# 2. Keeping track of the expansion stack
# =======================================
#
[m4_expansion_stack_push([$1])m4_pushdef([_m4_expanding($1)])])
m4_define([_m4_defun_pro_outer],
+[m4_set_delete([_m4_provide])]dnl
[m4_pushdef([_m4_divert_dump], m4_divnum)m4_divert_push([GROW])])
# _m4_defun_epi(MACRO-NAME)
[m4_if(_m4_divert_dump, [],
[m4_fatal([$0($1): cannot be used outside of an ]dnl
m4_if([$0], [m4_require], [[m4_defun]], [[AC_DEFUN]])['d macro])])]dnl
-[m4_provide_if([$1], [],
- [_m4_require_call([$1], [$2], _m4_divert_dump)])])
+[m4_provide_if([$1], [m4_ifdef([_m4_require],
+ [m4_set_contains([_m4_provide], [$1],
+ [m4_warn([syntax], [$0: `$1' was expanded before it was required])])])],
+ [_m4_require_call([$1], [$2], _m4_divert_dump)])])
# _m4_require_call(NAME-TO-CHECK, [BODY-TO-EXPAND = NAME-TO-CHECK],
# by avoiding dnl and other overhead on the common path.
m4_define([_m4_require_call],
[m4_pushdef([_m4_divert_grow], m4_decr(_m4_divert_grow))]dnl
+[m4_pushdef([_m4_require])]dnl
[m4_divert_push(_m4_divert_grow)]dnl
[m4_if([$2], [], [$1], [$2])
-m4_provide_if([$1], [], [m4_warn([syntax],
- [$1 is m4_require'd but not m4_defun'd])])]dnl
+m4_provide_if([$1], [m4_set_remove([_m4_provide], [$1])],
+ [m4_warn([syntax], [$1 is m4_require'd but not m4_defun'd])])]dnl
[_m4_divert_raw($3)_m4_undivert(_m4_divert_grow)]dnl
-[m4_divert_pop(_m4_divert_grow)_m4_popdef([_m4_divert_grow])])
+[m4_divert_pop(_m4_divert_grow)_m4_popdef([_m4_divert_grow], [_m4_require])])
# _m4_divert_grow
# m4_provide(MACRO-NAME)
# ----------------------
m4_define([m4_provide],
-[m4_define([m4_provide($1)])])
+[m4_ifdef([m4_provide($1)], [],
+[m4_set_add([_m4_provide], [$1], [m4_define([m4_provide($1)])])])])
# m4_provide_if(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
post
]])
-dnl Direct invocation, top level
+dnl Direct invocation, nested requires, top level
AT_CHECK_M4SUGAR_TEXT([[dnl
m4_defun([a], [[a]])dnl
m4_defun([b], [[b]m4_require([a])])dnl
post
]])
-dnl Direct invocation, nested. This is an example of expansion before
-dnl requirement, such that b occurs before its prerequisite a. This
-dnl indicates a bug in the macros (but not in autoconf), so we should
-dnl be emitting a warning.
+dnl Direct invocation, nested requires, nested defun. This is an example
+dnl of expansion before requirement, such that b occurs before its
+dnl prerequisite a. This indicates a bug in the macros (but not in
+dnl autoconf), so we should be emitting a warning.
AT_CHECK_M4SUGAR_TEXT([[dnl
m4_defun([a], [[a]])dnl
m4_defun([b], [[b]m4_require([a])])dnl
a
c
post
-]], [stderr])
-AT_XFAIL_IF([:])
-AT_CHECK([test -s stderr])
+]],
+[[script.4s:14: warning: m4@&t@_require: `a' was expanded before it was required
+script.4s:5: b is expanded from...
+script.4s:6: c is expanded from...
+script.4s:7: outer is expanded from...
+script.4s:14: the top level
+]])
+
+dnl Direct invocation, expand-before-require but no nested require. As this
+dnl is common in real life, but does not result in out-of-order expansion,
+dnl we silently permit this.
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_defun([a], [[a]])dnl
+m4_defun([b], [[b]m4_require([a])])dnl
+m4_defun([c], [[c]])dnl
+m4_defun([d], [[d]m4_require([c])])dnl
+pre1
+a
+b
+a
+b
+post1
+m4_defun([outer],
+[pre2
+c
+d
+c
+d
+post2])dnl
+outer
+]],
+[[pre1
+a
+b
+a
+b
+post1
+pre2
+c
+d
+c
+d
+post2
+]])
AT_CLEANUP