Skip to content

Commit e091736

Browse files
leifericfclaude
andcommitted
portability: gcc sanitizer detection + POSIX strcasecmp + empty-TU sentinels
Three pre-existing portability bugs that landed in the v0.241-v0.252 cycle but didn't surface locally (clang+macOS): * src/gc/internal.h:150 -- defined(__has_feature) && __has_feature(...) doesn't short-circuit at the preprocessor level under gcc; split into nested #if so the right side never parses when the macro is absent. * main.c -- strcasecmp is POSIX, lives in <strings.h>. Include it under #ifndef _WIN32; alias to _stricmp on Windows. Call sites use mino_strcasecmp. * src/eval/bc/jit/{helpers,emit,patcher,patcher_x86_64,stats}.c -- whole-file #ifdef MINO_CPJIT_HOST wrap meant an empty TU on targets where the host isn't matched (e.g., mingw without the x86_64-windows opt-in). gcc -Werror=pedantic rejects empty TUs. Add a typedef sentinel after each #endif. * src/eval/bc/jit/entry.c:680 -- non-host stub for mino_jit_invoke was 4 args; header declares 5 (mino_env *env added in the v0.219 refactor). Stub now matches. Verified: full local build + tests pass; pushed to retry CI on the gcc / mingw paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d5120bf commit e091736

10 files changed

Lines changed: 79 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
# Changelog
22

