Repository navigation
Transpile PostgreSQL C macros into Rust macro_rules! - #2389
eeeebbbbrrrr wants to merge 44 commits into
Conversation
Discover macro definitions from configured PostgreSQL headers with pgrx-pg-config and Clang. Provide an owned library inventory and a CLI with a required positional PostgreSQL version.
Macro discovery now scans pgrx's complete version-specific header wrapper and records physical files and line spans. The CLI and primary library inventory contain only function macros from the selected PostgreSQL server header tree, with other definitions retained as conversion context.
Inspect the selected PostgreSQL headers with matching Clang and emit a bounded integer macro_rules family during pgrx-pg-sys binding generation. Generated macros preserve C type identities, promotions, conversions, evaluation counts and overflow policy, while unsupported constructs retain source spans and skip reasons. Track compiler, flags, headers and include-search changes for regeneration. Differential fixtures compare generated Rust with original C across PostgreSQL 15–19.
Preserve verified PostgreSQL binding names, literal spellings, and direct macro calls in generated Rust macros. Track dependencies with petgraph and skip macros and their callers when bindgen and Clang disagree on a constant, reporting both values without changing bindgen output. Include original C definitions in documentation and ship PostgreSQL 15–19 macro snapshots for docs.rs.
Generate pointer, field, enum, callback, mutation and function-call adapters from Clang declarations and fresh bindgen output without handwritten macro signatures. Preserve C evaluation contexts and partially initialized storage, compile native access primitives under the inspected profile, and retain PostgreSQL's FFI guard. Expose generated adapters through the docs.rs include path and reject unsupported constructs or inconsistent bindings with dependency-aware diagnostics. Add differential tests against original C, including the macros pgrx previously ported by hand.
Avoid recording preprocessing inventories in declaration-only Clang probes, and share snapshot checks between adjacent compiler phases. Index C and Rust storage identities once per immutable emission pass instead of once per macro. Optimize SHA-2 in debug builds so CLI input verification does not dominate runtime. Preserve compiler input checks, dependency skips, and generated output.
Generated macros now execute ordered C statements, preserve nested scopes and local initialization, and apply C assignment conversion to caller returns. Caller-scope captures are explicit operands, while guards reject local capture that Rust hygiene cannot reproduce. Compiler assertions remain skips instead of becoming runtime calls, and C-oracle tests cover sequencing, conversions, mutations, and unused arguments.
Generated macros now preserve if/else control flow, caller returns, and local initialization across every surviving branch. Unbraced conditionals require an explicit statement boundary. Original argument names and verified Rust type aliases remain visible while C rank, qualifiers, and null semantics stay intact. Refresh the documentation snapshots from the current generated bindings and isolate their native adapters from docs.rs builds.
Verify builtin prototypes and dynamic byte-swap behavior under the selected compiler profile before lowering to Rust. Preserve C argument conversions, result ranks, evaluation counts, and closed null constants, while isolating rejected intrinsics. Track canonical identities for availability-only header dependencies so unchanged symlinks pass validation and changed inputs are rejected. Refresh the PG15–19 macro snapshots and compare generated expressions with original C in standalone and binding integration tests.
Transpile named, generic, and dotted member offsets without reading objects, including packed, volatile, flexible, and unsupported value fields. Verify the C size_t rank and share layout checks across field adapters to keep generation within its source budget.
Prove the exact long(long, long) prototype and first-operand identity under the selected compiler profile. Preserve evaluation and C conversion of both operands, including side effects, and retain the existing byte-swap failure isolation. Add independent C oracles and PostgreSQL integration tests, and refresh the PG15–19 macro snapshots from host-generated bindings.
Use bounded Clang probes to resolve fixed token pastes while rejecting pastes that construct identifiers from caller operands. Track synthesized helpers and constants in the dependency graph so unsupported constructs and binding disagreements still skip every affected macro. Add independent C oracles and PG15-19 integration tests, and refresh macro snapshots from the host-generated bindings.
Move generated PostgreSQL macros into formatted modules by source header and expose them through pgrx. Share expression bodies and restrict native adapters to required capabilities while preserving C parameter names, aliases, evaluation and branch prediction hints. Reject ambiguous type applications and callback identities, propagate adapter failures, and validate fresh compiler artifacts instead of accepting stale output.
Credit pgrx-c-macros in the generated headers and describe regeneration of macros and their support code.
Publish pgrx-c-macros after pgrx-pg-config and before pgrx-bindgen, and include it in the preflight packaging check.
Remove the develop-only pull request filters from the test, packaging, and runas workflows.
Explain handwritten declarations, private helpers, modules, and tests across the C macro pipeline. Document the runtime access and conversion contracts while leaving generated snapshots unchanged.
c3d3433 to
e0ebb0a
Compare
Keep the module, item, and test documentation while removing doc comments from use declarations.
Keep formatting optional when rustup lacks rustfmt, and handle Clang dependency paths and diagnostic excerpts across platforms. Accept reviewed LTO flags while producing ordinary native objects for the Rust linker. Compile native macro helpers and the C shim together for the active PostgreSQL version to avoid duplicate header definitions, and retain incompatible-pointer rejection coverage with the current compiler diagnostic.
Preprocess native helpers with the inspected flags before compiling PIC machine code, so backend flags cannot change header branches. Move PageSetPrunable coverage into the PostgreSQL harness and normalize Windows C stdio line endings in numeric oracle comparisons.
Describe the transpiler end to end, add extension examples, and introduce the feature in the top-level README. Enable macro warnings only with PGRX_MACRO_DEBUG=1 while preserving audit reports and tracking setting changes for rebuilds. Correct singular argument descriptions in generated documentation.
Generated pgstat_count_* expansions reference guarded PostgreSQL functions even when their test inputs short-circuit before calling them, so standalone Linux test binaries cannot link. Move the existing assertions and interrupt compile witnesses into pgrx-unit-tests while keeping pure prediction-hint checks in pgrx-pg-sys.
Select pgrx-c-macros alongside pgrx-bindgen so Cargo can resolve the local crate when testing unpublished release versions.
eeeebbbbrrrr
left a comment
There was a problem hiding this comment.
Static review of 5d0fc41. I didn't build or run anything. I read the hand-written code in pgrx-c-macros, pgrx-bindgen and the pgrx-pg-sys runtime, and sampled the pg15–19 snapshots.
What looks right
- Runtime: I found no path from safe code to UB. Every memory-touching entry point is an
unsafe fn, and the traits it trusts areunsafeor sealed. The generatedunsafe {}blocks that actually run never contain caller tokens. Native calls go throughpg_guard_ffi_boundary. - C typing: integer promotions, the usual arithmetic conversions, literal typing, shifts and
?:match the C rules for LP64.
What blocks this
- Builds that now fail: macro inspection errors fail the
pgrx-pg-sysbuild, with no opt-out. Three configurations that build ondevelopfail here:- RPM/Arch CFLAGS containing
-Wp,; - the cross-compile recipe used in CI and the book;
- hosts without a Clang driver that matches libclang.
- RPM/Arch CFLAGS containing
- Builds that silently lose every macro: Linux aarch64, and any profile with an unmodeled
-fflag. They compile, but export zero macros and print no warning.
Inline comments are tagged P0 (breaks a currently supported build), P1 (risk under specific conditions) or P2 (hardening or quality). "Latent" means the current PG15–19 output doesn't hit it. Three top-level comments cover the larger changes: failing soft, public API stability, and size and build cost.
The PR description should mention the new Clang-driver requirement and the change that passes CFLAGS to bindgen, since both affect existing users.
Larger change 1: make macro generation unable to break or silently empty a
|
Larger change 2: the public macro API varies by host; give users a way to deal with thatWhich
An extension that compiles against a pgrx-managed install can therefore fail against a distro or cassert build with only "cannot find macro Suggestions:
|
Larger change 3: package size, generated code volume, and rebuild costMeasured or counted locally on 5d0fc41 unless noted. Package size
Code compiled on every build
Build-script and rebuild cost (predicted, not measured)
Snapshot churn
Per-call expansion
Before merging, I'd like to see:
|
Execute floating-point value checks as standard C and retain compile-time type proofs. Restrict generated-runtime oracles to supported targets while keeping portable frontend checks and adding a compiler-derived LLP64 rejection regression. Account for Clang profiles that report indeterminate floating-point evaluation under fast-math.
The native support test intentionally compiles its C oracle with the original flags, which can produce relocations rejected by Linux's default PIE linker. Disable PIE only when linking that oracle, preserving its preprocessing semantics and retaining default executable and shared-library checks for generated archives.
Require the native expression oracle to use the supported LP64 target family and independently assert its C storage and sizeof identities. Terminate generated C test sources with a newline so strict compiler rejection proves the intended constraints, while retaining portable pointer checks on Windows.
Recognize Clang's -ccc-gcc-name as an option with a separate value instead of rejecting the GCC name as an extra C input. Keep the original compiler arguments unchanged and verify AArch64 target facts against independent C probes, with regression coverage for missing values, NUL bytes, and additional inputs.
Always emit the availability classifier, including when the inspected ABI rejects every macro or target inspection is unavailable. Retain operand adapters only when emitted macros need them, so empty output requires no C runtime capabilities. Cover refused cross profiles, zero-argument exports, output bounds, and external consumers of the unavailable module tree.
Keep optional macro failures from breaking ordinary bindings while preserving verified compiler and storage facts. Model target char, integer ranks, alignment and calling conventions with original C oracle coverage. Fix macro hygiene, field access, packaged headers, input tracking and public operand documentation.
Regenerate the PG15 through PG19 bindings and macro modules together from fresh local bindings and the verified C target profile.
Add compiler-verified static inline adapters and C expression result helpers so callers use the same macro API across PostgreSQL versions. Preserve selected binding identities, original string and metadata storage, callback contracts, compiler flags, and target ABI proofs. Transfer complete checked target bundles, retain ordinary binding builds when macro generation is unavailable, and refresh the Linux documentation snapshots.
Use the target C long and inspected uintptr_t identities in macro integration tests so LLP64 and narrower targets retain the same semantic checks. Reuse Rust identifier validation for generated typedef aliases, with coverage for C names that are Rust keywords or an unrepresentable underscore.
…ofiles Follow the inspected enum and timing-field storage when checking raw C values. Run byte-order checks in the PostgreSQL harness so guarded inline fallbacks are exercised safely, including PG19 Datum conversion.
Record actual inclusions in the existing macro-dump pass so valid -include and -imacros profiles reconcile with libclang. Keep canonical inclusion equality and separate availability dependencies, bound the temporary witness, and cover escaped paths, symlink changes, and output limits.
Retain one process-lifetime owner per loaded DLL path so LLVM thread-exit callbacks cannot jump into unloaded code. Preserve bindgen runtime identity and cover repeated scanner worker exits in an isolated regression.
Compare original C and emitted Rust text without the extra carriage returns inserted by Windows stdout. Preserve every record and literal carriage return, and verify spacing and empty records with an actual C program.
Use the target c_ulong representation for admitted numeric enum storage and reject the opposite storage width. Preserve separate checks for C rank, nominal enum identity, checked variants, and duplicate registrations on LP64 and LLP64 targets.
Preserve const and volatile on fundamental void types so pointer casts do not depend on incidental header declarations. Compare emitted validity macros with original C and check pointer qualification under native and Windows/Linux compiler profiles. Include structured skip reasons when the PostgreSQL oracle corpus is incomplete.
Configure Windows C stdout in binary mode before main in both execution harnesses so mixed diagnostic output compares byte for byte. Preserve literal carriage returns, CRLFs, blank records, and fixture source locations. Keep output comparisons exact and cover interleaved native and Rust writes.
Windows system headers define NULL identically at multiple locations, which caused PageIsValid to be skipped despite an established final replacement. Expand matching object dependencies through the original headers and retain every possible source span without assigning a guessed owner. Keep ambiguous public roots, function dependencies, and unresolved definitions rejected, and cover native and cross-target null semantics and restored macro definitions.
Formatting every generated file in one command exceeded the Windows process limit in temporary extension workspaces. Use bounded filename batches while preserving formatter options, optional-tool handling, source comments, and staged publication. Add long-path formatting and quoting-budget regressions, and simplify four existing bindgen expressions flagged by strict Clippy.
Clang's header writer prepends dependency entries, so newer Darwin SDKs were rejected as mismatched includes. Verify that prefix against the same invocation's effective -fdepfile-entry arguments and fingerprint both path spellings while keeping actual C include comparison exact. Decode records as a stream so duplicate paths do not accumulate.
PostgreSQL prediction macros return int under MSVC and long when they use the GNU builtin. Compare generated results with the original headers under the verified build profile, including exact rank, width, value and individual operand evaluation counts. Share the bounded original-header harness with the paste tests.
The outer module documentation already explains the verified binding identities; remove the redundant inner comment that triggers mixed_attributes_style.
|
The Windows PG15 failure came from the test assuming that I also reproduced a generation refusal with Apple Clang 21 and the current macOS SDK. Clang's header witness writer prepends explicit dependency entries such as SDKSettings.json before actual C inclusions; b9ee9d8 verifies that ordered prefix against the effective compiler arguments and fingerprints those dependencies while preserving exact agreement for C headers. All 222 affected compiler/oracle tests and nine original-header integration tests passed locally, as did workspace formatting and strict compiler-crate Clippy. Broader integration linting still reports three existing library warnings in libpq and panic handling; the changed test fixtures have no lint warnings, and eba7ebc removes the duplicate binding-namespace documentation found during that check. |
This adds
pgrx-c-macros, a library and clap CLI that follows cargo-pgrx’s PostgreSQL selection rules to discover and transpile function-style server macros with header and line-span provenance, supported by an architecture guide and extension book chapter.pgrx-pg-sysbuilds, libclang and a compatible Clang executable establish the active definitions, declarations and recorded C profile alongside fresh bindgen output, while tracked inputs trigger regeneration and optional inspection/native failures preserve ordinary bindings through an unavailable report and classifier;PGRX_C_MACROS=0opts out explicitly.sizeof, alignment,offsetof, return adapters, supported statement blocks, compiler-verified byte swaps/branch hints and closed token pastes, with native access and ABI adapters compiled into a verified static archive.cmacros/pgNN/modules follow source-header paths, preserve parameter and verified literal/symbol/type-alias spellings, document the original C definitions and safety/panic domains, and expose macros throughpgrxwith publiccmacros::coperand tags andif_c_macro!availability checks.OUT_DIR/pgNN_macro_report.json, diagnostics requirePGRX_MACRO_DEBUG=1, explanatory comments mark uncertain expansion fallbacks, and ordinary cargo tests compare generated Rust with original C for configured PostgreSQL installations.