possibly incorrect docs re: constant initialization
The documentation for PyTypeObject.tp_base (https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_base) says the following:
Note: Slot initialization is subject to the rules of initializing globals. C99 requires the initializers to be “address constants”. Function designators like PyType_GenericNew(), with implicit conversion to a pointer, are valid C99 address constants.
However, the unary ‘&’ operator applied to a non-static variable like PyBaseObject_Type() is not required to produce an address constant. Compilers may support this (gcc does), MSVC does not. Both compilers are strictly standard conforming in this particular behavior.
I think this may be incorrect. Specifically, it seems to be confusing "static variable" with "static storage duration." It is true that C99 requires address constants to have static storage duration. The relevant text in C99 is 6.6p9:
An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type.
Note that the language here is "static storage duration", not "static variable". The standard doesn't define the term "static variable", but in common usage it means any variable that includes the static storage-class specifier. Under this definition, PyBaseObject_Type is indeed not a static variable: // These are commonly referred to as "static variables" because they // use the "static" keyword: static int x; void f() { static int y; } // Not a static variable. extern PyTypeObject PyBaseObject_Type However "static storage duration" is a broader concept that describes the lifetime of an object. An object can have static storage duration even if it does not use the "static" keyword. From 6.2.4p3:
An object whose identifier is declared with external or internal linkage, or with the storage-class specifier static has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
Under this definition, PyBaseObject_Type does indeed have static storage duration, because it has external linkage. So I believe that &PyBaseObject_Type should be a valid address constant according to C99. Indeed, MSVC accepts an address of an extern as an address constant in the simple case: https://godbolt.org/z/8Tdc999z3 It is only when the extern is imported from another DLL that MSVC rejects it: https://godbolt.org/z/fMM1fr743 I believe that MSVC may be out of conformance in rejecting the latter case. If this is true, it wouldn't necessarily change the guidance in the docs (tp_base should still be initialized dynamically if the base could possibly come from a DLL on Windows). However it may make sense to change the language about both compilers being "strictly standard conforming." It also may be worth mentioning that this issue appears to be specific to DLLs; if the base class will never come from another DLL, this workaround may be unnecessary. One other minor nit; there is a small typographical error in the docs I quoted above:
However, the unary ‘&’ operator applied to a non-static variable like PyBaseObject_Type() is not required to produce an address
PyBaseObject_Type is not a function, so it shouldn't have the trailing '()'. Thanks, Josh
participants (1)
-
Josh Haberman