Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-04 Thread Bruno Haible
I did:
>   (error): Define as a macro that explicitly invokes exit().
>   (error_at_line): Likewise.

Oops. That patch broke the library namespacing via config.h. Namely,
when a package uses a config.h definition such as
  #define error libgettextpo_error
the intent is that the Gnulib module defines a function named
'libgettextpo_error', not 'error'.

With the error.in.h change, I now get redefinition warnings and compilation
errors (on platforms other than glibc and cygwin) such as

./error.h:458:11: warning: 'error' macro redefined [-Wmacro-redefined]
#  define error(status, ...) \
  ^
./config.h:51:9: note: previous definition is here
#define error libgettextpo_error
^

...

../../../gettext-tools/libgettextpo/gettext-po.c:1326:5: error: use of 
undeclared identifier 'error'
error (EXIT_FAILURE, 0, _("memory exhausted"));
^
./error.h:459:23: note: expanded from macro 'error'
 __gl_error_call (error, status, __VA_ARGS__)
  ^

The fix is:
  1) Capture the name of the error function through an static function.
  2) Since this static function is a varargs function and needs to call a
 varargs function, it needs to invoke __builtin_va_arg_pack().
 (The alternative would be to add a dependency from 'error' to the
 'verror' module. Which is not really better...)
  3) Since __builtin_va_arg_pack is only allowed in always-inline function,
 we need to mark it as _GL_ATTRIBUTE_ALWAYS_INLINE.
  4) The compiler then warns that the inlining may fail. To silence this
 warning, we need a '#pragma GCC diagnostic ignored "-Wattributes"'.
That's more involved than what I had expected.


2023-06-04  Bruno Haible  

error: Fix support for library namespacing (regression 2023-05-27).
* lib/error.in.h (error): If error is defined as a macro, define a
static inline function _gl_inline_error that invokes it, and let the
new error macro invoke that function.
(error_at_line): If error_at_line is defined as a macro, define a static
inline function _gl_inline_error_at_line that invokes it, and let the
new error_at_line macro invoke that function.

diff --git a/lib/error.in.h b/lib/error.in.h
index ef4b3c3815..94477fde08 100644
--- a/lib/error.in.h
+++ b/lib/error.in.h
@@ -30,7 +30,8 @@
 #ifndef _@GUARD_PREFIX@_ERROR_H
 #define _@GUARD_PREFIX@_ERROR_H
 
-/* This file uses _GL_ATTRIBUTE_FORMAT.  */
+/* This file uses _GL_ATTRIBUTE_ALWAYS_INLINE, _GL_ATTRIBUTE_FORMAT,
+  _GL_ATTRIBUTE_MAYBE_UNUSED.  */
 #if !_GL_CONFIG_H_INCLUDED
  #error "Please include config.h first."
 #endif
@@ -108,8 +109,28 @@ _GL_FUNCDECL_SYS (error, void,
 _GL_CXXALIAS_SYS (error, void,
   (int __status, int __errnum, const char *__format, ...));
 # ifndef _GL_NO_INLINE_ERROR
-#  define error(status, ...) \
- __gl_error_call (error, status, __VA_ARGS__)
+#  ifdef error
+/* Only gcc ≥ 4.7 has __builtin_va_arg_pack.  */
+#   if _GL_GNUC_PREREQ (4, 7)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+_GL_ATTRIBUTE_MAYBE_UNUSED
+static void
+_GL_ATTRIBUTE_ALWAYS_INLINE
+_GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_ERROR, 3, 4))
+_gl_inline_error (int __status, int __errnum, const char *__format, ...)
+{
+  return error (__status, __errnum, __format, __builtin_va_arg_pack ());
+}
+#pragma GCC diagnostic pop
+#undef error
+#define error(status, ...) \
+   __gl_error_call (_gl_inline_error, status, __VA_ARGS__)
+#   endif
+#  else
+#   define error(status, ...) \
+  __gl_error_call (error, status, __VA_ARGS__)
+#  endif
 # endif
 #endif
 #if __GLIBC__ >= 2
@@ -146,8 +167,30 @@ _GL_CXXALIAS_SYS (error_at_line, void,
   (int __status, int __errnum, const char *__filename,
unsigned int __lineno, const char *__format, ...));
 # ifndef _GL_NO_INLINE_ERROR
-#  define error_at_line(status, ...) \
- __gl_error_call (error_at_line, status, __VA_ARGS__)
+#  ifdef error_at_line
+/* Only gcc ≥ 4.7 has __builtin_va_arg_pack.  */
+#   if _GL_GNUC_PREREQ (4, 7)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+_GL_ATTRIBUTE_MAYBE_UNUSED
+static void
+_GL_ATTRIBUTE_ALWAYS_INLINE
+_GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_ERROR, 5, 6))
+_gl_inline_error_at_line (int __status, int __errnum, const char *__filename,
+  unsigned int __lineno, const char *__format, ...)
+{
+  return error_at_line (__status, __errnum, __filename, __lineno, __format,
+__builtin_va_arg_pack ());
+}
+#pragma GCC diagnostic pop
+#undef error_at_line
+#define error_at_line(status, ...) \
+   __gl_error_call (_gl_inline_error_at_line, status, __VA_ARGS__)
+#   endif
+#  else
+#   define error_at_line(status, ...) \
+  __gl_error_call (error_at_line, status, __VA_ARGS__)
+#  endif
 # endif
 #endif
 _GL_CXXALIASWARN (error_at_line);






Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-03 Thread Bruno Haible
Paul Eggert wrote:
> > How about changing this to use __builtin_constant_p instead of 
> > __OPTIMIZE__? I.e., use the comma expression if __builtin_constant_p 
> > (status), and use the statement expression otherwise.
> 
> I gave that a shot by installing the attached.

