From 9eed4cbd8c419ca951a32da4f40fc4bb09128cc9 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 20 Sep 2026 13:00:55 +0700 Subject: [PATCH] The dyn header claimed a guarantee the linker does not give test/dyn_ops.c includes the header and calls most of what it declares, so a renamed function fails to link. It does not catch a signature that drifts while the name stays -- C links on names, not types -- and it does not reach flan_dyn_map_get, flan_dyn_map_set, flan_dyn_map_contains, flan_dyn_is_nil or flan_dyn_need_not_nil at all, none of which dyn_ops.c mentions. The comment now says that, and says which hole is closable from here. --- runtime/flan_dyn.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/runtime/flan_dyn.h b/runtime/flan_dyn.h index 3d58cb4..6a82133 100644 --- a/runtime/flan_dyn.h +++ b/runtime/flan_dyn.h @@ -3,12 +3,21 @@ * This header is not compiled into a program. The build embeds the runtime's * .c files as strings and hands each one to clang on its own, with no include * path (see [Build.compile_c]), so runtime/flan_dyn.c declares everything it - * defines and this file declares it a second time. That second copy is not - * held honest by hand the way a repeated escape table would be: it is made - * mechanical, because test/dyn_ops.c includes this header and names every - * function below, so a signature that drifts from the implementation is a - * link error in `dune test` rather than a surprise at someone else's call - * site. + * defines and this file declares it a second time. What keeps that second + * copy honest is partial, and the exact shape of it is worth writing down + * rather than overclaiming: test/dyn_ops.c includes this header and calls + * most of what it declares, so a name that is spelled one way here and + * another way in flan_dyn.c fails to link in `dune test`. + * + * Two holes in that. A signature that drifts while the name stays is not + * caught at all — C links on names and not on types, so a changed parameter + * or return type compiles on both sides and goes wrong at the call site + * instead of failing the build. And five of the names below are never + * referenced by dyn_ops.c — [flan_dyn_map_get], [flan_dyn_map_set], + * [flan_dyn_map_contains], [flan_dyn_is_nil] and [flan_dyn_need_not_nil] — + * so not even the rename check reaches them. Calling those five from + * dyn_ops.c would close the second hole; nothing available from here closes + * the first. * * Who reads it: the compiler lane, which emits calls to these names, and the * C tests. The whole of the boundary is here. What is behind it — the value