increase type safety + add backwards compatibility including C99

Tue, 25 Aug 2026 18:35:18 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 25 Aug 2026 18:35:18 +0200
changeset 193
d1420f5c5704
parent 192
e57e2874fb2a
child 194
619f07c95894

increase type safety + add backwards compatibility including C99

Makefile file | annotate | diff | comparison | revisions
configure file | annotate | diff | comparison | revisions
make/cc.mk file | annotate | diff | comparison | revisions
make/project.xml file | annotate | diff | comparison | revisions
src/chess/king.c file | annotate | diff | comparison | revisions
src/chess/pawn.c file | annotate | diff | comparison | revisions
src/chess/pgn.c file | annotate | diff | comparison | revisions
src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
test/Makefile file | annotate | diff | comparison | revisions
test/test-datatypes.c file | annotate | diff | comparison | revisions
test/test-real-pgn.c file | annotate | diff | comparison | revisions
--- a/Makefile	Mon Aug 24 16:13:51 2026 +0200
+++ b/Makefile	Tue Aug 25 18:35:18 2026 +0200
@@ -28,7 +28,7 @@
 
 include config.mk
 
-all: app tests
+all: app
 
 app: $(BUILDDIR) FORCE
 	cd src && $(MAKE)
@@ -36,13 +36,13 @@
 tests: $(BUILDDIR) $(BUILDDIR)/libchess$(LIB_EXT) FORCE
 	cd test && $(MAKE)
 
-check: all FORCE
+check: tests FORCE
 	$(BUILDDIR)/run-tests
 
 $(BUILDDIR):
 	$(MKDIR) $(MKDIRFLAGS) $(BUILDDIR)
 
-$(BUILDDIR)/libchess$(LIB_EXT):
+$(BUILDDIR)/libchess$(LIB_EXT): FORCE
 	cd src/chess && $(MAKE)
 
 clean:
--- a/configure	Mon Aug 24 16:13:51 2026 +0200
+++ b/configure	Tue Aug 25 18:35:18 2026 +0200
@@ -101,6 +101,7 @@
 
 Optional Features:
   --enable-asan           address sanitizer
+  --enable-strict-errors  treat compiler warnings as errors
 
 __EOF__
     abort_configure
@@ -176,6 +177,8 @@
         "--release") BUILD_TYPE="release" ;;
         "--enable-asan") FEATURE_ASAN=on ;;
         "--disable-asan") unset FEATURE_ASAN ;;
+        "--enable-strict-errors") FEATURE_STRICT_ERRORS=on ;;
+        "--disable-strict-errors") unset FEATURE_STRICT_ERRORS ;;
         "-"*) echo "unknown option: $ARG"; abort_configure ;;
     esac
 done
@@ -447,6 +450,19 @@
 else
     :
 fi
+if [ -n "$FEATURE_STRICT_ERRORS" ]; then
+    if [ -n "$DISABLE_FEATURE_STRICT_ERRORS" ]; then
+        unset FEATURE_STRICT_ERRORS
+    fi
+fi
+if [ -n "$FEATURE_STRICT_ERRORS" ]; then
+    :
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
+CFLAGS+=-Werror
+__EOF__
+else
+    :
+fi
 
 
 if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
@@ -496,6 +512,12 @@
 else
     echo 'off'
 fi
+printf '  %-16s' 'strict-errors:'
+if [ -n "$FEATURE_STRICT_ERRORS" ]; then
+    echo 'on'
+else
+    echo 'off'
+fi
 echo
 
 # generate the config.mk file
--- a/make/cc.mk	Mon Aug 24 16:13:51 2026 +0200
+++ b/make/cc.mk	Tue Aug 25 18:35:18 2026 +0200
@@ -3,6 +3,6 @@
 #
 
 CFLAGS =
-DEBUG_CFLAGS = -g -Wall -Werror -Wno-pointer-sign
+DEBUG_CFLAGS = -g -Wall -Wextra -Wenum-conversion
 RELEASE_CFLAGS = -O3 -DNDEBUG
 LDFLAGS =
--- a/make/project.xml	Mon Aug 24 16:13:51 2026 +0200
+++ b/make/project.xml	Tue Aug 25 18:35:18 2026 +0200
@@ -27,5 +27,9 @@
             <desc>address sanitizer</desc>
             <dependencies>asan</dependencies>
         </feature>
+        <feature name="strict-errors">
+            <desc>treat compiler warnings as errors</desc>
+            <make>CFLAGS+=-Werror</make>
+        </feature>
     </target>
 </project>
