Skip to content

bpo-40686: Replace error suppression with static assertion #20624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

pganssle
Copy link
Member

@pganssle pganssle commented Jun 4, 2020

Rather than suppressing the unnecessary compiler warning, we will avoid the question by removing the lower bound and introducing a static assertion about the type of day.

In some ways this is still worse than the original version, since it only does the assertion when the __typeof__ GCC extension is available, and if the type of day is changed to be signed it will actually require code changes to the boundary check, but for practical purposes (since, at least at the moment, there's very little chance that we will remove GCC-based compilations from our CI checks) this will at least prevent the introduction of this particular bug.

This supercedes #20619. There's a corresponding patch to the backport here.

CC @corona10 @vstinner @pablogsal

https://bugs.python.org/issue40686

// This is not perfect, but it's better than nothing.
#ifdef __GNUC__
#define IS_TYPE_UNSIGNED(type) (((type)-1) > (type)0)
#define Py_ASSERT_VAR_UNSIGNED(var) \
Copy link
Member Author

@pganssle pganssle Jun 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, theoretically, move this into pymacro.h (or some private variant thereof), and potentially we could make it more portable, there might be an MSVC version of __typeof__ that I'm unaware of.

If we ever start allowing C11 (under flags or whatever so that it doesn't break non-C11 compilers), I think there are ways to accomplish the same thing using just the standard.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer this approach than PR #20619, but I would prefer to move things to pymacros.h to ease future maintenance and move the complexity out of _zoneinfo.c.

// variable, so we'll make Py_ASSERT_VAR_UNSIGNED a noop on non-GCC platforms.
// This is not perfect, but it's better than nothing.
#ifdef __GNUC__
#define IS_TYPE_UNSIGNED(type) (((type)-1) > (type)0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move IS_TYPE_UNSIGNED() to pymacro.h as PY_IS_TYPE_UNSIGNED()?

Py_ASSERT_VAR_UNSIGNED() can stay here :-)

// This takes advantage of GCC compiler extensions to determine the type of the
// variable, so we'll make Py_ASSERT_VAR_UNSIGNED a noop on non-GCC platforms.
// This is not perfect, but it's better than nothing.
#ifdef __GNUC__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, I would prefer to test a "HAVE_TYPEOF" rather than "hardcoding" GCC here. I suggest to define it in pymacros.h. Something like:

#if defined(__GNUC__) || defined(__clang__)
   // typeof() function
#  define HAVE_TYPEOF 1
#endif

clang also supports typeof().

Note: I prefer typeof() over __typeof__() :-)

typeof() is already used in pymacros.h. typeof() is used in pycore_atomic.h.

Copy link
Member

@vstinner vstinner Jun 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, clang manual says:

The parser recognizes “asm” and “typeof” as keywords in gnu* modes; the variants “__asm__” and “__typeof__” are recognized in all modes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think __typeof__ is the "compatibility mode" version of this. The standard way you build extensions seems to use one of the modes where typeof doesn't work as far as I can tell, so that's how I did it there. If we're already unconditionally using typeof() I guess it's fine to switch to that here.

# if defined(__cplusplus)
# define _Py_TYPEOF(x) decltype(x)
# else
# define _Py_TYPEOF(x) __typeof__(x)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure if maybe this should be __extension__ ( __typeof__(x) ) to allow its use in -pedantic mode or something.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have troubles with -pedantic, you might try to test defined(STRICT_ANSI): see Py_ARRAY_LENGTH() crazy macro.

#if defined(__GNUC__) || defined(__clang__) || defined(__cplusplus)
# define _Py_HAVE_TYPEOF 1
# if defined(__cplusplus)
# define _Py_TYPEOF(x) decltype(x)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that it's worth it to add _Py_TYPEOF() just to support C++. C extensions are usually built in C. All stdlib C extensions are built in C mode.

Since it's a private macro, I suggest to only support C and documented _Py_HAVE_TYPEOF as Define if __typeof__() builtin function is available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the purpose here was not so much to support C++ but to give us some compiler-independent behavior. In C++ mode, there's an alternative to typeof that works in all the scenarios we use __typeof__ for, so this extends the scenarios where we can do __typeof__-like operations.

I also think that maybe we want _Py_TYPEOF() to be defined as __extension__ ( __typeof__(x) ) , in which case it's better to have our own macro defined for it (and at that point the __cplusplus definition is only a cost in that we may not be testing it properly if we never build anything in C++ mode).

The other benefit to this is that if other compilers add support for typeof-like functionality, we'll be able to seamlessly add them.

"_Py_TYPEOF is not available in all supported compilation modes on " \
"all supported compilers. Use the _Py_HAVE_TYPEOF macro to guard " \
"any statements using _Py_TYPEOF." \
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of the purpose of defining _Py_TYPEOF() if it's not supported. If _Py_HAVE_TYPEOF is not defined, _Py_TYPEOF is not defined and typeof() must not be used. No?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that's true. I was mainly trying to give a clean error message, because C error messages are messy.

Another option is to drop _Py_HAVE_TYPEOF and have people use #ifdef _Py_TYPEOF as the guard, if they want to.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_Py_IS_TYPE_UNSIGNED() is simple and makes sense, but I still fail to see the benefit of Py_ASSERT_VAR_UNSIGNED(day);. IMO the preprocessor magic to define _Py_TYPEOF() is not worth it.

If you are scared that one day, someone changes the type to make it signed, just add a comment somewhere. But I don't see why anyone would do that without thinking twice about bounds checking.

@pganssle
Copy link
Member Author

If you are scared that one day, someone changes the type to make it signed, just add a comment somewhere. But I don't see why anyone would do that without thinking twice about bounds checking.

Even in Python I think it's better to have the semantics of your code enforce your assumptions wherever possible, but this is C — I 100% don't trust people to understand the full implications of everything they are doing and to read all the comments on everything that might be affected by changing the type in one struct or another.

If there were a real trade-off here, I might be happy to compromise, but this is just working around a bad compiler warning.

How about we just move this back to how I had it originally, with the macro defined in zoneinfo.c, so that we can get rid of the compiler warnings, and then we can address making a more generalized version of this macro in a separate enhancement?

@vstinner
Copy link
Member

Yeah, it's maybe better to move _Py_TYPEOF in _zoneinfo.c.

Rather than suppressing the unnecessary compiler warning, we will avoid
the question by removing the lower bound and introducing a static
assertion about the type of `day`.

In some ways this is still worse than the original version, since it
only does the assertion when the __typeof__ GCC extension is available,
and if the type of `day` is changed to be signed it will actually
require code changes to the boundary check, but for practical purposes
(since, at least at the moment, there's very little chance that we will
remove GCC-based compilations from our CI checks) this will at least
prevent the introduction of this particular bug.

https://bugs.python.org/issue40686
@pganssle pganssle force-pushed the zoneinfo_static_type_assertion branch from 2b0ced5 to b948cb2 Compare October 20, 2020 18:11
@pganssle
Copy link
Member Author

@vstinner I've restored the patch to its original version, before we tried to get more general with it. What do you think?

@vstinner
Copy link
Member

@vstinner I've restored the patch to its original version, before we tried to get more general with it. What do you think?

I don't think that it's worth it and I would suggest to remove all pragmas which produce annoying compiler warnings on Windows.

@pganssle
Copy link
Member Author

Ok, we'll just skip the whole thing if you don't think it's worth fixing these erroneous warnings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants