From 964affb1ccdea09a4b5149c65d57833c95db5530 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 9 Oct 2023 17:53:21 +0300 Subject: [PATCH] Stop using enums in bitfields 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 --- gdk/gdkpipeiostream.c | 2 +- gtk/gtkmenutrackeritem.c | 2 +- gtk/gtksizerequestcacheprivate.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gdk/gdkpipeiostream.c b/gdk/gdkpipeiostream.c index 6dabda9de7..57a9c087cf 100644 --- a/gdk/gdkpipeiostream.c +++ b/gdk/gdkpipeiostream.c @@ -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; }; diff --git a/gtk/gtkmenutrackeritem.c b/gtk/gtkmenutrackeritem.c index 141c1ca0d6..7478bcf0d4 100644 --- a/gtk/gtkmenutrackeritem.c +++ b/gtk/gtkmenutrackeritem.c @@ -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; diff --git a/gtk/gtksizerequestcacheprivate.h b/gtk/gtksizerequestcacheprivate.h index cc85dcd974..fa9173975b 100644 --- a/gtk/gtksizerequestcacheprivate.h +++ b/gtk/gtksizerequestcacheprivate.h @@ -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;