Stop using enums in bitfields
authorSergey Bugaev <bugaevc@gmail.com>
Tue, 17 Oct 2023 08:40:51 +0000 (11:40 +0300)
committerSergey Bugaev <bugaevc@gmail.com>
Tue, 17 Oct 2023 08:40:51 +0000 (11:40 +0300)
The C standard does not specify whether the underlying type of an enum
is signed or unsigned, and until C23 there was no way to control this
explicitly. GCC appears to make enums unsigned unless there is a
negative value among cases of the enum, in which case it becomes signed.
MSCV appears to make enums signed by default.

A bitfield of an enum type (which is not specificied in the C standard
either) behaves as if it was an instance of a numeric type with a
reduced value range. Specifically, a 'signed int val : 2;' bitfield will
have the possible values of -2, -1, 0, and 1, with the usual wraparound
behavior for the values that don't fit (although this too is
implementation-defined).

This causes the following issue, if we have:

typedef enum
{
  GTK_ZERO,
  GTK_ONE,
  GTK_TWO
} GtkFoo;

struct _GtkBar
{
  GtkFoo foo : 2;
};

and then assign bar.foo = GTK_TWO and read it back, it will have the
expected value of 2 (aka GTK_TWO) on GCC, but a value of -2 (not
matching any of the enum variants) on MSVC.

There does not seem to be any way to influence signedness of an enum
prior to C23, nor is there a 'unsigned GtkFoo foo : 2;' syntax. The only
remaining options seems to be never using enums in bitfields, which is
what this change implements.

In practice, this fixes GdkPipeIOStream crashing with an assertion when
trying to copy-paste in-app in MSVC builds on GTK.

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
gdk/gdkpipeiostream.c
gtk/gtkmenutrackeritem.c
gtk/gtksizerequestcacheprivate.h

index 6dabda9de7d51a61a9b63c19bf9e603dab8c4421..57a9c087cf0477b9fe9a67108025d9a851cfb92d 100644 (file)
@@ -40,7 +40,7 @@ struct _GdkIOPipe
   GCond cond;
   guchar *buffer;
   gsize size;
-  GdkIOPipeState state : 2;
+  guint state : 2; /* GdkIOPipeState */
   guint input_closed : 1;
   guint output_closed : 1;
 };
index 141c1ca0d6dd150462a477e303376740f13f44c6..7478bcf0d4eab65aadc0e44912a7c54ba613d99f 100644 (file)
@@ -86,7 +86,7 @@ struct _GtkMenuTrackerItem
   char *action_namespace;
   char *action_and_target;
   GMenuItem *item;
-  GtkMenuTrackerItemRole role : 4;
+  guint role : 4; /* GtkMenuTrackerItemRole */
   guint is_separator : 1;
   guint can_activate : 1;
   guint sensitive : 1;
index cc85dcd9744e6f5dc203b4d13270f44b38626570..fa9173975b2c791d0ad4c241327f4e9956772f28 100644 (file)
@@ -70,7 +70,7 @@ typedef struct {
   CachedSizeX  cached_size_x;
   CachedSizeY  cached_size_y;
 
-  GtkSizeRequestMode request_mode   : 3;
+  guint       request_mode          : 3; /* GtkSizeRequestMode */
   guint       request_mode_valid    : 1;
   struct {
     guint       n_cached_requests   : 15;