commit 50f2be4ee7f358e1fa5abd6b3808d6a4c4742cd6
parent c09a8d50742caad102d77ca255eb63ae2b68a6ce
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 1 Jun 2026 12:25:18 -0700
include: make frontend.h the single frontend front door
frontend.h already aspired to be the "one-stop include for source
language implementations" but was missing compile.h (the
CfreeFrontendVTable registration contract), so frontends still pulled
cg.h/compile.h/core.h/object.h directly alongside it.
Add compile.h to frontend.h so <cfree/frontend.h> alone is sufficient to
write and register a frontend, then migrate lang/ to the front door:
drop the now-redundant granular includes from frontend TUs and reduce
the c/toy/wasm public headers to just frontend.h. Internal headers that
genuinely need only the CG surface (decl.h/type.h/cg_public_compat.h)
and the preprocessor (must not pull in cg.h) keep their narrow includes.
Reconcile INTERFACES.md: the "no umbrella header" invariant now
distinguishes a forbidden whole-library cfree.h from the sanctioned
tier-scoped frontend.h facade.
make lib green under -Werror; test-toy (1338/0/24) and test-parse
(3784/0/0 + parse-err 123/123) pass.
Diffstat:
10 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/doc/INTERFACES.md b/doc/INTERFACES.md
@@ -48,8 +48,13 @@ entirely do; the deliberate exceptions are the C frontend reaching
to `include/cfree/`, don't widen the exception list.
**Invariants (keep them true):**
-- There is **no** umbrella `include/cfree.h`. Consumers include the specific
- headers they use: `include/cfree/*.h` and `include/cfree/support/*.h`.
+- There is **no** whole-library umbrella `include/cfree.h`. Consumers include
+ the specific headers they use: `include/cfree/*.h` and
+ `include/cfree/support/*.h`. The one sanctioned exception is a *tier-scoped*
+ facade: `frontend.h` is the deliberate single front door for the frontend
+ tier and re-exports `cg.h`, `compile.h`, `source.h`, and `support/arena.h`.
+ Scoping a facade to one tier is fine; a grab-bag spanning the whole library
+ is not.
- The driver includes only `<cfree/...>` (the `-Ilang` exception is a single
frontend public header), never `src/`.
- `*_internal.h` headers are private to one subsystem and must not be included
@@ -72,7 +77,7 @@ it uses.
| `config.h` | Build-time component enable flags (arch / obj-format / language / subsystem / tool). Preprocessor-only. | — | build |
| `compile.h` | High-level source->object compilation; frontend registration vtable; dep iteration. | `CfreeCompileSession`, `CfreeDepIter` | driver, frontends |
| `cg.h` | Code-generation API (the largest contract): a stack-machine typed IR over `CfreeCg`. Types/ABI, functions, control flow, memory, arithmetic, calls, intrinsics, inline+file asm, static data. | `CfreeCg` | frontends |
-| `frontend.h` | Frontend convenience bridge: panic boundary (`cfree_frontend_run`), metrics scopes, fatal helpers. | — | frontends |
+| `frontend.h` | Frontend **front door**: re-exports `cg.h` / `compile.h` / `source.h` / `support/arena.h`, plus the panic boundary (`cfree_frontend_run`), metrics scopes, and fatal helpers. Including this alone is enough to write and register a frontend. | — | frontends |
| `source.h` | Source registry: stable file IDs + include-edge recording. | — | frontends |
| `preprocess.h` | Standalone C preprocessor entry. | — | driver |
| `object.h` | Format-neutral object model: builder + read-only inspection; section/symbol/reloc enums. | `CfreeObjBuilder`, `CfreeObjFile` | cg, link, jit, disasm, dwarf |
@@ -94,6 +99,13 @@ it uses.
- `cg.h` is by far the largest contract and the one frontends couple to hardest.
Changes here ripple to every frontend — treat it as the highest-risk public
interface.
+- `frontend.h` is the only sanctioned aggregator header — a *front door* scoped
+ to the frontend tier. It re-exports the codegen / registration / source /
+ arena surface so a frontend TU includes just `<cfree/frontend.h>`. This is the
+ deliberate exception to the "no umbrella header" invariant: the scope is one
+ tier, not the whole library. A TU still reaches for a single narrow header
+ directly when that is all it needs — e.g. a lexer wanting only `core.h`, or
+ the preprocessor, which must not pull in `cg.h`.
- `core.h` defines the host vtables (`CfreeHeap`, `CfreeWriter`, `CfreeDiagSink`,
`CfreeContext`). These are the project's "no global state" enforcement point;
every subsystem threads context through them rather than reaching for a static.
@@ -269,9 +281,13 @@ with `cfree_register_frontend(compiler, language, vtable)`;
`src/api/lang_registry.h::lang_registry_init` auto-wires the enabled
`CFREE_LANG_*` frontends at compiler construction.
-**What frontends consume** is overwhelmingly the public API — `cg.h`,
-`frontend.h`, `source.h`, `object.h`, `support/arena.h`, `support/hashmap.h`,
-`core.h` — with the two documented internal exceptions noted in the boundary map.
+**What frontends consume** is overwhelmingly the public API, reached through a
+single front door: `<cfree/frontend.h>` re-exports `cg.h`, `compile.h`,
+`source.h`, and `support/arena.h` (and transitively `core.h` + `object.h`), so a
+frontend TU includes just that one header. The remaining direct public includes
+are the ones *outside* the facade — `support/hashmap.h` for hashed tables, and
+`preprocess.h` / `jit.h` / `wasm.h` where a specific frontend or runner needs
+them — plus the two documented internal exceptions noted in the boundary map.
| Frontend | Public entry | Notable internal headers |
|----------|--------------|--------------------------|
diff --git a/include/cfree/frontend.h b/include/cfree/frontend.h
@@ -2,6 +2,7 @@
#define CFREE_FRONTEND_H
#include <cfree/cg.h>
+#include <cfree/compile.h>
#include <cfree/source.h>
#include <cfree/support/arena.h>
#include <stdarg.h>
@@ -10,11 +11,17 @@
/*
* Language frontend convenience API.
*
- * This header is the intended one-stop include for source language
- * implementations. It includes:
- * - cfree/cg.h for code emission
- * - cfree/source.h for source file ids and include-edge recording
- * - cfree/support/arena.h for short-lived frontend allocation
+ * This header is the intended one-stop front door for source language
+ * implementations. It aggregates the public surface a frontend needs:
+ * - cfree/cg.h code emission (transitively pulls core.h + object.h)
+ * - cfree/compile.h the CfreeFrontendVTable registration contract
+ * - cfree/source.h source file ids and include-edge recording
+ * - cfree/support/arena.h short-lived frontend allocation
+ *
+ * Including <cfree/frontend.h> alone is therefore sufficient to write and
+ * register a frontend. Reach for a single narrow public header directly only
+ * when a translation unit genuinely needs just that one — e.g. a lexer that
+ * wants only core.h, or the preprocessor, which must not pull in cg.h.
*
* The declarations below are the frontend execution boundary and host-service
* shims that do not belong to codegen, source registry, or allocation.
diff --git a/lang/c/c.c b/lang/c/c.c
@@ -1,7 +1,5 @@
#include "c.h"
-#include <cfree/cg.h>
-
#include "decl/decl.h"
#include "lex/lex.h"
#include "parse/parse.h"
diff --git a/lang/c/c.h b/lang/c/c.h
@@ -16,10 +16,7 @@
* Standalone preprocessing has moved to <cfree/preprocess.h>'s
* cfree_cpp_preprocess(). */
-#include <cfree/compile.h>
-#include <cfree/core.h>
#include <cfree/frontend.h>
-#include <cfree/object.h>
extern const CfreeFrontendVTable cfree_c_frontend_vtable;
diff --git a/lang/toy/expr.c b/lang/toy/expr.c
@@ -1,4 +1,3 @@
-#include <cfree/cg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
diff --git a/lang/toy/internal.h b/lang/toy/internal.h
@@ -1,8 +1,6 @@
#ifndef CFREE_TOY_INTERNAL_H
#define CFREE_TOY_INTERNAL_H
-#include <cfree/cg.h>
-#include <cfree/compile.h>
#include <cfree/frontend.h>
#include <stddef.h>
#include <stdint.h>
diff --git a/lang/toy/parser.c b/lang/toy/parser.c
@@ -1,4 +1,3 @@
-#include <cfree/cg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c
@@ -1,4 +1,3 @@
-#include <cfree/source.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
diff --git a/lang/toy/toy.h b/lang/toy/toy.h
@@ -1,7 +1,6 @@
#ifndef CFREE_TOY_H
#define CFREE_TOY_H
-#include <cfree/compile.h>
#include <cfree/frontend.h>
extern const CfreeFrontendVTable cfree_toy_frontend_vtable;
diff --git a/lang/wasm/wasm.h b/lang/wasm/wasm.h
@@ -1,8 +1,6 @@
#ifndef CFREE_LANG_WASM_H
#define CFREE_LANG_WASM_H
-#include <cfree/compile.h>
-#include <cfree/core.h>
#include <cfree/frontend.h>
#include "runtime_abi.h"