From a864eca69e16143f724dc12e52c3a5dda4f66d80 Mon Sep 17 00:00:00 2001 From: Kris Kersey Date: Sat, 27 Jun 2026 18:20:20 +0000 Subject: [PATCH] fix(build): rebuild lsp_all.o when its included headers change lsp_all.o is a unity object that #includes every lsp/*.c resolver; those include cbm.h, and CBMCall is copied BY VALUE across the lsp->pipeline boundary (cbm_calls_push). Its Make rule depended only on lsp_all.c, not on the headers it pulls in. So after CBMCall gained a field (is_method, #477) an incremental build kept a stale lsp_all.o with the old struct layout while the rest of the tree recompiled against the new one -- an ODR mismatch ASan flags as a stack-buffer-underflow in cbm_calls_push (the by-value copy under-reads the shorter stale struct). Clean builds (CI) always match, so it only bites incremental developer builds. Add an LSP_UNITY_DEPS prerequisite (lsp/*.c + lsp/*.h + lsp/generated/*.c + internal/cbm/*.h) to lsp_all.o and prod_lsp_all.o so the object rebuilds when any included header or unity source changes. Wildcard-based, so new LSP resolvers are covered automatically; explicit because this Makefile has no -MMD auto-deps. Verified: touching internal/cbm/cbm.h now rebuilds lsp_all.o (was stale before); clean build + full ASan suite green, rustlsp_dbg_macro_inner_call no longer aborts. Signed-off-by: Kris Kersey --- Makefile.cbm | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile.cbm b/Makefile.cbm index 2bcf7b4d..7b687d19 100644 --- a/Makefile.cbm +++ b/Makefile.cbm @@ -135,6 +135,18 @@ EXTRACTION_SRCS = \ # LSP resolvers (compiled as one unit via lsp_all.c) LSP_SRCS = $(CBM_DIR)/lsp_all.c +# Header/source dependencies of the lsp_all unity object. lsp_all.c #includes +# every lsp/*.c resolver, which in turn include cbm.h — and CBMCall is copied +# BY VALUE across the lsp -> pipeline boundary (cbm_calls_push). This object is +# compiled standalone and linked in, NOT recompiled with the rest on every link, +# so it must list the headers/sources it pulls in. Without this, a changed struct +# layout (e.g. a new CBMCall field) leaves a stale lsp_all.o with the old layout — +# an ODR mismatch that under-reads the by-value struct copy. Explicit because this +# Makefile does not use compiler auto-dependency (-MMD) generation. +LSP_UNITY_DEPS = $(CBM_DIR)/lsp_all.c \ + $(wildcard $(CBM_DIR)/lsp/*.c $(CBM_DIR)/lsp/*.h \ + $(CBM_DIR)/lsp/generated/*.c $(CBM_DIR)/*.h) + # Tree-sitter runtime TS_RUNTIME_SRC = $(CBM_DIR)/ts_runtime.c @@ -434,7 +446,7 @@ $(BUILD_DIR)/%.o: $(CBM_DIR)/%.c | $(BUILD_DIR) $(BUILD_DIR)/ts_runtime.o: $(CBM_DIR)/ts_runtime.c | $(BUILD_DIR) $(CC) $(GRAMMAR_CFLAGS_TEST) -c -o $@ $< -$(BUILD_DIR)/lsp_all.o: $(CBM_DIR)/lsp_all.c | $(BUILD_DIR) +$(BUILD_DIR)/lsp_all.o: $(LSP_UNITY_DEPS) | $(BUILD_DIR) $(CC) $(GRAMMAR_CFLAGS_TEST) $(SANITIZE) -c -o $@ $< $(BUILD_DIR)/preprocessor.o: $(CBM_DIR)/preprocessor.cpp | $(BUILD_DIR) @@ -524,7 +536,7 @@ $(BUILD_DIR)/prod_%.o: $(CBM_DIR)/%.c | $(BUILD_DIR) $(BUILD_DIR)/prod_ts_runtime.o: $(CBM_DIR)/ts_runtime.c | $(BUILD_DIR) $(CC) $(GRAMMAR_CFLAGS) -c -o $@ $< -$(BUILD_DIR)/prod_lsp_all.o: $(CBM_DIR)/lsp_all.c | $(BUILD_DIR) +$(BUILD_DIR)/prod_lsp_all.o: $(LSP_UNITY_DEPS) | $(BUILD_DIR) $(CC) $(GRAMMAR_CFLAGS) -c -o $@ $< $(BUILD_DIR)/prod_preprocessor.o: $(CBM_DIR)/preprocessor.cpp | $(BUILD_DIR)