3+
## v0.255.7 — Portability: gcc Sanitizer Detection, POSIX strcasecmp, Empty TUs
4+
5+
CI on ubuntu-24.04, ubuntu-24.04-arm, and windows-2022 went red on
6+
the v0.255.6 push with three pre-existing portability bugs that
7+
landed sometime in the v0.241-v0.252 cycle but didn't surface
8+
locally:
9+
10+
* `src/gc/internal.h:150` -- `defined(__has_feature) &&
11+
__has_feature(address_sanitizer)` doesn't short-circuit at the
12+
preprocessor level under gcc. gcc evaluates the right side
13+
syntactically and fails with "missing binary operator before
14+
token '('". Split into nested `#if defined(__has_feature)` so
15+
gcc never sees the `__has_feature(...)` call when the macro
16+
isn't defined.
17+
18+
* `main.c:815` -- `strcasecmp` is POSIX, declared in `<strings.h>`
19+
(not `<string.h>`). Add the include under `#ifndef _WIN32`; on
20+
Windows alias `mino_strcasecmp` to `_stricmp`. Call sites updated
21+
to use the portable name.
22+
23+
* `src/eval/bc/jit/{helpers,emit,patcher,patcher_x86_64,stats}.c`
24+
-- empty translation unit under `-Werror=pedantic` when
25+
`MINO_CPJIT_HOST` isn't defined for the build target (e.g.,
26+
mingw without `MINO_CPJIT_X86_64_WINDOWS`). Added a sentinel
27+
typedef after each `#endif` to keep each TU non-empty.
28+
29+
* `src/eval/bc/jit/entry.c:680` -- `mino_jit_invoke` stub had 4
30+
parameters; header declared 5 (`mino_env *env` was added
31+
in the v0.219+ refactor and the stub wasn't updated). Stub
32+
signature now matches the header.
33+
34+
Verified: full local build + tests pass (1273 tests / 4555
35+
assertions, 0 failed). Pushed to trigger a fresh CI run.
36+
337
## v0.255.6 — Fix: BC Speculative Fold Longjmps Through Active Try-Frame
438

539
Surfaced by mino-tests v0.7.0's `gen_program.clj`: a `defn` whose

main.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727

2828
#ifndef _WIN32
2929
#include <execinfo.h>
30+
#include <strings.h> /* POSIX strcasecmp on Linux + macOS */
31+
#define mino_strcasecmp strcasecmp
3032
#else
3133
# define WIN32_LEAN_AND_MEAN
3234
# include <windows.h>
35+
# define mino_strcasecmp _stricmp
3336
#endif
3437

3538
#if defined(__APPLE__)
@@ -812,11 +815,11 @@ int main(int argc, char **argv)
812815
mino_jit_mode_t cli_jit = MINO_JIT_MODE_AUTO;
813816
int cli_jit_set = 0;
814817
if (cli_jit_mode != NULL) {
815-
if (strcasecmp(cli_jit_mode, "auto") == 0) {
818+
if (mino_strcasecmp(cli_jit_mode, "auto") == 0) {
816819
cli_jit = MINO_JIT_MODE_AUTO; cli_jit_set = 1;
817-
} else if (strcasecmp(cli_jit_mode, "off") == 0) {
820+
} else if (mino_strcasecmp(cli_jit_mode, "off") == 0) {
818821
cli_jit = MINO_JIT_MODE_OFF; cli_jit_set = 1;
819-
} else if (strcasecmp(cli_jit_mode, "on") == 0) {
822+
} else if (mino_strcasecmp(cli_jit_mode, "on") == 0) {
820823
cli_jit = MINO_JIT_MODE_ON; cli_jit_set = 1;
821824
} else {
822825
fprintf(stderr,
@@ -898,7 +901,7 @@ int main(int argc, char **argv)
898901
int explicit_on = (cli_jit_set && cli_jit == MINO_JIT_MODE_ON);
899902
if (!explicit_on) {
900903
const char *env_jit = getenv("MINO_JIT");
901-
if (env_jit != NULL && strcasecmp(env_jit, "on") == 0) {
904+
if (env_jit != NULL && mino_strcasecmp(env_jit, "on") == 0) {
902905
explicit_on = 1;
903906
}
904907
}

src/eval/bc/jit/emit.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,3 +762,7 @@ int mino_jit_compile_inner(mino_state_t *S, mino_val_t *fn_val)
762762
}
763763

764764
#endif /* MINO_CPJIT_HOST */
765+
766+
/* Keep this TU non-empty under -Werror=pedantic when the gate above
767+
* is false. */
768+
typedef int mino_jit_emit_tu_marker;

src/eval/bc/jit/entry.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,9 +678,10 @@ int mino_jit_compile(mino_state_t *S, mino_val_t *fn_val)
678678
}
679679

680680
mino_val_t *mino_jit_invoke(mino_state_t *S, mino_bc_fn_t *bc,
681-
mino_val_t **regs, mino_val_t **consts)
681+
mino_val_t **regs, mino_val_t **consts,
682+
mino_env_t *env)
682683
{
683-
(void)S; (void)bc; (void)regs; (void)consts; return NULL;
684+
(void)S; (void)bc; (void)regs; (void)consts; (void)env; return NULL;
684685
}
685686

686687
void mino_jit_invalidate(mino_state_t *S, mino_val_t *fn)

src/eval/bc/jit/helpers.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,3 +778,7 @@ mino_val_t **mino_jit_loop_int_lt_inc_slow(mino_state_t *S, mino_val_t **regs,
778778
}
779779

780780
#endif /* MINO_CPJIT_HOST */
781+
782+
/* Keep this TU non-empty under -Werror=pedantic when MINO_CPJIT_HOST
783+
* isn't defined for the build target. */
784+
typedef int mino_jit_helpers_tu_marker;

src/eval/bc/jit/patcher.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,7 @@ void mino_jit_write_trampoline(unsigned char *slot, uintptr_t target_addr)
200200
}
201201

202202
#endif /* MINO_CPJIT_HOST_ARM64 */
203+
204+
/* Keep this TU non-empty under -Werror=pedantic when the gate above
205+
* is false (e.g., x86_64 builds). */
206+
typedef int mino_jit_patcher_arm64_tu_marker;

src/eval/bc/jit/patcher_x86_64.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,7 @@ void mino_jit_write_trampoline(unsigned char *slot, uintptr_t target_addr)
188188
}
189189

190190
#endif /* MINO_CPJIT_HOST_X86_64 */
191+
192+
/* Keep this TU non-empty under -Werror=pedantic when the gate above
193+
* is false (e.g., arm64 builds). */
194+
typedef int mino_jit_patcher_x86_64_tu_marker;

src/eval/bc/jit/stats.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,7 @@ void mino_jit_stats_record(const mino_bc_fn_t *bc,
172172
}
173173

174174
#endif /* MINO_CPJIT_HOST */
175+
176+
/* Keep this TU non-empty under -Werror=pedantic when the gate above
177+
* is false. */
178+
typedef int mino_jit_stats_tu_marker;

src/gc/internal.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,20 @@ typedef struct {
146146
* Sanitizer builds want loud failure to flag liveness regressions;
147147
* release builds keep the documented soft-loss path so the
148148
* conservative C-stack scanner still covers values that escape
149-
* the pin array. */
150-
#if (defined(__has_feature) && __has_feature(address_sanitizer)) \
151-
|| defined(__SANITIZE_ADDRESS__) \
149+
* the pin array. The __has_feature check is nested inside its own
150+
* `defined` test because gcc evaluates the second half of an &&
151+
* syntactically even when the first half is false -- splitting
152+
* into nested #if keeps gcc preprocessing happy while still
153+
* detecting clang's ASan / TSan / UBSan flavours. */
154+
#if defined(__has_feature)
155+
# if __has_feature(address_sanitizer) \
156+
|| __has_feature(thread_sanitizer) \
157+
|| __has_feature(undefined_behavior_sanitizer)
158+
# define MINO_GC_PIN_LOUD_ASSERT 1
159+
# else
160+
# define MINO_GC_PIN_LOUD_ASSERT 0
161+
# endif
162+
#elif defined(__SANITIZE_ADDRESS__) \
152163
|| defined(__SANITIZE_THREAD__) \
153164
|| defined(__SANITIZE_UNDEFINED__)
154165
# define MINO_GC_PIN_LOUD_ASSERT 1

src/mino.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*/
2929
#define MINO_VERSION_MAJOR 0
3030
#define MINO_VERSION_MINOR 255
31-
#define MINO_VERSION_PATCH 6
31+
#define MINO_VERSION_PATCH 7
3232

3333
/*
3434
* Human-readable version string of the *linked* runtime, e.g. "0.48.0".

0 commit comments

Comments
 (0)