--- a/src/chess/king.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/chess/king.c	Tue Aug 25 18:35:18 2026 +0200
@@ -66,7 +66,7 @@
 
 bool king_isblocked(const GameState *gamestate, const Move *move) {
     
-    uint8_t opponent_color = opponent_color(piece_color(move->piece));
+    uint8_t op_color = opponent_color(piece_color(move->piece));
     
     /* being in check does not "block" the king, so don't test it here */
     bool blocked = false;
@@ -82,7 +82,7 @@
             incheck = is_check_position(gamestate);
         }
         blocked |= incheck || gamestate->board[move->torow][midfile] ||
-            is_covered(gamestate, move->torow, midfile, opponent_color);
+            is_covered(gamestate, move->torow, midfile, op_color);
     }
     
     return blocked;
--- a/src/chess/pawn.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/chess/pawn.c	Tue Aug 25 18:35:18 2026 +0200
@@ -80,6 +80,7 @@
 
 size_t pawn_moves(const GameState *gamestate,
         Color c, Row r, File f, Move *moves) {
+    (void) gamestate; /* no (theoretical) move depends on the game state */
     size_t count = 0;
     int rowdelta = c == WHITE ? 1 : -1;
     int promotionrow = c == WHITE ? 7 : 0;
--- a/src/chess/pgn.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/chess/pgn.c	Tue Aug 25 18:35:18 2026 +0200
@@ -231,7 +231,7 @@
 
 int read_pgn(FILE *stream, GameState *gamestate) {
     fseek(stream, 0, SEEK_END);
-    long int size = ftell(stream);
+    size_t size = ftell(stream);
     fseek(stream, 0, SEEK_SET);
     char *data = malloc(size + 1);
     memset(data, 0, size + 1);
--- a/src/chess/rules.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/chess/rules.c	Tue Aug 25 18:35:18 2026 +0200
@@ -1158,6 +1158,11 @@
                 move->check = move->checkmate = false;
                 switch (determine_check_or_checkmate(gamestate, move)) {
                     case 2: move->checkmate = true;
+#if __STDC_VERSION__ > 202310L
+                    [[fallthrough]];
+#elif __GNUC__
+                    __attribute__((fallthrough));
+#endif
                     case 1: move->check = true;
                 }
             }
--- a/src/chess/rules.h	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/chess/rules.h	Tue Aug 25 18:35:18 2026 +0200
@@ -49,11 +49,27 @@
 #define INVALID_CHECKMATE     11
 #define RULES_VIOLATED        32
 
+#if __STDC_VERSION__ < 202310L
+/* since #warning is also a C23 feature, the only hope is this: */
+#pragma GCC warning "Type safety for enums is only available since C23"
+#define enum_uint8_t(name) enum e##name
+#define typedef_enum_uint8_t(name) typedef uint8_t name
+#else
+#define enum_uint8_t(name) enum e##name : uint8_t
+#define typedef_enum_uint8_t(name) typedef enum e##name name
+#endif
+
 #define ENPASSANT_THREAT 0x40u
 
-#define WHITE 0x10u
-#define BLACK 0x20u
-#define opponent_color(color) ((color)==WHITE?BLACK:WHITE)
+enum_uint8_t(Color) {
+    WHITE = 0x10u,
+    BLACK = 0x20u,
+};
+typedef_enum_uint8_t(Color);
+
+static inline Color opponent_color(Color color) {
+    return color == WHITE ? BLACK : WHITE;
+}
 
 #define PIECE_MASK 0x0Fu
 #define COLOR_MASK 0x30u
@@ -65,25 +81,36 @@
 #define QUEEN  0x05u
 #define KING   0x06u
 
-#define WPAWN   (WHITE|PAWN)
-#define WROOK   (WHITE|ROOK)
-#define WKNIGHT (WHITE|KNIGHT)
-#define WBISHOP (WHITE|BISHOP)
-#define WQUEEN  (WHITE|QUEEN)
-#define WKING   (WHITE|KING)
-#define BPAWN   (BLACK|PAWN)
-#define BROOK   (BLACK|ROOK)
-#define BKNIGHT (BLACK|KNIGHT)
-#define BBISHOP (BLACK|BISHOP)
-#define BQUEEN  (BLACK|QUEEN)
-#define BKING   (BLACK|KING)
+enum_uint8_t(Piece) {
+    WPAWN   = WHITE|PAWN,
+    WROOK   = WHITE|ROOK,
+    WKNIGHT = WHITE|KNIGHT,
+    WBISHOP = WHITE|BISHOP,
+    WQUEEN  = WHITE|QUEEN,
+    WKING   = WHITE|KING,
+    BPAWN   = BLACK|PAWN,
+    BROOK   = BLACK|ROOK,
+    BKNIGHT = BLACK|KNIGHT,
+    BBISHOP = BLACK|BISHOP,
+    BQUEEN  = BLACK|QUEEN,
+    BKING   = BLACK|KING,
+};
+typedef_enum_uint8_t(Piece);
+
+#define POS_UNSPECIFIED 255u
+enum_uint8_t(Row) {
+    RANK_1 = 0, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8,
+    RANK_UNSPECIFIED = POS_UNSPECIFIED
+};
+typedef_enum_uint8_t(Row);
+
+enum_uint8_t(File) {
+    FILE_A = 0, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H,
+    FILE_UNSPECIFIED = POS_UNSPECIFIED
+};
+typedef_enum_uint8_t(File);
 
 typedef uint8_t Board[8][8];
-typedef uint8_t BoardIndex;
-typedef BoardIndex Row;
-typedef BoardIndex File;
-typedef uint8_t Piece;
-typedef uint8_t Color;
 
 struct movetimeval {
     uint64_t sec;
@@ -154,31 +181,26 @@
     bool review;
 } GameState;
 
-#define piece_type(piece) ((piece)&PIECE_MASK)
-#define piece_color(piece) ((piece)&COLOR_MASK)
-#define mkpiece(type,color) ((type)|(color))
+#define piece_type(piece) ((uint8_t)(piece)&PIECE_MASK)
+#define piece_color(piece) ((uint8_t)(piece)&COLOR_MASK)
+#define mkpiece(type,color) (Piece)((type)|(color))
 
-#define POS_UNSPECIFIED UINT8_MAX
 #define mdst(m) (m)->torow, (m)->tofile
 #define msrc(m) (m)->fromrow, (m)->fromfile
 
 /** Checks if the index is specified and valid. */
-#define isidx(idx) ((BoardIndex)(idx) < 8)
+static inline bool isidx(uint8_t idx) {return idx < 8;}
 /** Checks if the index is unspecified or valid. */
-#define isidxr(idx) ((idx) == POS_UNSPECIFIED || (BoardIndex)(idx) < 8)
-
-#define isfile(file) (file >= 'a' && file <= 'h')
-#define isrow(row) (row >= '1' && row <= '8')
+static inline bool isidxr(uint8_t idx) {return idx==POS_UNSPECIFIED || idx<8;}
 
-#define rowidx(row) (row-'1')
-#define fileidx(file) (file-'a')
+static inline bool isfile(char file) {return file >= 'a' && file <= 'h';}
+static inline bool isrow(char row) {return row >= '1' && row <= '8';}
 
-#define rowchr(row) (row+'1')
-#define filechr(file) (file+'a')
+static inline Row rowidx(char row) {return row-'1';}
+static inline File fileidx(char file) {return file-'a';}
 
-/* secure versions - use, if index is not checked with isidx() */
-#define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED)
-#define rowidx_s(c) (isrow(c)?rowidx(c):POS_UNSPECIFIED)
+static inline char rowchr(Row row) {return (char)row+'1';}
+static inline char filechr(File file) {return (char)file+'a';}
 
 
 static inline void enpassant_threat_add(GameState *gamestate,
--- a/src/main.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/src/main.c	Tue Aug 25 18:35:18 2026 +0200
@@ -1038,6 +1038,7 @@
 
 static int server_fd = -1;
 static void interrupt_listen(int sig) {
+    (void)sig; /* unused */
     if (server_fd > -1) {
         // this interrupts
         close(server_fd);
--- a/test/Makefile	Mon Aug 24 16:13:51 2026 +0200
+++ b/test/Makefile	Tue Aug 25 18:35:18 2026 +0200
@@ -29,8 +29,9 @@
 include ../config.mk
 
 SRC  = run-tests.c \
-	   test-rules-helper.c \
-	   test-real-pgn.c
+       test-datatypes.c \
+       test-rules-helper.c \
+       test-real-pgn.c
 
 OBJ = $(SRC:%.c=$(BUILDDIR)/%.o)
 
@@ -47,45 +48,12 @@
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
+$(BUILDDIR)/test-datatypes.o: test-datatypes.c ../src/chess/rules.h
+	@echo "Compiling $<"
+	$(CC) -o $@ $(CFLAGS) -c $<
+
 $(BUILDDIR)/test-real-pgn.o: test-real-pgn.c ucxtest.h ../src/chess/pgn.h \
- ../src/chess/rules.h data/game001.pgn data/game002.pgn data/game003.pgn \
- data/game004.pgn data/game005.pgn data/game006.pgn data/game007.pgn \
- data/game008.pgn data/game009.pgn data/game010.pgn data/game011.pgn \
- data/game012.pgn data/game013.pgn data/game014.pgn data/game015.pgn \
- data/game016.pgn data/game017.pgn data/game018.pgn data/game019.pgn \
- data/game020.pgn data/game021.pgn data/game022.pgn data/game023.pgn \
- data/game024.pgn data/game025.pgn data/game026.pgn data/game027.pgn \
- data/game028.pgn data/game029.pgn data/game030.pgn data/game031.pgn \
- data/game032.pgn data/game033.pgn data/game034.pgn data/game035.pgn \
- data/game036.pgn data/game037.pgn data/game038.pgn data/game039.pgn \
- data/game040.pgn data/game041.pgn data/game042.pgn data/game043.pgn \
- data/game044.pgn data/game045.pgn data/game046.pgn data/game047.pgn \
- data/game048.pgn data/game049.pgn data/game050.pgn data/game051.pgn \
- data/game052.pgn data/game053.pgn data/game054.pgn data/game055.pgn \
- data/game056.pgn data/game057.pgn data/game058.pgn data/game059.pgn \
- data/game060.pgn data/game061.pgn data/game062.pgn data/game063.pgn \
- data/game064.pgn data/game065.pgn data/game066.pgn data/game067.pgn \
- data/game068.pgn data/game069.pgn data/game070.pgn data/game071.pgn \
- data/game072.pgn data/game073.pgn data/game074.pgn data/game075.pgn \
- data/game076.pgn data/game077.pgn data/game078.pgn data/game079.pgn \
- data/game080.pgn data/game081.pgn data/game082.pgn data/game083.pgn \
- data/game084.pgn data/game085.pgn data/game086.pgn data/game087.pgn \
- data/game088.pgn data/game089.pgn data/game090.pgn data/game091.pgn \
- data/game092.pgn data/game093.pgn data/game094.pgn data/game095.pgn \
- data/game096.pgn data/game097.pgn data/game098.pgn data/game099.pgn \
- data/game100.pgn data/game101.pgn data/game102.pgn data/game103.pgn \
- data/game104.pgn data/game105.pgn data/game106.pgn data/game107.pgn \
- data/game108.pgn data/game109.pgn data/game110.pgn data/game111.pgn \
- data/game112.pgn data/game113.pgn data/game114.pgn data/game115.pgn \
- data/game116.pgn data/game117.pgn data/game118.pgn data/game119.pgn \
- data/game120.pgn data/game121.pgn data/game122.pgn data/game123.pgn \
- data/game124.pgn data/game125.pgn data/game126.pgn data/game127.pgn \
- data/game128.pgn data/game129.pgn data/game130.pgn data/game131.pgn \
- data/game132.pgn data/game133.pgn data/game134.pgn data/game135.pgn \
- data/game136.pgn data/game137.pgn data/game138.pgn data/game139.pgn \
- data/game140.pgn data/game141.pgn data/game142.pgn data/game143.pgn \
- data/game144.pgn data/game145.pgn data/game146.pgn data/game147.pgn \
- data/game148.pgn data/game149.pgn data/game150.pgn
+ ../src/chess/rules.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test-datatypes.c	Tue Aug 25 18:35:18 2026 +0200
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Mike Becker. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "chess/rules.h"
+
+#if __STDC_VERSION__ < 201112L
+#pragma GCC warning "Need at least C11 to compile test-datatypes.c"
+#else
+#if __STDC_VERSION__ < 202310L
+#include <assert.h>
+#endif
+
+static_assert(sizeof(Color) == 1);
+static_assert(sizeof(Row) == 1);
+static_assert(sizeof(File) == 1);
+static_assert(sizeof(Piece) == 1);
+
+static_assert(sizeof(Board) == 64);
+
+#endif /* C11 */
--- a/test/test-real-pgn.c	Mon Aug 24 16:13:51 2026 +0200
+++ b/test/test-real-pgn.c	Tue Aug 25 18:35:18 2026 +0200
@@ -31,8 +31,13 @@
 #include "chess/pgn.h"
 
 #if __STDC_VERSION__ < 202310L
-#error Need C23 compiler to compile this test.
-#endif
+/* since #warning is also a C23 feature, the only hope is this: */
+#pragma GCC warning "Need at least a C23 compiler to compile test-real-pgn.c"
+CxTestSuite* test_real_pgn_suite(void) {
+    CxTestSuite* suite = cx_test_suite_new("Skip testing real PGN Files");
+    return suite;
+}
+#else
 
 const char pgn_data[150][1500];
 
@@ -858,3 +863,5 @@
         , '\0'
     },
 };
+
+#endif /* C23 compiler */

mercurial