Thread: pg_attribute_noreturn(), MSVC, C11

pg_attribute_noreturn(), MSVC, C11

From
Andres Freund
Date:
Hi,

I just encountered another
  warning C4715: 'XYZ: not all control paths return a value

with msvc in CI in a case where it should be trivial for the compiler to
recognize that the function return isn't reachable.

Which made me check if these days msvc has something like gcc's
__attribute__((noreturn)).

And it turns out that yes! The _Noreturn attribute has been added to C11 and
msvc supports C11:
  https://learn.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170

Besides the _Noreturn keyword the standard also added a stdnoreturn.h which
provides the 'noreturn' macro.


I first thought we could just implement pg_attribute_noreturn() using
_Noreturn if available. However, our current pg_attribute_noreturn() is in the
wrong place for that to work :(.  _Noreturn is to be placed with the return
type, whereas function attributes with the __attribute__(()) syntax are after
the parameter list.

So we probably can't transparently switch between these.


C11 has been out a while, so I'm somewhat inclined to adopt _Noreturn/noreturn
in a conditional way. Older compilers would still work, just not understand
noreturn.

One wrinkle: _Noreturn/noreturn have been deprecated in C23, because that
adopted C++11's attribute syntax (i.e. [[noreturn]]). But that's at least in
the same place as _Noreturn/return.

We can't remove [[noreturn]] with preprocessor magic, so it's not really
viable to use that for, uhm, quite a while.

If we were to use _Noreturn, I think it could just be something like:

I think it should suffice to do something like

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define pg_noreturn _Noreturn
#else
#define pg_noreturn
#endif

(or alternatively include stdreturn if __STDC_VERSION__ indicates support and
define a bare 'noreturn' if not)

For msvc that mean we'd need to add /std:c11 (or /std:c17) to the compiler
flags, as it otherwise it results in a weird mix of c89 an c99). But that
might be a good idea anyway. With one minor change [1] the tests pass with
msvc when using /std:c17.


Before realizing that we'd have to change our existing annotations and that
there's no way to use both old and new syntax, depending on compiler support,
I was thinking this would be an obvious thing to do.  I'm still leaning on it
being worth it, but not as clearly as before.

For an example of _Noreturn being used: https://godbolt.org/z/j15d35dao

Greetings,

Andres Freund


[1] The msvc implementation of VA_ARGS_NARGS only works with the traditional
    preprocessor, but C11 support enables the new one. But we can detect that
    with something like
    (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL)

    See https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
    and https://learn.microsoft.com/en-us/cpp/build/reference/std-specify-language-standard-version?view=msvc-170

    Without adapting the definition of VA_ARGS_NARGS compilation fails as it
    results in the macro resulting in VA_ARGS_NARGS_(prefix, 63, 62, ...) in
    the using code...



Re: pg_attribute_noreturn(), MSVC, C11

From
Andres Freund
Date:
Hi,

On 2024-12-13 14:10:13 -0500, Andres Freund wrote:
> I just encountered another
>   warning C4715: 'XYZ: not all control paths return a value
> 
> with msvc in CI in a case where it should be trivial for the compiler to
> recognize that the function return isn't reachable.
> 
> Which made me check if these days msvc has something like gcc's
> __attribute__((noreturn)).
> 
> And it turns out that yes! The _Noreturn attribute has been added to C11 and
> msvc supports C11:
>   https://learn.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170
> 
> Besides the _Noreturn keyword the standard also added a stdnoreturn.h which
> provides the 'noreturn' macro.
> 
> 
> I first thought we could just implement pg_attribute_noreturn() using
> _Noreturn if available. However, our current pg_attribute_noreturn() is in the
> wrong place for that to work :(.  _Noreturn is to be placed with the return
> type, whereas function attributes with the __attribute__(()) syntax are after
> the parameter list.

The point about __attribute__(()) being after the parameter list is wrong, I
confused myself there. But that doesn't change that much, the common current
placement doesn't work for _Noreturn.


> C11 has been out a while, so I'm somewhat inclined to adopt _Noreturn/noreturn
> in a conditional way. Older compilers would still work, just not understand
> noreturn.
> 
> One wrinkle: _Noreturn/noreturn have been deprecated in C23, because that
> adopted C++11's attribute syntax (i.e. [[noreturn]]). But that's at least in
> the same place as _Noreturn/return.
> 
> We can't remove [[noreturn]] with preprocessor magic, so it's not really
> viable to use that for, uhm, quite a while.
> 
> If we were to use _Noreturn, I think it could just be something like:
> 
> I think it should suffice to do something like
> 
> #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
> #define pg_noreturn _Noreturn
> #else
> #define pg_noreturn
> #endif
> 
> (or alternatively include stdreturn if __STDC_VERSION__ indicates support and
> define a bare 'noreturn' if not)

Another wrinkle: While __attribute__((noreturn)) works for function pointers
(or function pointer typedefs) _Noreturn doesn't. Gah.  We only use it that
way in two places, but still :(

Greetings,

Andres Freund



Re: pg_attribute_noreturn(), MSVC, C11

From
Peter Eisentraut
Date:
On 13.12.24 20:10, Andres Freund wrote:
> C11 has been out a while, so I'm somewhat inclined to adopt _Noreturn/noreturn
> in a conditional way. Older compilers would still work, just not understand
> noreturn.
> 
> One wrinkle: _Noreturn/noreturn have been deprecated in C23, because that
> adopted C++11's attribute syntax (i.e. [[noreturn]]). But that's at least in
> the same place as _Noreturn/return.
> 
> We can't remove [[noreturn]] with preprocessor magic, so it's not really
> viable to use that for, uhm, quite a while.
> 
> If we were to use _Noreturn, I think it could just be something like:
> 
> I think it should suffice to do something like
> 
> #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
> #define pg_noreturn _Noreturn
> #else
> #define pg_noreturn
> #endif

This looks reasonable to me.  We also have pg_nodiscard.  (That's got a 
slightly different history in the C standard, but I mean it's also 
"pg_someattribute".)

> (or alternatively include stdreturn if __STDC_VERSION__ indicates support and
> define a bare 'noreturn' if not)
> 
> For msvc that mean we'd need to add /std:c11 (or /std:c17) to the compiler
> flags, as it otherwise it results in a weird mix of c89 an c99). But that
> might be a good idea anyway. With one minor change [1] the tests pass with
> msvc when using /std:c17.

According to my notes, C11 requires MSVC 2019, and we currently require 
2015, so this will require a bit of logic.



Re: pg_attribute_noreturn(), MSVC, C11

From
Peter Eisentraut
Date:
On 13.12.24 20:54, Andres Freund wrote:
> Another wrinkle: While __attribute__((noreturn)) works for function pointers
> (or function pointer typedefs) _Noreturn doesn't. Gah.  We only use it that
> way in two places, but still :(

Yeah, I wrote an experimental patch for noreturn support some years ago, 
and that was also my result back then.  (I assume you have a current 
patch, otherwise I can dig out that one.)  I had also written down that 
there were some problems with Perl and Tcl headers, FWIW.  Did you have 
any problems with those?

I think we can take a small loss here and move with the standard. 
Unless you can think of a way to define 
pg_noreturn_but_for_function_pointers in a systematic way.




Re: pg_attribute_noreturn(), MSVC, C11

From
Andres Freund
Date:
Hi,

On 2024-12-14 18:15:13 +0100, Peter Eisentraut wrote:
> On 13.12.24 20:10, Andres Freund wrote:
> > (or alternatively include stdreturn if __STDC_VERSION__ indicates support and
> > define a bare 'noreturn' if not)
> > 
> > For msvc that mean we'd need to add /std:c11 (or /std:c17) to the compiler
> > flags, as it otherwise it results in a weird mix of c89 an c99). But that
> > might be a good idea anyway. With one minor change [1] the tests pass with
> > msvc when using /std:c17.
> 
> According to my notes, C11 requires MSVC 2019, and we currently require
> 2015, so this will require a bit of logic.

Yea. Not hard though:

@@ -298,6 +298,7 @@ elif host_system == 'windows'
   if cc.get_id() == 'msvc'
     ldflags += '/INCREMENTAL:NO'
     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
+    cflags += cc.first_supported_argument('/std:c17', '/std:c11')
     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
   else
     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))

It's not quite the way meson wants you to do it (declare it in
default_options), but with our minimum meson version that's just a single
option, not a list...

Greetings,

Andres Freund



Re: pg_attribute_noreturn(), MSVC, C11

From
Andres Freund
Date:
Hi,

On 2024-12-14 18:18:35 +0100, Peter Eisentraut wrote:
> On 13.12.24 20:54, Andres Freund wrote:
> > Another wrinkle: While __attribute__((noreturn)) works for function pointers
> > (or function pointer typedefs) _Noreturn doesn't. Gah.  We only use it that
> > way in two places, but still :(
> 
> Yeah, I wrote an experimental patch for noreturn support some years ago, and
> that was also my result back then.

It's quite annoying...


> (I assume you have a current patch, otherwise I can dig out that one.)

Yea, I do. Not pretty, but ...

I guess I'll try to pretty it up a bit and post it then.


> I had also written down that there were some problems with Perl and Tcl
> headers, FWIW.  Did you have any problems with those?

Not so far.


> I think we can take a small loss here and move with the standard. Unless you
> can think of a way to define pg_noreturn_but_for_function_pointers in a
> systematic way.

The small loss unfortunately isn't that small, because clang treats
__attribute__((noreturn)) to be part of the function signature, but not
_Noreturn. Which means you can't just put __attribute__((noreturn)) to the
function pointer's signature, because it'll complain about incompatible
function pointers:

../../../../../home/andres/src/postgresql/src/backend/backup/basebackup_incremental.c:179:20: error: incompatible
functionpointer types assigning to 'json_manifest_error_callback' (aka 'void (*)(struct JsonManifestParseContext *,
constchar *, ...) __attribute__((noreturn))') from 'void (JsonManifestParseContext *, const char *, ...)' (aka 'void
(structJsonManifestParseContext *, const char *, ...)') [-Wincompatible-function-pointer-types]
 
  179 |         context->error_cb = manifest_report_error;

A workaround would be to have pg_nodiscard to just specify both
__attribute__((noreturn)) and _Nodiscard, and
pg_noreturn_but_for_function_pointers just specify __attribute__((noreturn)).
But at that point it's not obvious why we'd use _Nodiscard at all.

I wonder if we should switch to pg_nodiscard in the position that _Nodiscard
would be used and implement it as __attribute__((noreturn)) on gnu like
compilers and __declspec(noreturn) on msvc.


Kinda curious that we have pg_nodiscard, but didn't use __declspec(nodiscard).

Greetings,

Andres Freund