vmagnin/gtk-fortran

`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:

p-pap commented

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.