Oh, I see now what you meant. That's clever. So clever that it
deserves comments :-)


2023-06-03  Bruno Haible  

error: Improve comments.
* lib/error.in.h (__gl_error_call): Add more comments.

diff --git a/lib/error.in.h b/lib/error.in.h
index 279258f63e..ef4b3c3815 100644
--- a/lib/error.in.h
+++ b/lib/error.in.h
@@ -50,13 +50,21 @@
 #endif
 
 /* Helper macro for supporting the compiler's control flow analysis better.
-   It evaluates its arguments only once.  It uses __builtin_constant_p
-   and comma expressions to work around GCC false positives.
+   It evaluates its arguments only once.
Test case: Compile copy-file.c with "gcc -Wimplicit-fallthrough".  */
 #ifdef __GNUC__
+/* Use 'unreachable' to tell the compiler when the function call does not
+   return.  */
 # define __gl_error_call1(function, status, ...) \
 ((function) (status, __VA_ARGS__), \
  (status) != 0 ? unreachable () : (void) 0)
+/* If STATUS is a not a constant, the function call may or may not return;
+   therefore -Wimplicit-fallthrough will produce a warning.  Use a compound
+   statement in order to evaluate STATUS only once.
+   If STATUS is a constant, we don't use a compound statement, because that
+   would trigger a -Wimplicit-fallthrough warning even when STATUS is != 0,
+   when not optimizing.  This causes STATUS to be evaluated twice, but
+   that's OK since it does not have side effects.  */
 # define __gl_error_call(function, status, ...) \
 (__builtin_constant_p (status) \
  ? __gl_error_call1 (function, status, __VA_ARGS__) \






Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-02 Thread Paul Eggert

On 2023-06-02 15:03, Paul Eggert wrote:
How about changing this to use __builtin_constant_p instead of 
__OPTIMIZE__? I.e., use the comma expression if __builtin_constant_p 
(status), and use the statement expression otherwise.


I gave that a shot by installing the attached.From cfd41b3ac199a4f95fb78ee251e8c8a7c6f86ad1 Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Fri, 2 Jun 2023 22:30:52 -0700
Subject: [PATCH] error: do not evaluate status twice

Do this in a different way, so that the status is evaluated
once even when not optimizing and when using GCC.
* lib/error.in.h (__gl_error_call1) [__GNUC__]: New macro.
(__gl_error_call) [__GNUC__]: Use it.
---
 ChangeLog  |  8 
 lib/error.in.h | 27 ---
 2 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3187128c1f..fda2ed0e3f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-06-02  Paul Eggert  
+
+	error: do not evaluate status twice
+	Do this in a different way, so that the status is evaluated
+	once even when not optimizing and when using GCC.
+	* lib/error.in.h (__gl_error_call1) [__GNUC__]: New macro.
+	(__gl_error_call) [__GNUC__]: Use it.
+
 2023-06-02  Bruno Haible  
 
 	warnings: Add ability to inhibit all warnings.
diff --git a/lib/error.in.h b/lib/error.in.h
index f37dba170c..279258f63e 100644
--- a/lib/error.in.h
+++ b/lib/error.in.h
@@ -50,23 +50,20 @@
 #endif
 
 /* Helper macro for supporting the compiler's control flow analysis better.
+   It evaluates its arguments only once.  It uses __builtin_constant_p
+   and comma expressions to work around GCC false positives.
Test case: Compile copy-file.c with "gcc -Wimplicit-fallthrough".  */
 #ifdef __GNUC__
-/* Avoid evaluating STATUS twice, if this is possible without making the
-   "warning: this statement may fall through" reappear.  */
-# if __OPTIMIZE__
-#  define __gl_error_call(function, status, ...) \
- ({ \
-int const __errstatus = (status); \
-(function) (__errstatus, __VA_ARGS__); \
-__errstatus != 0 ? unreachable () : (void) 0; \
- })
-# else
-#  define __gl_error_call(function, status, ...) \
- ((function) (status, __VA_ARGS__), \
-  (status) != 0 ? unreachable () : (void)0 \
- )
-# endif
+# define __gl_error_call1(function, status, ...) \
+((function) (status, __VA_ARGS__), \
+ (status) != 0 ? unreachable () : (void) 0)
+# define __gl_error_call(function, status, ...) \
+(__builtin_constant_p (status) \
+ ? __gl_error_call1 (function, status, __VA_ARGS__) \
+ : ({ \
+ int const __errstatus = status; \
+ __gl_error_call1 (function, __errstatus, __VA_ARGS__); \
+   }))
 #else
 # define __gl_error_call(function, status, ...) \
 (function) (status, __VA_ARGS__)
-- 
2.39.2



Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-02 Thread Paul Eggert

On 6/2/23 11:10, Bruno Haible wrote:

+/* Avoid evaluating STATUS twice, if this is possible without making the
+   "warning: this statement may fall through" reappear.  */
+# if __OPTIMIZE__
+#  define __gl_error_call(function, status, ...) \


That means semantics are different when optimizing; generally speaking 
optimization should affect only performance, not correctness.


How about changing this to use __builtin_constant_p instead of 
__OPTIMIZE__? I.e., use the comma expression if __builtin_constant_p 
(status), and use the statement expression otherwise.




Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-02 Thread Bruno Haible
Paul Eggert wrote on 2023-05-30:
> > +#  define error(status, ...) \
> > + ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
> ...
> Also, it's better to not evaluate 'status' twice. Not that I think 
> 'status' should have side effects or even that it does have side effects 
> in any Gnulib-using code, just that it's more hygienic in case some 
> caller foolishly puts side effects there.

Unfortunately, this change makes the warning in copy-file.c reappear,
when compiling with gcc 13.1.0 and no optimization.

How to reproduce:

$ rm -rf ../testdir2; ./gnulib-tool --create-testdir --dir=../testdir2 
--single-configure copy-file

$ cd ../testdir2
$ ./configure CPPFLAGS=-ggdb CFLAGS=-Wimplicit-fallthrough
$ make
...
/inst-gcc/13.1.0/bin/gcc -ftrapv -DHAVE_CONFIG_H -I. -I..  
-DGNULIB_STRICT_CHECKING=1 -ggdb  -Wimplicit-fallthrough -MT copy-file.o -MD 
-MP -MF .deps/copy-file.Tpo -c -o copy-file.o copy-file.c
In file included from error.h:28,
 from copy-file.c:31:
copy-file.c: In function ‘copy_file_preserving’:
./error.h:392:6: warning: this statement may fall through 
[-Wimplicit-fallthrough=]
  392 | ({ \
  | ~^~~
  393 |int const __errstatus = status; \
  |~
  394 |(function) (__errstatus, __VA_ARGS__); \
  |
  395 |__errstatus != 0 ? unreachable () : (void) 0; \
  |~~~
  396 | })
  | ~~
./error.h:434:6: note: in expansion of macro ‘__gl_error_call’
  434 |  __gl_error_call (error, status, __VA_ARGS__)
  |  ^~~
copy-file.c:192:7: note: in expansion of macro ‘error’
  192 |   error (EXIT_FAILURE, errno, _("error while opening %s for 
reading"),
  |   ^
copy-file.c:195:5: note: here
  195 | case GL_COPY_ERR_OPEN_BACKUP_WRITE:
  | ^~~~
./error.h:392:6: warning: this statement may fall through 
[-Wimplicit-fallthrough=]
  392 | ({ \
  | ~^~~
  393 |int const __errstatus = status; \
  |~
  394 |(function) (__errstatus, __VA_ARGS__); \
  |
  395 |__errstatus != 0 ? unreachable () : (void) 0; \
  |~~~
  396 | })
  | ~~
./error.h:434:6: note: in expansion of macro ‘__gl_error_call’
  434 |  __gl_error_call (error, status, __VA_ARGS__)
  |  ^~~
copy-file.c:196:7: note: in expansion of macro ‘error’
  196 |   error (EXIT_FAILURE, errno, _("cannot open backup file %s for 
writing"),
  |   ^
copy-file.c:199:5: note: here
  199 | case GL_COPY_ERR_READ:
  | ^~~~
./error.h:392:6: warning: this statement may fall through 
[-Wimplicit-fallthrough=]
  392 | ({ \
  | ~^~~
  393 |int const __errstatus = status; \
  |~
  394 |(function) (__errstatus, __VA_ARGS__); \
  |
  395 |__errstatus != 0 ? unreachable () : (void) 0; \
  |~~~
  396 | })
  | ~~
./error.h:434:6: note: in expansion of macro ‘__gl_error_call’
  434 |  __gl_error_call (error, status, __VA_ARGS__)
  |  ^~~
copy-file.c:200:7: note: in expansion of macro ‘error’
  200 |   error (EXIT_FAILURE, errno, _("error reading %s"),
  |   ^
copy-file.c:203:5: note: here
  203 | case GL_COPY_ERR_WRITE:
  | ^~~~
./error.h:392:6: warning: this statement may fall through 
[-Wimplicit-fallthrough=]
  392 | ({ \
  | ~^~~
  393 |int const __errstatus = status; \
  |~
  394 |(function) (__errstatus, __VA_ARGS__); \
  |
  395 |__errstatus != 0 ? unreachable () : (void) 0; \
  |~~~
  396 | })
  | ~~
./error.h:434:6: note: in expansion of macro ‘__gl_error_call’
  434 |  __gl_error_call (error, status, __VA_ARGS__)
  |  ^~~
copy-file.c:204:7: note: in expansion of macro ‘error’
  204 |   error (EXIT_FAILURE, errno, _("error writing %s"),
  |   ^
copy-file.c:207:5: note: here
  207 | case GL_COPY_ERR_AFTER_READ:
  | ^~~~
./error.h:392:6: warning: this statement may fall through 
[-Wimplicit-fallthrough=]
  392 | ({ \
  | ~^~~
  393 |int const __errstatus = status; \
  |~
  394 |(function) (__errstatus, __VA_ARGS__); \
  |
  395 |__errstatus != 0 ? unreachable () : (void) 0; \
  |~

Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-01 Thread Pádraig Brady

On 01/06/2023 13:10, Bruno Haible wrote:

Pádraig Brady wrote:

Was there a reason to prefer curly braces there, rather than the more
conventional parentheses?

 '$(error_fns) \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \


I had a slight preference for the curly braces since it was used in a shell 
pipeline
and so it's immediately obvious it's a ${variable interpolation}
rather than being misread as a $(command substitution).


Sorry, but I find it as confusing as Jim. Inside the commands of a Makefile 
rule,
   $$x or $${x}   references a shell variable
   $$(x)  does a command substitution
   $(x) or ${x)   references a Makefile variable
   $(x y) or ${x y}   does a GNU make function call
For the latter two, the customary notation is with parentheses.

So, if someone writes  ${x)  or  ${x y}, the reader immediately wonders:
What is the point of the braces? Was it a typo, and they mean $${x} instead?


Fair point.
I've adjusted to consistent $() interpolation now.

cheers,
Pádraig




Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-01 Thread Bruno Haible
Pádraig Brady wrote:
> > Was there a reason to prefer curly braces there, rather than the more
> > conventional parentheses?
> > 
> > '$(error_fns) \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \
> 
> I had a slight preference for the curly braces since it was used in a shell 
> pipeline
> and so it's immediately obvious it's a ${variable interpolation}
> rather than being misread as a $(command substitution).

Sorry, but I find it as confusing as Jim. Inside the commands of a Makefile 
rule,
  $$x or $${x}   references a shell variable
  $$(x)  does a command substitution
  $(x) or ${x)   references a Makefile variable
  $(x y) or ${x y}   does a GNU make function call
For the latter two, the customary notation is with parentheses.

So, if someone writes  ${x)  or  ${x y}, the reader immediately wonders:
What is the point of the braces? Was it a typo, and they mean $${x} instead?

Bruno






Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-06-01 Thread Pádraig Brady

On 31/05/2023 18:46, Jim Meyering wrote:

On Wed, May 31, 2023 at 9:12 AM Pádraig Brady  wrote:

-   'error \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \
+   '${error_fns} \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \


Thanks!
Was there a reason to prefer curly braces there, rather than the more
conventional parentheses?

'$(error_fns) \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \


I had a slight preference for the curly braces since it was used in a shell 
pipeline
and so it's immediately obvious it's a ${variable interpolation}
rather than being misread as a $(command substitution).

cheers,
Pádraig



Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-31 Thread Jim Meyering
On Wed, May 31, 2023 at 9:12 AM Pádraig Brady  wrote:
> On 30/05/2023 22:29, Paul Eggert wrote:
> > On 5/28/23 06:07, Pádraig Brady wrote:
> >> There still is a gotcha (hit in dd.c in coreutils)
> >> where if you define an error macro yourself
> >> you get a macro redefinition error,
> >
> > I see you fixed that by adding a quick "#define _GL_NO_INLINE_ERROR" to
> > coreutils/src/dd.c. It's a bit cleaner to fix the underlying naming
> > problem instead, so that dd.c need not define the Gnulib internals macro
> > (or its own quirky error macro), so I installed the attached to
> > coreutils to do that.
>
> I was debating that option but decided against it
> as we'd then lose some of the syntax checks on error(args).
> But we can augment the syntax checks to cater for this class of function,
> which I'm doing as follows.
>
> cheers,
> Pádraig
>
> diff --git a/cfg.mk b/cfg.mk
> index 263bc0cfd..64db2bec4 100644
> --- a/cfg.mk
> +++ b/cfg.mk
> @@ -189,12 +189,15 @@ sc_prohibit_quotes_notation:
> exit 1; }  \
>|| :
>
> +error_fns = (error|die|diagnose)
> +
>   # Files in src/ should quote all strings in error() output, so that
>   # unexpected input chars like \r etc. don't corrupt the error.
>   # In edge cases this can be avoided by putting the format string
>   # on a separate line to the arguments, or the arguments in parenthesis.
>   sc_error_quotes:
> -   @cd $(srcdir)/src && GIT_PAGER= git grep -n 'error *(.*%s.*, 
> [^(]*);$$'\
> +   @cd $(srcdir)/src \
> + && GIT_PAGER= git grep -E -n '${error_fns} *\(.*%s.*, [^(]*\);$$' \
>*.c | grep -v ', q' \
>&& { echo '$(ME): '"Use quote() for error string arguments" 1>&2; \
> exit 1; }  \
> @@ -206,7 +209,7 @@ sc_error_quotes:
>   sc_error_shell_quotes:
>  @cd $(srcdir)/src && \
>{ GIT_PAGER= git grep -E \
> -   'error \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \
> +   '${error_fns} \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \

Thanks!
Was there a reason to prefer curly braces there, rather than the more
conventional parentheses?

   '$(error_fns) \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \



Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-31 Thread Pádraig Brady

On 30/05/2023 22:29, Paul Eggert wrote:

On 5/28/23 06:07, Pádraig Brady wrote:

There still is a gotcha (hit in dd.c in coreutils)
where if you define an error macro yourself
you get a macro redefinition error,


I see you fixed that by adding a quick "#define _GL_NO_INLINE_ERROR" to
coreutils/src/dd.c. It's a bit cleaner to fix the underlying naming
problem instead, so that dd.c need not define the Gnulib internals macro
(or its own quirky error macro), so I installed the attached to
coreutils to do that.


I was debating that option but decided against it
as we'd then lose some of the syntax checks on error(args).
But we can augment the syntax checks to cater for this class of function,
which I'm doing as follows.

cheers,
Pádraig

diff --git a/cfg.mk b/cfg.mk
index 263bc0cfd..64db2bec4 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -189,12 +189,15 @@ sc_prohibit_quotes_notation:
   exit 1; }  \
  || :

+error_fns = (error|die|diagnose)
+
 # Files in src/ should quote all strings in error() output, so that
 # unexpected input chars like \r etc. don't corrupt the error.
 # In edge cases this can be avoided by putting the format string
 # on a separate line to the arguments, or the arguments in parenthesis.
 sc_error_quotes:
-   @cd $(srcdir)/src && GIT_PAGER= git grep -n 'error *(.*%s.*, [^(]*);$$'\
+   @cd $(srcdir)/src \
+ && GIT_PAGER= git grep -E -n '${error_fns} *\(.*%s.*, [^(]*\);$$' \
  *.c | grep -v ', q' \
  && { echo '$(ME): '"Use quote() for error string arguments" 1>&2; \
   exit 1; }  \
@@ -206,7 +209,7 @@ sc_error_quotes:
 sc_error_shell_quotes:
@cd $(srcdir)/src && \
  { GIT_PAGER= git grep -E \
-   'error \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \
+   '${error_fns} \(.*%s[:"], .*(name|file)[^"]*\);$$' *.c; \
GIT_PAGER= git grep -E \
' quote[ _].*file' *.c; } \
  | grep -Ev '(quotef|q[^ ]*name)' \
@@ -220,13 +223,13 @@ sc_error_shell_quotes:
 # to provide better support for copy and paste.
 sc_error_shell_always_quotes:
@cd $(srcdir)/src && GIT_PAGER= git grep -E \
-   'error \(.*[^:] %s[ "].*, .*(name|file)[^"]*\);$$' \
+   '${error_fns} \(.*[^:] %s[ "].*, .*(name|file)[^"]*\);$$' \
*.c | grep -Ev '(quoteaf|q[^ ]*name)' \
  && { echo '$(ME): '"Use quoteaf() for space delimited names" 1>&2; \
   exit 1; }  \
  || :
@cd $(srcdir)/src && GIT_PAGER= git grep -E -A1 \
-   'error \([^%]*[^:] %s[ "]' *.c | grep 'quotef' \
+   '${error_fns} \([^%]*[^:] %s[ "]' *.c | grep 'quotef' \
  && { echo '$(ME): '"Use quoteaf() for space delimited names" 1>&2; \
   exit 1; }  \
  || :




Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-30 Thread Paul Eggert

On 5/30/23 15:06, Bruno Haible wrote:


In terms of functions calls, I don't think it makes a difference,
whether exit() gets called from within the error() invocation [1] or
after the error() invocation.


It makes the calling code a bit smaller, e.g., it avoids GCC having to 
generate code to save and restore registers in the caller, along with 
issuing two calls instead of one. Admittedly not a huge deal.


I also vaguely think that perhaps GCC will find the resulting code 
easier to analyze. Although this is just a guess, GCC is kinda buggy in 
this area and I hope the smaller code avoid bugs.




+ __gl_error_call (rpl_error, status, __VA_ARGS)


What is __VA_ARGS ? Did you mean __VA_ARGS__ ?


Yes I did. Thanks for catching that typo. I installed a fix.




Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-30 Thread Bruno Haible
Paul Eggert wrote:
> > +#  define error(status, ...) \
> > + ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
> 
> We can do a bit better than that by using 'unreachable ()' instead of 
> 'exit (status)', and passing 'status' (instead of 0) to the underlying 
> error function. This saves a function call

In terms of functions calls, I don't think it makes a difference,
whether exit() gets called from within the error() invocation [1] or
after the error() invocation. 

[1] lib/error.c:285

> Also, it's better to not evaluate 'status' twice. Not that I think 
> 'status' should have side effects or even that it does have side effects 
> in any Gnulib-using code, just that it's more hygienic in case some 
> caller foolishly puts side effects there.

Indeed, having it evaluate only once is better. Thanks.

> + __gl_error_call (rpl_error, status, __VA_ARGS)

What is __VA_ARGS ? Did you mean __VA_ARGS__ ?

Bruno






Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-30 Thread Paul Eggert

On 5/27/23 13:53, Bruno Haible wrote:

+#  define error(status, ...) \
+ ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)


We can do a bit better than that by using 'unreachable ()' instead of 
'exit (status)', and passing 'status' (instead of 0) to the underlying 
error function. This saves a function call and should still pacify GCC.


Also, it's better to not evaluate 'status' twice. Not that I think 
'status' should have side effects or even that it does have side effects 
in any Gnulib-using code, just that it's more hygienic in case some 
caller foolishly puts side effects there.


I installed the attached further patches into Gnulib to try to address 
these issues.From 8b21ff255996a518244a5635e36ea58e21f818be Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Tue, 30 May 2023 12:49:20 -0700
Subject: [PATCH 1/2] =?UTF-8?q?error:=20don=E2=80=99t=20evaluate=20status?=
 =?UTF-8?q?=20arg=20twice?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This avoids potential issues if the first argument has a side effect.
* lib/error.in.h (__gl_error_call): New macro, which evaluates its
status arg only once, by using a statement expression if GNU C -
the only platform we need to worry about pacifying - and by simply
calling ‘error’ otherwise.
(error, error_at_line): Use it.
---
 ChangeLog  | 10 ++
 lib/error.in.h | 20 
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 91a6135f8a..f70eeef893 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2023-05-30  Paul Eggert  
+
+	error: don’t evaluate status arg twice
+	This avoids potential issues if the first argument has a side effect.
+	* lib/error.in.h (__gl_error_call): New macro, which evaluates its
+	status arg only once, by using a statement expression if GNU C -
+	the only platform we need to worry about pacifying - and by simply
+	calling ‘error’ otherwise.
+	(error, error_at_line): Use it.
+
 2023-05-28  Bruno Haible  
 
 	warnings, manywarnings: Assume autoconf >= 2.64.
diff --git a/lib/error.in.h b/lib/error.in.h
index 70fb132133..a5f49e4143 100644
--- a/lib/error.in.h
+++ b/lib/error.in.h
@@ -49,6 +49,18 @@
 # define _GL_ATTRIBUTE_SPEC_PRINTF_ERROR _GL_ATTRIBUTE_SPEC_PRINTF_SYSTEM
 #endif
 
+#ifdef __GNUC__
+# define __gl_error_call(function, status, ...) \
+({ \
+   int const __errstatus = status; \
+   (function) (0, __VA_ARGS__); \
+   __errstatus != 0 ? exit (__errstatus) : (void) 0; \
+})
+#else
+# define __gl_error_call(function, status, ...) \
+(function) (status, __VA_ARGS__)
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -69,7 +81,7 @@ _GL_CXXALIAS_RPL (error, void,
 # ifndef _GL_NO_INLINE_ERROR
 #  undef error
 #  define error(status, ...) \
- ((rpl_error)(0, __VA_ARGS__), (status) != 0 ? exit (status) : (void)0)
+ __gl_error_call (rpl_error, status, __VA_ARGS)
 # endif
 #else
 # if ! @HAVE_ERROR@
@@ -81,7 +93,7 @@ _GL_CXXALIAS_SYS (error, void,
   (int __status, int __errnum, const char *__format, ...));
 # ifndef _GL_NO_INLINE_ERROR
 #  define error(status, ...) \
- ((error)(0, __VA_ARGS__), (status) != 0 ? exit (status) : (void)0)
+ __gl_error_call (error, status, __VA_ARGS__)
 # endif
 #endif
 #if __GLIBC__ >= 2
@@ -105,7 +117,7 @@ _GL_CXXALIAS_RPL (error_at_line, void,
 # ifndef _GL_NO_INLINE_ERROR
 #  undef error_at_line
 #  define error_at_line(status, ...) \
- ((rpl_error_at_line)(0, __VA_ARGS__), (status) != 0 ? exit (status) : (void)0)
+ __gl_error_call (rpl_error_at_line, status, __VA_ARGS__)
 # endif
 #else
 # if ! @HAVE_ERROR_AT_LINE@
@@ -119,7 +131,7 @@ _GL_CXXALIAS_SYS (error_at_line, void,
unsigned int __lineno, const char *__format, ...));
 # ifndef _GL_NO_INLINE_ERROR
 #  define error_at_line(status, ...) \
- ((error_at_line)(0, __VA_ARGS__), (status) != 0 ? exit (status) : (void)0)
+ __gl_error_call (error_at_line, status, __VA_ARGS__)
 # endif
 #endif
 _GL_CXXALIASWARN (error_at_line);
-- 
2.40.1

From 47811d1ac24e38e8842dc37e2e3cd3b4120338ad Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Tue, 30 May 2023 14:38:41 -0700
Subject: [PATCH 2/2] =?UTF-8?q?error:=20don=E2=80=99t=20call=20=E2=80=98ex?=
 =?UTF-8?q?it=E2=80=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Let the underlying functions call ‘exit’, instead of having the
Gnulib replacement macros do it.  Use ‘unreachable’ to tell the
compiler that those functions exit when the status is nonzero.
This saves a function call.
* lib/error.in.h: Include stddef.h, not stdlib.h.
(__gl_error_call): Rely on the function to exit, using
‘unreachable’ to tell the compiler that the function does not return.
* modules/error (Depends-on): Add stddef.
---
 ChangeLog  | 10 ++
 lib/error.in.h | 10 +-
 modules/error  |  1 +
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git

Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-30 Thread Paul Eggert

On 5/28/23 06:07, Pádraig Brady wrote:

There still is a gotcha (hit in dd.c in coreutils)
where if you define an error macro yourself
you get a macro redefinition error,


I see you fixed that by adding a quick "#define _GL_NO_INLINE_ERROR" to 
coreutils/src/dd.c. It's a bit cleaner to fix the underlying naming 
problem instead, so that dd.c need not define the Gnulib internals macro 
(or its own quirky error macro), so I installed the attached to 
coreutils to do that.
From 48f5a39872acbf3f23f02cf3216a6e30d00db671 Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Tue, 30 May 2023 14:24:46 -0700
Subject: [PATCH] =?UTF-8?q?dd:=20fix=20=E2=80=98error=E2=80=99=20name=20is?=
 =?UTF-8?q?sue=20without=20macros?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* src/dd.c (_GL_NO_INLINE_ERROR): Remove; no longer needed.
(diagnose): Rename from nl_error and omit first arg since it is
always zero.  All uses changed.
(error): Remove macro.
---
 src/dd.c | 109 ++-
 1 file changed, 52 insertions(+), 57 deletions(-)

diff --git a/src/dd.c b/src/dd.c
index f41966969..8fcfdd3db 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -17,7 +17,6 @@
 /* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */
 
 #include 
-#define _GL_NO_INLINE_ERROR  /* Avoid gnulib's error macro. */
 
 #include 
 #include 
@@ -515,11 +514,12 @@ maybe_close_stdout (void)
 _exit (EXIT_FAILURE);
 }
 
-/* Like the 'error' function but handle any pending newline.  */
+/* Like the 'error' function but handle any pending newline,
+   and do not exit.  */
 
-ATTRIBUTE_FORMAT ((__printf__, 3, 4))
+ATTRIBUTE_FORMAT ((__printf__, 2, 3))
 static void
-nl_error (int status, int errnum, char const *fmt, ...)
+diagnose (int errnum, char const *fmt, ...)
 {
   if (0 < progress_len)
 {
@@ -529,12 +529,10 @@ nl_error (int status, int errnum, char const *fmt, ...)
 
   va_list ap;
   va_start (ap, fmt);
-  verror (status, errnum, fmt, ap);
+  verror (0, errnum, fmt, ap);
   va_end (ap);
 }
 
-#define error nl_error
-
 void
 usage (int status)
 {
@@ -1138,12 +1136,12 @@ iread (int fd, char *buf, idx_t size)
 {
   idx_t prev = prev_nread;
   if (status_level != STATUS_NONE)
-error (0, 0, ngettext (("warning: partial read (%td byte); "
+diagnose (0, ngettext (("warning: partial read (%td byte); "
 "suggest iflag=fullblock"),
("warning: partial read (%td bytes); "
 "suggest iflag=fullblock"),
select_plural (prev)),
-   prev);
+  prev);
   warn_partial_read = false;
 }
 }
@@ -1188,8 +1186,8 @@ iwrite (int fd, char const *buf, idx_t size)
   int old_flags = fcntl (STDOUT_FILENO, F_GETFL);
   if (fcntl (STDOUT_FILENO, F_SETFL, old_flags & ~O_DIRECT) != 0
   && status_level != STATUS_NONE)
-error (0, errno, _("failed to turn off O_DIRECT: %s"),
-   quotef (output_file));
+diagnose (errno, _("failed to turn off O_DIRECT: %s"),
+  quotef (output_file));
 
   /* Since we have just turned off O_DIRECT for the final write,
  we try to preserve some of its semantics.  */
@@ -1263,7 +1261,7 @@ write_output (void)
   w_bytes += nwritten;
   if (nwritten != output_blocksize)
 {
-  error (0, errno, _("writing to %s"), quoteaf (output_file));
+  diagnose (errno, _("writing to %s"), quoteaf (output_file));
   if (nwritten != 0)
 w_partial++;
   quit (EXIT_FAILURE);
@@ -1392,8 +1390,9 @@ parse_symbols (char const *str, struct symbol_value const *table,
   if (! entry->symbol[0])
 {
   idx_t slen = strcomma ? strcomma - str : strlen (str);
-  error (0, 0, "%s: %s", _(error_msgid),
- quotearg_n_style_mem (0, locale_quoting_style, str, slen));
+  diagnose (0, "%s: %s", _(error_msgid),
+quotearg_n_style_mem (0, locale_quoting_style,
+  str, slen));
   usage (EXIT_FAILURE);
 }
 }
@@ -1456,10 +1455,9 @@ parse_integer (char const *str, strtol_error *invalid)
   else
 {
   if (result == 0 && STRPREFIX (str, "0x"))
-error (0, 0,
-   _("warning: %s is a zero multiplier; "
- "use %s if that is intended"),
-   quote_n (0, "0x"), quote_n (1, "00x"));
+diagnose (0, _("warning: %s is a zero multiplier; "
+   "use %s if that is intended"),
+  quote_n (0, "0x"), quote_n (1, "00x"));
   e = LONGINT_OK;
 }
 }
@@ -1500,8 +1498,7 @@ scanargs (int argc, char *const *argv)
 
   if (val == NULL)
 {
-  error (0, 0, _("unr

Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-28 Thread Pádraig Brady

On 28/05/2023 13:50, Pádraig Brady wrote:

On 28/05/2023 13:31, Pádraig Brady wrote:

On 27/05/2023 21:53, Bruno Haible wrote:

+# ifndef _GL_NO_INLINE_ERROR
+#  undef error
+#  define error(status, ...) \
+ ((rpl_error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
+# endif


We might need to cast STATUS to bool to avoid the
following failure from coreutils CI:

In file included from src/die.h:22,
from src/chroot.c:27:
src/chroot.c: In function 'main':
src/chroot.c:362:25: error: '?:'using integer constants in boolean context 
[-Werror=int-in-bool-context]
 362 | error (warn ? 0 : EXIT_CANCELED, 0, "%s", (err));
./lib/error.h:422:33: note: in definition of macro 'error'
 422 |  ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
 | ^~


Actually casting with (bool), or using !! does NOT help here.

It looks to be due to the nested use of ?: that's triggering the issue.

$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110


To avoid this one can use `(status) != 0`.

There still is a gotcha (hit in dd.c in coreutils)
where if you define an error macro yourself
you get a macro redefinition error,
but that's something we can/should handle on the coreutils side anyway.

cheers,
Pádraig




Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-28 Thread Pádraig Brady

On 28/05/2023 13:31, Pádraig Brady wrote:

On 27/05/2023 21:53, Bruno Haible wrote:

+# ifndef _GL_NO_INLINE_ERROR
+#  undef error
+#  define error(status, ...) \
+ ((rpl_error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
+# endif


We might need to cast STATUS to bool to avoid the
following failure from coreutils CI:

In file included from src/die.h:22,
   from src/chroot.c:27:
src/chroot.c: In function 'main':
src/chroot.c:362:25: error: '?:'using integer constants in boolean context 
[-Werror=int-in-bool-context]
362 | error (warn ? 0 : EXIT_CANCELED, 0, "%s", (err));
./lib/error.h:422:33: note: in definition of macro 'error'
422 |  ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
| ^~


Actually casting with (bool), or using !! does NOT help here.

It looks to be due to the nested use of ?: that's triggering the issue.

$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110

cheers,
Pádraig



Re: error: Support the compiler's control flow analysis better (was: copy-file: Silence gcc warnings)

2023-05-28 Thread Pádraig Brady

On 27/05/2023 21:53, Bruno Haible wrote:

Paul Eggert wrote:

Maybe by defining
error and error_at_line as inline functions


They're defined by glibc, no? The definitions might collide.


Yes, they are defined in glibc. Overriding functions without causing
collisions is our core expertise here :)


Also, I suppose the compiler might not inline them


Indeed, when I attempt to define
   void inline_error (int status, int errnum, const char *fmt, ...) { ... }
gcc does not inline the function, because it never inlines varargs functions.

An alternative would be to define
   void inline_error (int status, int errnum, const char *message) { ... }
and pass xasprintf (fmt, ...) as message argument. But the out-of-memory
handling or the LGPLv2+ / GPL license difference causes problems.

Fortunately, we don't need an inline function: Jim's die() implementation
shows that it can be done with just a macro definition.


The basic problem is that the old 'error' API doesn't mix well with
[[noreturn]] and the like. We could write a new function, "eexit" say,
that behaves like "error" except it never returns. (I chose the name
"eexit" so as to not mess up the indenting of existing code.)

Or we could just live with "die", as it works.


While this is definitely working, it has the problem that it pulls
developers away from the glibc API. Things are easier for developers
if they can use the symbols from POSIX or glibc rather than gnulib-
invented symbols. And that is possible here.

Also, the name 'die' has the problem that it may invoke traumatic
memories in some people (like the verb 'kill', but we can't get rid
of this one since it's standardized).



+# ifndef _GL_NO_INLINE_ERROR
+#  undef error
+#  define error(status, ...) \
+ ((rpl_error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
+# endif


We might need to cast STATUS to bool to avoid the
following failure from coreutils CI:

In file included from src/die.h:22,
 from src/chroot.c:27:
src/chroot.c: In function 'main':
src/chroot.c:362:25: error: '?:'using integer constants in boolean context 
[-Werror=int-in-bool-context]
  362 | error (warn ? 0 : EXIT_CANCELED, 0, "%s", (err));
./lib/error.h:422:33: note: in definition of macro 'error'
  422 |  ((error)(0, __VA_ARGS__), (status) ? exit (status) : (void)0)
  | ^~


cheers,
Pádraig