Repository: cython/cython

Issue:
[BUG] 3.0.0b1: Very big integers causes MSVC to fail compiling the cythonized .c file (distutils.setup)
### Describe the bug

After switching from 3.0.0a10 to 3.0.0b1, our builds started to fail during cythonization step (`from Cython.Build.Dependencies import cythonize`). We narrowed down the problem as much as we can. Basically, very big integers makes MSVC (cl.exe) fail to compile the cythonized code. 

You can see a very basic python script below to reproduce the crash (or here: https://gist.github.com/ozankececioglu/b0bcaeede0b14952eb28c47db841735e).
The cythonized .c code from 3.0.0b1 can be found here: https://gist.github.com/ozankececioglu/e0f6fe87ba0cfa78f0e1f3dba43a69e0
3.0.0a10 is here: https://gist.github.com/ozankececioglu/61d381f86d949eddebe424dd7a2993a1

The cl.exe error we are having with 3.0.0b1:
```
C:\Users\ozan\ws\cython_fail.c(1754): error C2039: '__pyx_mstate_global': is not a member of '__pyx_mstate'
C:\Users\ozan\ws\cython_fail.c(1563): note: see declaration of '__pyx_mstate'
error: command 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC\\14.34.31933\\bin\\HostX64\\x64\\cl.exe' failed with exit code 2
```

Our hunch is, MSVC does not like a very long variable name inside a struct. In the 3.0.0a10, the same variable seems to be defined in a global statement.

### Code to reproduce the behaviour:

```cython
# example code
big_int = 4987677775770777705567775877727067747764603697707784482312707370727377746579652870748702677779367580564477176737780205722456774623774677778779737207277790472074247369772375770497605677127617687117775737717771791797177003701313277130447880079714473771737778664492717497477211878772475778771795577377767077776777772177298975793975577073795437779750715773979127977511722837953078370461277575519762782713874139777777774700743774773770998777437772776198179733909687574772704167777795797966373977780709777877895072797
print(big_int)
```


### Expected behaviour

MSVC compiles successfully. 

### Environment

OS: Windows 10 Pro 21H2
Python version: Python 3.9.16
MSVC version: 14.34.31933 (Visual Studio 2022)
Cython version: 3.0.0b1


### Additional context

_No response_


Discussion:
Thanks for the detailed report and the short reproducer. The difference is that we now (always) put all global names into a module state struct, rather than making them static global C names. See https://github.com/cython/cython/pull/5056

This was not a change that is strictly required, but makes it easier for us to support the module initialisation in the CPython Limited API and, more generally (and eventually), features like module reload and subinterpreters, which are based on such a struct that holds the module state.

Apparently, MSVC cannot deal with very long struct field names, although I couldn't quickly find a specific reference for that. The limitations of MSVC have annoyed me (and certainly others) before, so this is just another annoyance on top. There seems to be a general limit for the length of names, but we don't seem to be close to that here (your generated C code uses <700 characters, which seems reasonably below 2048 to even allow for additional name mangling and what not).

https://stackoverflow.com/questions/6007568/what-is-max-length-for-an-c-c-identifier-on-common-build-systems

The reason why we spell out the integer in the variable/field name is readability. If something goes wrong, you want to be able to understand the constant values used in the C code easily, rather than having to dig through naming indirections first.

We could limit the name length here, but then also have to deal with collisions of integer constant names. Probably not overly difficult to do, since we already have to do it for other types of constants. We just didn't need it here before, and it adds complexity. Probably still the best way out.

I can reproduce this on my machine
> We could limit the name length here, but then also have to deal with collisions of integer constant names. Probably not overly difficult to do, since we already have to do it for other types of constants. We just didn't need it here before, and it adds complexity. Probably still the best way out.

I think that the logical thing to do here would be to disable inprinting for integers with number of digits > 100
To avoid collitions we can use another name, for example `__pyx_int_large_{id}`

I'll work on it
So I tried integers of different length and found out that MSVC's max filend name length is 255 characters, i.e.
`__pyx_int_82813147070913921502168547010770424983756187714789644129131811052948576862951911496710117041873202651584850832294643313253817251230668868646087277980565561296505848824960244534987133811018600702756248272156154738975143985520544903881770262345616` works
and `__pyx_int_828131470709139215021685470107704249837561877147896441291318110529485768629519114967101170418732026515848508322946433132538172512306688686460872779805655612965058488249602445349871338110186007027562482721561547389751439855205449038817702623456160` does not
IMHO nobody really neeeds such long names and we wouldn't make much sense whlie debugging from the whole length of an integer
And actually the proposal of @scoder to just cut off the excess and have duplicates like this seems like a better options
something like `__pyx_int_828131470709139215021685470107704249837561877147896441291318110529485768629519114967101170418732026_5` or even `__pyx_int_828131470709139215021685470107704249837561877147896441291318110529485768629519114967101170418732026_large_5` would be more helpful than `__pyx_int_large_5`

Should we cut it off at, say, a completely arbitrary 42 digits then? That's well below the 'standard' line length of 80.

I was considering to include the final (least significant) digits as well (e.g. `1239717__13712_large_5`, leaving out digits at the `__`), to better represent number series, but 'm not sure if that's really helpful. It could be, in some cases, but it's probably still not so easy to spot them before the identity counter. Maybe `__pyx_int_large5_1239717__13712`?
+1, it seems better to place `large` in front of the name

iterating on the proposal: `__pyx_int_large5_1239717_skipped_13712` or `__pyx_int_large5_1239717_XXX_13712`
Why not take 80 as maximum length of an int name? even with indentatoin this probably wouldn't be that bad
if `__pyx_int_numbers` does not fit in 80 characters than we'll represent it as large
Although I am not sure if this is feasible as I am not a contributor, please let me put my uninformed opinion. Python line (and even column) number in the variable name could also help with debugging (and collisions). Something like `__pyx_int_large5_L102_82813147070913`. 
> Although I am not sure if this is feasible as I am not a contributor, please let me put my uninformed opinion. Python line (and even column) number in the variable name could also help with debugging (and collisions). Something like `__pyx_int_large5_L102_82813147070913`.

It makes sense, but from what I see in the name generator (it gets passed the value and it's type as string for ints) it would be harder than changing {id} if we already use this name
controlling the length of the whole string is a little fiddly, reverting to the answer to the unltimate question of life, the universe and everything

Resolving patch:
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -1423,23 +1423,33 @@ def new_string_const_cname(self, bytes_value):
         value = bytes_value.decode('ASCII', 'ignore')
         return self.new_const_cname(value=value)
 
-    def new_num_const_cname(self, value, py_type):
+    def unique_const_cname(self, format_str):  # type: (str) -> str
+        used = self.const_cnames_used
+        cname = value = format_str.format(sep='', counter='')
+        while cname in used:
+            counter = used[value] = used[value] + 1
+            cname = format_str.format(sep='_', counter=counter)
+        used[cname] = 1
+        return cname
+
+    def new_num_const_cname(self, value, py_type):  # type: (str, str) -> str
         if py_type == 'long':
             value += 'L'
             py_type = 'int'
         prefix = Naming.interned_prefixes[py_type]
-        cname = "%s%s" % (prefix, value)
-        cname = cname.replace('+', '_').replace('-', 'neg_').replace('.', '_')
+
+        value = value.replace('.', '_').replace('+', '_').replace('-', 'neg_')
+        if len(value) > 42:
+            # update tests/run/large_integer_T5290.py in case the amount is changed
+            cname = self.unique_const_cname(
+                prefix + "large{counter}_" + value[:18] + "_xxx_" + value[-18:])
+        else:
+            cname = "%s%s" % (prefix, value)
         return cname
 
     def new_const_cname(self, prefix='', value=''):
         value = replace_identifier('_', value)[:32].strip('_')
-        used = self.const_cnames_used
-        name_suffix = value
-        while name_suffix in used:
-            counter = used[value] = used[value] + 1
-            name_suffix = '%s_%d' % (value, counter)
-        used[name_suffix] = 1
+        name_suffix = self.unique_const_cname(value + "{sep}{counter}")
         if prefix:
             prefix = Naming.interned_prefixes[prefix]
         else:


Test patch:
diff --git a/tests/run/large_integer_T5290.py b/tests/run/large_integer_T5290.py
new file mode 100644
--- /dev/null
+++ b/tests/run/large_integer_T5290.py
@@ -0,0 +1,19 @@
+# cython: test_assert_c_code_has = __pyx_int_large_498767777577077770_xxx_709777877895072797
+# cython: test_assert_c_code_has = __pyx_int_large2_498767777577077770_xxx_709777877895072797
+
+"""
+>>> print(large_int1)
+498767777577077770556777587772706774776460369770778448231270737072737774657965287074870267\
+777936758056447717673778020572245677462377467777877973720727779047207424736977237577049760\
+567712761768711777573771777179179717700370131327713044788007971447377173777866449271749747\
+721187877247577877179557737776707777677777217729897579397557707379543777975071577397912797\
+751172283795307837046127757551976278271387413977777777470074377477377099877743777277619817\
+9733909687574772704167777795797966373977780709777877895072797
+>>> large_int2 - large_int1 == 10**485  or  large_int2
+True
+"""
+
+large_int1 = 4987677775770777705567775877727067747764603697707784482312707370727377746579652870748702677779367580564477176737780205722456774623774677778779737207277790472074247369772375770497605677127617687117775737717771791797177003701313277130447880079714473771737778664492717497477211878772475778771795577377767077776777772177298975793975577073795437779750715773979127977511722837953078370461277575519762782713874139777777774700743774773770998777437772776198179733909687574772704167777795797966373977780709777877895072797  # noqa
+large_int2 = 4987677775770777705567775977727067747764603697707784482312707370727377746579652870748702677779367580564477176737780205722456774623774677778779737207277790472074247369772375770497605677127617687117775737717771791797177003701313277130447880079714473771737778664492717497477211878772475778771795577377767077776777772177298975793975577073795437779750715773979127977511722837953078370461277575519762782713874139777777774700743774773770998777437772776198179733909687574772704167777795797966373977780709777877895072797  # noqa
+#                                     ^
+#                                     Here ...77758... -> ...77759...