`menubar.f90` does not compile on 32 bits systems (gssize type bug)
vmagnin opened this issue · 7 comments
Describe the bug
On a 32 bits system:
Scanning dependencies of target menubar
[ 40%] Building Fortran object examples/CMakeFiles/menubar.dir/menubar.f90.o
gtk-fortran/src/gtk-fortran-gtk4/examples/menubar.f90:91:68:
91 | call gtk_css_provider_load_from_data (provider, color, -1_int64)
| 1
Error: Type mismatch in argument ‘length’ at (1); passed INTEGER(8) to INTEGER(4)
Your system:
- OS version: 32 bits Linux 5.15.80-smp i686
- Compiler version: gfortran 11.2.0
- GTK branch: gtk4
- GTK 4.4.1
Additional context
menubar.f90
was written for the gtk4 branch, and is therefore not in previous branches.
Temporary workaround: you can ignore that error with $ make -i
or manually replace -1_int64
by -1
or -1_int32
.
The GTK 4 doc says gtk_css_provider_load_from_data is expecting a gssize
data. gssize is an alias for the ssize_t
type (not defined in the C standard but in POSIX). The Fortran ISO_C_BINDING
modules defines c_size_t
but not c_ssize_t
. We could assume they are identical but it may not be true in all OS.
Here the example assumes it is a 64 bits integer:
call gtk_css_provider_load_from_data (provider, color, -1_int64)
The doc of that function says the last argument is:
The length of data in bytes, or -1 for NUL terminated strings.
The GLib doc defines a 8 bytes length for ssize_t, but it probably assumes it is for a 64 bits system.
A few ideas:
- We could use CMake to detect if the system is 32 or 64 bits (https://discourse.cmake.org/t/how-to-distinguish-windows-architecture/5104/6) and use its
string(REPLACE...
command. - We could try to find a GLib function returning a
gssize
variable and use it ingeniously as the third argument. See for example https://docs.gtk.org/glib/?q=gssize or https://docs.gtk.org/gio/?q=gssize
I don't think gfortran version matters here, version 12.2.0 gives the same error as version 11.2.0.
This works in both 64 and 32 bit (compile with -cpp to enable pre-processor):
#ifdef _LP64
call gtk_css_provider_load_from_data (provider, color, -1_int64)
#else
call gtk_css_provider_load_from_data (provider, color, -1)
#endif
However I believe it's much better if this is fixed in src/gtk-auto.in at the interface for gtk_css_provider_load_from_data
. Line 3307 should be replaced by something like
#ifdef _LP64
integer(c_size_t), value :: length
#else
integer(c_int), value :: length
#endif
I am not sure this will work on Window$, but I guess it will.
The root of the bug is in the src/cfwrapper/cfwrapper.py
file, line 149:
#typedef unsigned long gsize; also GType
"gsize": ("integer(c_size_t)", "c_size_t"),
#typedef signed long gssize;
"gssize": ("integer(c_size_t)", "c_size_t"),
"GType": ("integer(c_size_t)", "c_size_t"),
"size_t": ("integer(c_size_t)", "c_size_t"),
gssize
(alias ssize_t
) was wrongly assumed to be identical to gsize
(alias size_t
) on all systems. The bug was not detected earlier because gssize
appears only 106 times in the src/gtk-fortran-index.csv
file, over 10130 functions.
This is for example the interface to gtk_css_provider_load_from_data()
in the src/gtk-auto.in
file:
! GDK_AVAILABLE_IN_ALL
!void gtk_css_provider_load_from_data (GtkCssProvider *css_provider, const char *data, gssize length);
subroutine gtk_css_provider_load_from_data(css_provider, data, length) bind(c)
import :: c_ptr, c_char, c_size_t
type(c_ptr), value :: css_provider
character(kind=c_char), dimension(*) :: data
integer(c_size_t), value :: length
end subroutine
I have reproduced the bug in a Debian Sid 32bits virtual machine.
As gtk-fortran must work either with CMake or fpm, using a preprocessor rather than CMake replacement functions seems necessary. The difficulty is to be sure it will work with all OS.
We could for example add a glib_kinds.f90
module that would be imported by the GTK modules, with something like:
#ifdef _LP64
integer :: gssize = c_int64_t
#else
integer :: gssize = c_int32_t
#endif
- Extract of
include/x86_64-linux-gnu/bits/posix1_lim.h
:
#ifndef SSIZE_MAX
/* ssize_t is not formally required to be the signed type
corresponding to size_t, but it is for all configurations supported
by glibc. */
# if WORDSIZE == 64 || WORDSIZE32_SIZE_ULONG
# define SSIZE_MAX LONG_MAX
# else
# define SSIZE_MAX INT_MAX
# endif
#endif
The types size_t and ssize_t are, respectively, unsigned and signed integer data types specified by POSIX.1.
Other information about size_t
only:
size_t is the unsigned integer type of the result of sizeof , _Alignof (since C11) and offsetof, depending on the data model. [...] size_t can store the maximum size of a theoretically possible object of any type (including array).
It’s a type which is used to represent the size of objects in bytes and is therefore used as the return type by the sizeof operator. It is guaranteed to be big enough to contain the size of the biggest object the host system can handle. Basically the maximum permissible size is dependent on the compiler; if the compiler is 32 bit then it is simply a typedef(i.e., alias) for unsigned int but if the compiler is 64 bit then it would be a typedef for unsigned long long. The size_t data type is never negative.
I have verified that a print *, c_size_t
(not ssize_t) is printing 4 in a Debian 32 bits and 8 in an Ubuntu 64 bits.
In both cases with the following line, the menubar.f90
is compiled and runs without problem on both OS:
call gtk_css_provider_load_from_data (provider, color, -1_c_size_t)
https://discourse.gnome.org/t/where-are-defined-glib-types-in-the-new-doc/14473/4
Since gssize is a hard coded as a signed version of size_t, you can assume it passes a similar check.
Therefore it is finally correct to assume that gssize
is a c_size_t
. And the suffix _c_size_t
must be used in constant values when needed. The examples will be checked and comments will added in the wrapper.