From bebd2fedbf97ee35e57836c5915fca4abaee2567 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 3 Sep 2026 14:14:47 +0200 Subject: [PATCH] Record the Go signature each generated wrapper is emitted with A caller of a generated wrapper needs the shape the generator CHOSE, and that shape is not the C parameter list. `tbool_value_at_timestamptz` declares four C parameters while the emitted wrapper takes three, folding the `bool *value` out-parameter into a second result; an array and its length collapse into one slice; a `char *` becomes a `string`. Anything reading the catalog C signature to call these wrappers reconstructs a shape they do not have. `SIGNATURES` states what the generator emits: each MEOS C name mapped to the Go name, the parameters and the result types, recorded at the two sites that assemble a signature. A function absent from it has no wrapper to call, which is the honest answer rather than a guess. It is the piece the object layer consumes, so the two generators cannot disagree about a folded out-parameter -- the same split MEOS.NET keeps between `tools/codegen.py` and `tools/objectgen.py`. MEASURED: 3562 wrappers emitted, 3562 recorded, so the map is total over the emitted surface rather than a subset. Regenerating from one catalog with and without this commit yields 15 files on each side and a diff of 0 lines over 41653 lines of generated Go, so the emitted package does not move. The baseline run carries no `SIGNATURES` attribute, which is what makes it a control rather than a second run of the same code. The change is confined to the Python generator and its Go output is byte-identical, so it carries no compilable change. --- tools/codegen.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/codegen.py b/tools/codegen.py index ccb0f60..9d4bf98 100644 --- a/tools/codegen.py +++ b/tools/codegen.py @@ -117,6 +117,40 @@ def _result_signature(all_returns: list[str]) -> str: return f" ({', '.join(named)})" +@dataclass +class Signature: + """The Go signature this generator emits for one MEOS function. + + A caller of a generated wrapper needs the shape the generator CHOSE, which is not + the C parameter list: an out-parameter is folded into an extra result, an array and + its length collapse into one slice, and a ``char *`` becomes a ``string``. Reading + the C signature instead reconstructs a shape the wrapper does not have. + """ + + c_name: str + go_name: str + params: list[tuple[str, str]] # (Go parameter name, Go type), in order + returns: list[str] # Go result types, WITHOUT the trailing error + + +# Every wrapper this generator emits, by MEOS C name. ``objectgen.py`` reads it so the +# object layer and the flat layer cannot disagree about a folded out-parameter: the +# object layer never parses a C parameter list, and a function absent from here has no +# wrapper to call. Filled as each function is emitted, so a caller that emits one +# function gets that one entry and a full ``generate()`` run gets them all. +SIGNATURES: dict[str, Signature] = {} + + +def _record(c_name: str, go_name: str, go_args: list[str], + returns: list[str]) -> None: + """Record the signature just assembled, splitting each ``"name type"`` apart.""" + params = [] + for arg in go_args: + name, _, go_type = arg.partition(" ") + params.append((name, go_type)) + SIGNATURES[c_name] = Signature(c_name, go_name, params, list(returns)) + + # Type mapping ---------------------------------------------------------- @dataclass @@ -602,6 +636,7 @@ def _emit_array_input_group(entry: dict, group: dict) -> EmittedFunc: f"func {go_name}({sig_args}){ret_sig} {{\n" f"{body}\n{tail}\n}}\n" ) + _record(c_name, go_name, go_args, [] if return_c == "void" else [ret_go]) return EmittedFunc(go_name, code, False) @@ -962,6 +997,7 @@ def classify_one(p, i): + "\n".join(body_lines) + "\n}\n" ) + _record(c_name, go_name, go_args, all_returns) return EmittedFunc(go_name, code, False)