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
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/2WUFTVQA7SLEDEDYSRJ75XFIR3EUTKKO/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to