From c903600e34225689fdb5a20b1d74daceb393ea5a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 17:37:37 +0000 Subject: [PATCH 1/2] edg: commit kencc_targ.h; record that both named risks are closed `kencc_targ.h' existed only in a scratch directory. It is the one input to `sys/src/external/edg' that is neither upstream's nor generated -- the whole target configuration, 181 lines -- so losing it with the container would have made the 133 committed files unreproducible. The NOTE referred to it and the tree did not have it. The NOTE's list of staged header edits was also stale. Four of its eight were fixed in APExp's own header tree later in the same session, for their own reasons, so a re-translation does not have to repeat them: `signal()' is prototyped, the SIG_* macros moved with it, `features.h' no longer defines `hidden', and `bsd.h' has POSIX's `getopt'. Verified against the tree rather than assumed. The four that remain are each a parameter name or a keyword and so invisible to a C caller. CLAUDE.md's EDG entry still said "investigated, not started" and "No mkfile written" with both risks open. Both are closed: the target description is EDG's own win64 integer model, and the self-translation produced 133 files that gcc accepts. What is open is the link model -- 3,094 COMDAT symbols named in more than one of the 75 files -- and that nothing here has been through pcc. Also records that the reject revert builds on the VM. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 75 +++++++++++-- sys/src/external/edg/NOTE | 45 +++++--- sys/src/external/edg/kencc_targ.h | 181 ++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 20 deletions(-) create mode 100644 sys/src/external/edg/kencc_targ.h diff --git a/CLAUDE.md b/CLAUDE.md index ba9d0b2b0..f356bdd8f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1559,6 +1559,12 @@ sweep rather than argued, and each named its own error. blast radius is every package in the tree, and no instrument here measures that* -- the cheap guard is to grep `external/` for the bare word before adding any name to `sys/include/ape`. + **CONFIRMED ON THE VM: the revert builds.** `mk distclean` plus + `mk install` completes, so the tree gets past `lex` and the whole + order after it was reached for the first time since the header + changed. *That is the weakest useful result and is worth saying so* + -- it confirms the collision is gone, and the `net_*` removal + beside it costs no link, which is all a completed build can say. - **`getopt`: TWO INSTALLED HEADERS DECLARED IT INCOMPATIBLY, and my note said there was only one.** `bsd.h:47` had `(int, char**, char*)` while **`getopt.h:8` has had POSIX's @@ -1754,13 +1760,68 @@ live either way. ## What is open, as a list **Ports not started**, in the order their risk was last measured: -- **EDG** -- investigated and measured on the host (it builds, the - default build IS the C-generating one, `eccp -S` translates a toy - with templates and virtual bases into C that gcc compiles). Two - named risks remain and both are one host afternoon: `targ_def.h` - describing kencc (`int` 4, `long` **4**, pointer 8 -- LLP64, which - EDG supports for MSVC), and whether its C back end handles the C++ - subset EDG itself is written in. No mkfile written. +- **EDG** -- **BOTH NAMED RISKS ARE CLOSED AND THE SELF-TRANSLATION IS + IN THE TREE.** `sys/src/external/edg` holds **133 generated C files, + ~2.90M lines**: all 75 `src/`, 7 `util/` and 51 `lib_src/`, produced + by a `cpfe` built from the same commit with a kencc target, plus + EDG's own 17 C++ headers verbatim. `gcc -fsyntax-only` over all 133 + is **0 errors, 0 warnings** -- which is the host's answer and says + nothing about kencc. Full detail in `sys/src/external/edg/NOTE`; + only the consequences are here. + - **Risk 1, the target description: answered by EDG's own `win64`.** + kencc on amd64 is LLP64 and win64 is EDG's LLP64 configuration, so + the values were taken from the build's generated + `cmake_defines.h` rather than invented. Every scalar width + matches, **including `long double` 8** -- win64 says 8 because + MSVC has no extended precision and kencc says 8 because + `cc/sub.c`'s `simplet()` maps `BDOUBLE|BLONG` to `types[TDOUBLE]`, + which was not predicted. `size_t`/`ssize_t`/`ptrdiff_t` are + win64's kinds too, read out of `stddef_arch.h`; **`wchar_t` is the + one place the two differ** (`unsigned int` here, `unsigned short` + there). `kencc_targ.h`, committed beside the NOTE, is the whole + configuration -- a **pre-included header**, so nothing in the EDG + tree is modified. + - **Risk 2, self-translation: it translates.** One file does not and + it is the right one -- `util/cfe_daemon_client.c`, the only file + in the tree needing a real C++ standard library, and a + unix-domain-socket client is out of scope here twice over. + - **The target was inferred from the compiler that BUILT cpfe, which + is the finding of the round.** `targ_def.h:3505` derives + `GCC_IS_GENERATED_CODE_TARGET` from `defined(__GNUC__)`, and that + one variable is the sole gate on both `__weak__` emission sites -- + so cpfe built by g++ decided its OUTPUT was for gcc and emitted + **26,712 `__attribute__((__weak__))`**, which kencc has not. A + first round also baked in LP64 and glibc's headers. Setting it to + 0 and staging APExp's own headers takes the applied-attribute + count **38,245 -> 0**; the 11 textual occurrences left are all + inside string literals, EDG's own emitter text. + - **THE LINK MODEL IS THE OPEN PROBLEM, and removing `__weak__` + exposed it rather than solving it.** Those attributes were COMDAT + emulation for vague linkage. Measured: two trivial TUs sharing one + header collide on **10 symbols**, and across `src/` there are + **3,094 COMDAT symbols named in more than one of the 75 files**. + `--one_instantiation_per_object` is deliberately NOT used -- it + covers only the template instantiations (3 of the 10) and makes + the output depend on an `edg_prelink` phase mk cannot naturally + express. Upstream's answer for a target without COMDAT is the + **Cfront-like ABI**, and that was attempted and **backed out**: + `IA64_ABI=0` makes cpfe fail to *compile* on three + `TARGET_CONFIGURATION`-suffixed macros the Cfront ABI needs, so it + is reachable by regenerating the macro configuration rather than + by setting switches, and it changes mangling, the ctor/dtor model + and vtable layout. Its own round. + - **NO MKFILE, AND NOTHING HERE HAS BEEN THROUGH pcc.** That is the + next step and the link model is what it will run into. + - **Its staging list doubled as a to-do list for our own headers.** + Eight APE headers could not be included from C++; **four were + fixed on their own evidence** later in the same session + (`signal()`'s prototype, the `SIG_*` casts, `features.h`'s + `hidden`, `bsd.h`'s `getopt`) and a re-translation need not repeat + them. Four remain, every one a parameter name or a keyword and so + invisible to a C caller: `stdlib.h`'s unguarded `_Noreturn`, a + parameter named `template`, `unistd.h`'s parameter named `new`, + and `signal.h`'s `restrict` read as a duplicate parameter name. + *EDG is a stricter compiler than gcc, and that is what it bought.* - **muon** -- in `_OPTIONAL_APPS` commented out. Never built here. - **go** -- `go1.4` is in the tree, commented out of `_CORE_APPS`, and is the only thing that mentions `Ureg` outside libap. diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index 94e817150..8832cad04 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -26,7 +26,11 @@ Converted cmake --preset linux-gcc-release -B build/kencc \ -DCMAKE_CXX_FLAGS="-include /kencc_targ.h" ninja cpfe - kencc_targ.h is the target configuration; see below. It is a + kencc_targ.h IS COMMITTED BESIDE THIS FILE -- it is the one + input to this directory that is neither upstream's nor + generated, so a scratch copy of it would have made the whole + translation unreproducible once the container went. + It is the target configuration; see below. It is a pre-included header rather than a patch: NOTHING IN THE EDG TREE IS MODIFIED. Driver: dev_tools/bin/eccp, with build/kencc/environment.sh sourced. @@ -275,26 +279,41 @@ NOTHING IN APExp's HEADER TREE WAS MODIFIED. To reproduce this directory those edits have to be repeated, and they are all cases of an APE header not being usable from C++: +STILL NEEDED -- four, and all four are a parameter name or a keyword, +so none of them changes what a C caller sees: + sys/include/ape/stdlib.h:47,49,87 `_Noreturn` (a C11 keyword that does not exist in C++) used unguarded sys/include/ape/stdlib.h:69,70 parameter named `template` sys/include/ape/unistd.h:186,192 parameter named `new` - sys/include/ape/signal.h:132-135 `restrict` as a parameter name; + sys/include/ape/signal.h:161-164 `restrict` as a parameter name; two per prototype reads as a duplicate parameter name in C++ - sys/include/ape/signal.h:57 `signal()`'s handler parameter - declared `void (*)()` -- unprototyped -- where POSIX says - `void (*)(int)` - sys/include/ape/signal.h:16-18 SIG_DFL/SIG_ERR/SIG_IGN cast to - `void (*)()`, same defect as the declaration beside them - sys/include/ape/features.h:55 `#define hidden` -- musl's - internal visibility macro, in a PUBLIC header - sys/include/ape/bsd.h:47 `getopt(int, char**, char*)`, - non-const, where POSIX says `(int, char *const *, const char *)` + +NO LONGER NEEDED -- four of the original eight were fixed in APExp's +own header tree later in the same session, for their own reasons, and +a re-translation does NOT have to repeat them. Verified against the +tree rather than assumed: + + signal.h `signal()` is prototyped now -- + `extern void (*signal(int, void (*)(int)))(int)` at :86 + signal.h SIG_DFL/SIG_ERR/SIG_IGN are `(void (*)(int))` at :45-47; + they had to move with the declaration, since callers + compare the return value against them + features.h `#define hidden` is GONE from the public header -- it had + a measured victim in `sqlite3.h:10957`'s + `unsigned char hidden[48]` + bsd.h:53 `getopt(int, char * const [], const char *)`, POSIX's + spelling, agreeing with `` at last + +*That is the translation's own staging list working as a to-do list +for the HEADER TREE*: it was written as eight things EDG could not +use, and half of them turned out to be defects worth fixing on their +own evidence, found by a stricter compiler than gcc. One absolute include also had to be rewritten for a host compile: amd64/include/ape/stddef_arch.h's `#include "/sys/include/ape/..."`. Four others of the form `#include "/sys/include/..."` name native Plan 9 headers and are unreachable from EDG, so they were left. -These are reported as findings in their own right; see the round-2 -report. They are not EDG's problem and they are not fixed here. +The four that remain are reported as findings in their own right and +are not fixed here; they are not EDG's problem either. diff --git a/sys/src/external/edg/kencc_targ.h b/sys/src/external/edg/kencc_targ.h new file mode 100644 index 000000000..210067d82 --- /dev/null +++ b/sys/src/external/edg/kencc_targ.h @@ -0,0 +1,181 @@ +/* + * kencc_targ.h -- APExp target configuration for EDG's cpfe. + * + * NOT part of EDG. Supplied with -include so that it is seen before + * src/defines.h pulls in the generated cmake_defines.h, every entry of + * which is #ifndef-guarded. Nothing in the EDG tree is modified. + * + * Target: APExp/kencc on amd64 (6c/pcc). This is LLP64 -- the same + * integer model EDG already ships as its "win64" configuration, whose + * values in cmake_defines.h are quoted beside each line below. + */ + +#ifndef APEXP_KENCC_TARG_H +#define APEXP_KENCC_TARG_H + +/* + * Only for translation units that take defines.h's USE_CMAKE_DEFINES + * branch -- that is cpfe, and cpfe is the only thing whose target + * matters. The helper programs in util/ are compiled WITHOUT that + * define, so they take the legacy "#include defines_linux.h" path at + * defines.h:518, where the TARG_* macros are UNCONDITIONAL and a + * command-line definition only produces a redefinition warning that + * the header then wins. They are host programs here; leave them on + * the host's own configuration. + */ +#if defined(USE_CMAKE_DEFINES) + +/* ---- generated-code target -------------------------------------- * + * The round-1 build inferred GCC_IS_GENERATED_CODE_TARGET from + * __GNUC__ (targ_def.h:3505), i.e. from the compiler that built cpfe, + * and that is the sole gate on both __attribute__((__weak__)) sites in + * c_gen_be.c (8806, 11103). kencc is "a compiler for which we have no + * special handling", which is targ_def.h's own EDG_WIN32 #else arm. + */ +#define GCC_IS_GENERATED_CODE_TARGET 0 +#define CLANG_IS_GENERATED_CODE_TARGET 0 +#define SUN_IS_GENERATED_CODE_TARGET 0 +#define MSVC_IS_GENERATED_CODE_TARGET 0 +#define MICROSOFT_DIALECT_IS_GENERATED_CODE_TARGET 0 +#define C_GEN_BE_GENERATES_ANSI_C 1 + +/* ---- no COMDAT ---------------------------------------------------- * + * Clearing GCC_IS_GENERATED_CODE_TARGET above stops __weak__ being + * EMITTED, but by itself that only turns a kencc-unsupported attribute + * into a duplicate-definition link error: measured, two trivial TUs + * sharing one header collide on 10 symbols. + * + * host_envir.h:862 names the real axis -- + * LINKER_CAN_DISCARD_DUPLICATE_DEFINITIONS, whose own comment is + * "a simplified interpretation of this flag is: do we have COMDAT + * sections?". Plan 9's loaders do not. It is forced TRUE by + * IA64_ABI (host_envir.h:868), which the Linux configuration sets, so + * IA64_ABI is what has to go: that selects EDG's Cfront-like ABI, + * which is the no-COMDAT world this whole mechanism was built for. + * With IA64_ABI off, extern inline entities are routed through the + * template instantiation machinery instead of a COMDAT section + * (targ_def.h:4641-4686) and vtables/typeinfo follow the same path. + * + * ATTEMPTED AND BACKED OUT -- see the note at the end of this block. + * It is left here, disabled, because the wall it hits is the finding + * and re-deriving it would cost another round. + */ +#if defined(APEXP_TRY_CFRONT_ABI) +#define IA64_ABI 0 +#define LINKER_CAN_DISCARD_DUPLICATE_DEFINITIONS 0 +#define INSTANTIATE_EXTERN_INLINE 1 +#define INSTANTIATE_INLINE_VARIABLES 1 + +/* + * targ_def.h:648 defaults this to GNU_EXTENSIONS_ALLOWED, and + * targ_def.h:657 only permitted it to be off when IA64_ABI was off -- + * which it now is. kencc has no weak references, so lazy + * initialization must not be built on them. + */ +#define LAZY_INITIALIZATION_USES_WEAK_REFERENCES 0 + +/* + * Two consistency checks fire on IA64_ABI going off, and each names + * its own remedy; both are Cfront-ABI settings rather than choices: + * targ_def.h:357 TIE_DEFAULT_GNU_ABI_VERSION_TO_GNU_VERSION + * requires that IA64_ABI be TRUE + * targ_def.h:4603 HANDLE_VIRTUAL_BASES_IN_COMPLETE_CTOR_DTORS must + * be FALSE when IA64_ABI is FALSE + */ +#define TIE_DEFAULT_GNU_ABI_VERSION_TO_GNU_VERSION 0 +#define HANDLE_VIRTUAL_BASES_IN_COMPLETE_CTOR_DTORS 0 + +/* + * ...and then it stops, which is the thing to record. With the two + * above supplied, cpfe fails to COMPILE in src/target_map.h: + * + * target_map.h:106 error: 'TARG_ALIGNOF_PTR_TO_VIRTUAL_BASE_CLASS_linux_i686' + * was not declared in this scope + * target_map.h:302 error: 'TARG_RUNTIME_ELEM_COUNT_INT_KIND_linux_i686' + * target_map.h:338 error: 'TARG_SIZEOF_PTR_TO_VIRTUAL_BASE_CLASS_linux_i686' + * + * The Cfront ABI needs three target quantities the IA-64 ABI does not, + * and this build is MULTI-TARGET: the platform cmakedef + * (cmake/macro-conf/support/platform/linux-x86_64/base.cmakedef:14-38) + * declares TARGET_CONFIGURATION_1..7, and target.c instantiates the + * whole target map once per configuration, each needing a + * _ spelling. The generated cmake_defines.h carries + * those suffixed variants only for the macros reachable under + * IA64_ABI=1, which is what it was generated for. Setting + * EDG_CPP_RT_LIBS="" does NOT help: that list is separate from + * TARGET_CONFIGURATION_n. + * + * Two of the three are mechanical (targ_def.h:2837,2842 derive them + * from TARG_SIZEOF_POINTER / TARG_ALIGNOF_POINTER) but the suffixed + * spellings still have to be written out by hand for all seven + * secondary targets, and TARG_RUNTIME_ELEM_COUNT_INT_KIND has no + * derived default at all -- that one would be invented. + * + * So the Cfront ABI is reachable by REGENERATING the macro + * configuration for it, not by setting switches, and it is a large + * semantic change (mangling, ctor/dtor model, vtable layout) that + * wants its own round and its own validation. Not done here. + */ +#endif /* defined(APEXP_TRY_CFRONT_ABI) */ + +/* ---- integer model: LLP64 (= EDG's win64) ----------------------- */ +#define TARG_LITTLE_ENDIAN 1 /* amd64 */ +#define TARG_CHAR_BIT 8 +#define TARG_HAS_SIGNED_CHARS 1 /* win64 1; APE's own + limits_generic.h:44,52 make + CHAR_MAX==SCHAR_MAX and + CHAR_MIN==SCHAR_MIN */ +#define TARG_SIZEOF_SHORT 2 +#define TARG_ALIGNOF_SHORT 2 +#define TARG_SIZEOF_INT 4 /* win64 4 */ +#define TARG_ALIGNOF_INT 4 +#define TARG_SIZEOF_LONG 4 /* win64 4 -- NOT 8; kencc long + is 32-bit on every target */ +#define TARG_ALIGNOF_LONG 4 /* win64 4 */ +#define TARG_SIZEOF_LONG_LONG 8 /* win64 8 */ +#define TARG_ALIGNOF_LONG_LONG 8 /* win64 8 */ +#define TARG_SIZEOF_POINTER 8 /* win64 8 */ +#define TARG_ALIGNOF_POINTER 8 /* win64 8 */ + +/* ---- floating point --------------------------------------------- * + * kencc has no extended precision: cc/sub.c's simplet() maps + * BDOUBLE|BLONG to types[TDOUBLE], so long double IS double. + * EDG's win64 says 8 for the same reason (MSVC does the same). + * targ_def.h:3954 requires DOUBLE == LONG_DOUBLE in that case. + */ +#define TARG_SIZEOF_FLOAT 4 +#define TARG_ALIGNOF_FLOAT 4 +#define TARG_SIZEOF_DOUBLE 8 /* win64 8 */ +#define TARG_ALIGNOF_DOUBLE 8 /* win64 8 */ +#define TARG_SIZEOF_LONG_DOUBLE 8 /* win64 8 */ +#define TARG_ALIGNOF_LONG_DOUBLE 8 /* win64 8 */ + +/* + * The SIZE alone is not enough: floating.h:216 cross-checks it against + * the long double FORMAT, and cmake_defines.h:166 asserts the host's + * 80-bit extended. Saying 8 bytes while the format says 80 bits is + * "long double type is mis-configured", which is the check doing its + * job. binary64 is the format that matches kencc's long-double-is- + * double, and it is one of the three floating.h already names. + */ +#define FP_LONG_DOUBLE_IS_BINARY64 1 +#define FP_LONG_DOUBLE_IS_80BIT_EXTENDED 0 +#define FP_LONG_DOUBLE_IS_BINARY128 0 + +/* ---- derived library types -------------------------------------- * + * Read from APE's own headers rather than inferred: + * amd64/include/ape/stddef_arch.h:4,10,14 + * _ptrdiff_t = long long, size_t = unsigned long long, + * ssize_t = long long + * sys/include/ape/stddef.h:50 wchar_t = unsigned int + * The first three are exactly win64's kinds; wchar_t is not + * (win64 uses unsigned short), which is the one place APExp and + * EDG's LLP64 configuration differ. + */ +#define TARG_SIZE_T_INT_KIND ((an_integer_kind)ik_unsigned_long_long) +#define TARG_SSIZE_T_INT_KIND ((an_integer_kind)ik_long_long) +#define TARG_PTRDIFF_T_INT_KIND ((an_integer_kind)ik_long_long) +#define TARG_WCHAR_T_INT_KIND ((an_integer_kind)ik_unsigned_int) + +#endif /* defined(USE_CMAKE_DEFINES) */ +#endif /* APEXP_KENCC_TARG_H */ From 596c354a63f1c8e463ebb23ce523f60954fb3eb7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 17:43:02 +0000 Subject: [PATCH 2/2] headers: make stdlib.h, unistd.h and signal.h usable from C++ The last four items on EDG's staging list, each a parameter name or a keyword, so none of them changes what a C caller sees: stdlib.h _Noreturn is a C11 keyword with no C++ spelling stdlib.h parameter named `template' unistd.h parameter named `new' signal.h `restrict' with no parameter name reads in C++ as the name itself, and two per prototype is then a duplicate THE OBVIOUS FIX FOR TWO OF THEM WAS THE ONE THAT BROKE THE BUILD ONE ROUND AGO. `#define _Noreturn' or `#define restrict' under __cplusplus puts an unreserved name in a public APE header that external packages define themselves -- gnulib ships a whole _Noreturn.h, and a dozen config.h here define `restrict'. That is `reject' again. The bare-word sweep was run first this time and it is what chose the spellings: everything used is in the reserved double-underscore space. Three of the four need no macro at all. `restrict' becomes `__restrict', which costs nothing because kencc lexes all three spellings to the same LRESTRICT (cc/lex.c:1652,1678-1679), and where a package's own cdefs.h defines __restrict (twelve do) it expands to the qualifier or to nothing, correct either way. The two parameters are renamed. `__ape_template' was not invented here -- the generated C under sys/src/external/edg already carries it, so the tree now matches its own output. Control, a before-and-after pair on identical staging: a C++ translation unit including the three headers gives 12 errors before and 1 after; the same unit as C gives 0 both ways, which is the half that could have cost the tree. apehdr-sweep 148 headers / 0 findings with the together-case at its recorded 8; apdecl-sweep 0. The survivor is a ninth item the staging list never named: stddef.h:50 typedefs `wchar_t', a built-in type in C++. Present in both runs, so pre-existing. Recorded, not fixed -- it blocks nothing in the EDG directory, which uses --no_standard_includes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 42 ++++++++++++++++++++++++++++--- sys/include/ape/signal.h | 21 +++++++++++++--- sys/include/ape/stdlib.h | 32 ++++++++++++++++++++---- sys/include/ape/unistd.h | 11 +++++++-- sys/src/external/edg/NOTE | 52 ++++++++++++++++++++++++++++++--------- 5 files changed, 132 insertions(+), 26 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f356bdd8f..f47e97b35 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1817,11 +1817,45 @@ live either way. fixed on their own evidence** later in the same session (`signal()`'s prototype, the `SIG_*` casts, `features.h`'s `hidden`, `bsd.h`'s `getopt`) and a re-translation need not repeat - them. Four remain, every one a parameter name or a keyword and so - invisible to a C caller: `stdlib.h`'s unguarded `_Noreturn`, a - parameter named `template`, `unistd.h`'s parameter named `new`, - and `signal.h`'s `restrict` read as a duplicate parameter name. + them. **The other four are fixed now too, so the staging list is + closed**: `stdlib.h`'s unguarded `_Noreturn`, its parameter named + `template`, `unistd.h`'s parameter named `new`, and `signal.h`'s + `restrict` read as a duplicate parameter name -- every one a + parameter name or a keyword, so invisible to a C caller. *EDG is a stricter compiler than gcc, and that is what it bought.* + - **AND THE OBVIOUS FIX FOR TWO OF THEM WAS THE ONE THAT HAD JUST + BROKEN THE BUILD.** `#define _Noreturn` or `#define restrict` + under `__cplusplus` would put an UNRESERVED name in a public APE + header that external packages define themselves -- gnulib ships a + whole `_Noreturn.h`, and a dozen `config.h` here define + `restrict` -- which is `reject` again, one round later. **The + bare-word sweep was run FIRST this time**, and it is what chose + the spellings: everything used is in the reserved + double-underscore space, which nothing outside an implementation + may define. + **Three of the four need no macro at all.** `restrict` becomes + `__restrict`, which costs nothing because *kencc lexes all three + spellings to the same `LRESTRICT`* (`cc/lex.c:1652,1678-1679`) -- + and where a package's own `cdefs.h` defines `__restrict` (twelve + do) it expands to the qualifier or to nothing, correct either way. + The two parameters are renamed, which no caller can see. + `__ape_template` was not invented here: **EDG's generated C + already carries it**, so the staged edit had used this convention + and the tree now matches its own output. + - **The control is a before-and-after pair on IDENTICAL staging, and + it found a FIFTH item.** A C++ translation unit including + ``, `` and `` gives **12 errors + before and 1 after** -- and the survivor is `stddef.h:50` + typedef'ing **`wchar_t`, which is a built-in type in C++**, + present in BOTH runs and named nowhere in the NOTE's list of + eight. *Recorded, not fixed.* The half that could have cost the + tree is the other one: the same unit compiled as **C is 0 + errors**. `apehdr-sweep` 148 headers / 0 findings with the + together-case holding at its recorded 8; `apdecl-sweep` 0. + *The first staging recipe differed slightly between the two runs + and the after-count was re-taken with the before's exact + commands* -- two compiles differing in anything but the change + are not a control, which this file records from the `-J` round. - **muon** -- in `_OPTIONAL_APPS` commented out. Never built here. - **go** -- `go1.4` is in the tree, commented out of `_CORE_APPS`, and is the only thing that mentions `Ureg` outside libap. diff --git a/sys/include/ape/signal.h b/sys/include/ape/signal.h index 8f7fd0d97..7d32471e0 100644 --- a/sys/include/ape/signal.h +++ b/sys/include/ape/signal.h @@ -158,10 +158,25 @@ extern int sigaction(int, const struct sigaction *, struct sigaction *); extern int sigprocmask(int, sigset_t *, sigset_t *); extern int sigpending(sigset_t *); extern int sigsuspend(const sigset_t *); -extern int sigwait(const sigset_t *restrict, int *restrict); -extern int sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict); +/* + * `__restrict' RATHER THAN `restrict', AND IT COSTS NOTHING: kencc + * lexes all three spellings to the same LRESTRICT (`cc/lex.c:1652, + * 1678-1679'). `restrict' is a C keyword and NOT a C++ one, so in C++ + * these read as PARAMETER NAMES -- and two per prototype is then a + * duplicate parameter name, which is why a C++ compiler could not + * read these three lines at all. + * + * `#define restrict' away for C++ would be the wrong fix twice over: + * it is an unreserved name, and gnulib's `config.h' already defines + * it in a dozen packages here. `__restrict' is reserved, so nothing + * outside an implementation may define it -- and where a package's + * own `cdefs.h' does (twelve do), it expands to the qualifier or to + * nothing, which is correct either way. + */ +extern int sigwait(const sigset_t *__restrict, int *__restrict); +extern int sigwaitinfo(const sigset_t *__restrict, siginfo_t *__restrict); struct timespec; /* avoid pulling in time.h */ -extern int sigtimedwait(const sigset_t *restrict, siginfo_t *restrict, const struct timespec *restrict); +extern int sigtimedwait(const sigset_t *__restrict, siginfo_t *__restrict, const struct timespec *__restrict); extern int sigqueue(pid_t, int, const union sigval); /* diff --git a/sys/include/ape/stdlib.h b/sys/include/ape/stdlib.h index 5b93f2d92..589b25f66 100644 --- a/sys/include/ape/stdlib.h +++ b/sys/include/ape/stdlib.h @@ -12,6 +12,28 @@ typedef struct { int quot, rem; } div_t; typedef struct { long quot, rem; } ldiv_t; +/* + * `_Noreturn' IS A C11 KEYWORD AND C++ HAS NO SUCH SPELLING, so the + * three declarations using it below could not be read by a C++ + * compiler at all. Found by EDG, which is stricter than gcc: its own + * sources had to be translated against a STAGED copy of this header, + * and the staging list is in `sys/src/external/edg/NOTE'. + * + * **The obvious fix is the one this tree has just been burned by.** + * `#define _Noreturn' under __cplusplus would be a public APE header + * defining a name ten external packages define themselves -- gnulib + * ships a whole `_Noreturn.h' -- which is the `reject' collision + * waiting for the next build. The spelling used instead is in the + * RESERVED double-underscore space, which nothing outside an + * implementation may define, and `__ape_template' below is the same + * convention; EDG's generated C already carries that one. + */ +#if defined(__cplusplus) +#define __ape_Noreturn +#else +#define __ape_Noreturn _Noreturn +#endif + #ifdef __cplusplus extern "C" { #endif @@ -44,9 +66,9 @@ extern void *calloc(size_t, size_t); extern void free(void *); extern void *malloc(size_t); extern void *realloc(void *, size_t); -extern _Noreturn void abort(void); +extern __ape_Noreturn void abort(void); extern int atexit(void (*func)(void)); -extern _Noreturn void exit(int); +extern __ape_Noreturn void exit(int); extern char *getenv(const char *); extern int putenv(char *); extern int system(const char *); @@ -66,8 +88,8 @@ extern size_t wcstombs(char *, const wchar_t *, size_t); #include extern char *mktemp(char *); -extern int mkstemp(char *template); -extern int mkostemp(char *template, int); +extern int mkstemp(char *__ape_template); +extern int mkostemp(char *__ape_template, int); /* from musl */ typedef struct { long long quot, rem; } lldiv_t; @@ -84,7 +106,7 @@ extern long long llabs(long long); extern lldiv_t lldiv(long long, long long); extern int at_quick_exit(void (*)(void)); -extern _Noreturn void quick_exit(int); +extern __ape_Noreturn void quick_exit(int); /* musl and other ports */ diff --git a/sys/include/ape/unistd.h b/sys/include/ape/unistd.h index 073e196ff..a0d53c101 100644 --- a/sys/include/ape/unistd.h +++ b/sys/include/ape/unistd.h @@ -183,13 +183,20 @@ int openat(int dirfd, const char *path, int flags, ...); /* unistd/at_functions. int fstatat(int dirfd, const char *path, struct stat *buf, int flags); int unlinkat(int dirfd, const char *path, int flags); int mkdirat(int dirfd, const char *path, mode_t mode); -int renameat(int olddirfd, const char *old, int newdirfd, const char *new); +/* + * `__ape_new' because `new' is a C++ KEYWORD, so these two lines + * could not be read from C++ at all. A parameter name in a prototype + * is documentation -- no caller can see it -- so this changes nothing + * for C. Same convention as `'s `__ape_template'; the + * reason both are in the reserved space is recorded there. + */ +int renameat(int olddirfd, const char *old, int newdirfd, const char *__ape_new); ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t n); int symlinkat(const char *target, int dirfd, const char *linkpath); int faccessat(int dirfd, const char *path, int mode, int flags); int fchownat(int dirfd, const char *path, uid_t uid, gid_t gid, int flags); int fchmodat(int dirfd, const char *path, mode_t mode, int flags); -int linkat(int old_dfd, const char *old, int new_dfd, const char *new, int flags); +int linkat(int old_dfd, const char *old, int new_dfd, const char *__ape_new, int flags); /* GNU extensions */ extern int pipe2(int [2], int); diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index 8832cad04..efa8f0771 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -274,13 +274,18 @@ it makes the output DEPEND on a prelink step. APExp HEADER CHANGES THIS TRANSLATION DEPENDS ON ------------------------------------------------ -The headers were STAGED (copied) and edited in the staging copy only. -NOTHING IN APExp's HEADER TREE WAS MODIFIED. To reproduce this -directory those edits have to be repeated, and they are all cases of -an APE header not being usable from C++: +At translation time the headers were STAGED (copied) and edited in the +staging copy only, and NOTHING IN APExp's HEADER TREE WAS MODIFIED. +Eight edits were needed, every one a case of an APE header not being +usable from C++. -STILL NEEDED -- four, and all four are a parameter name or a keyword, -so none of them changes what a C caller sees: +**ALL EIGHT ARE FIXED IN THE TREE NOW, so a re-translation needs NONE +of them** -- stage the headers as they stand. The list is kept +because it is the record of what a stricter compiler found, and +because each was fixed on its own evidence rather than to suit EDG. + +The last four, each a parameter name or a keyword, so none of them +changes what a C caller sees: sys/include/ape/stdlib.h:47,49,87 `_Noreturn` (a C11 keyword that does not exist in C++) used unguarded @@ -289,10 +294,8 @@ so none of them changes what a C caller sees: sys/include/ape/signal.h:161-164 `restrict` as a parameter name; two per prototype reads as a duplicate parameter name in C++ -NO LONGER NEEDED -- four of the original eight were fixed in APExp's -own header tree later in the same session, for their own reasons, and -a re-translation does NOT have to repeat them. Verified against the -tree rather than assumed: +And the first four, fixed earlier in the same session for their own +reasons. Verified against the tree rather than assumed: signal.h `signal()` is prototyped now -- `extern void (*signal(int, void (*)(int)))(int)` at :86 @@ -307,8 +310,33 @@ tree rather than assumed: *That is the translation's own staging list working as a to-do list for the HEADER TREE*: it was written as eight things EDG could not -use, and half of them turned out to be defects worth fixing on their -own evidence, found by a stricter compiler than gcc. +use, and every one turned out to be a defect worth fixing on its own +evidence, found by a compiler stricter than gcc. + +The control is a before-and-after pair on IDENTICAL staging: a C++ +translation unit including , and +gives 12 errors before and 1 after. The same unit as C is 0 errors, +which is the half that could have cost the tree. + +THE SURVIVOR IS A NINTH ITEM THIS LIST NEVER NAMED: stddef.h:50 +typedefs `wchar_t', which is a BUILT-IN TYPE in C++. It is present +in both runs, so it is pre-existing rather than anything done here, +and it is recorded and not fixed. cpfe does not trip on it -- +`--no_standard_includes' plus EDG's own include_c99 is why -- so it +blocks nothing in this directory. + +Two of the four could not be fixed the obvious way. `#define +_Noreturn' or `#define restrict' under __cplusplus would put an +UNRESERVED name in a public APE header that external packages define +themselves: gnulib ships a whole `_Noreturn.h' and a dozen `config.h' +here define `restrict'. That is exactly the collision that broke +flex over `reject' one round earlier, so the spellings used are all +in the reserved double-underscore space. Three of the four need no +macro at all -- `restrict' becomes `__restrict', which kencc lexes to +the same LRESTRICT (cc/lex.c:1652,1678-1679), and the two parameters +are simply renamed. `__ape_template' is this directory's own +convention arriving back in the tree: the generated C already carries +it. One absolute include also had to be rewritten for a host compile: amd64/include/ape/stddef_arch.h's `#include "/sys/include/ape/..."`.