Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 102 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1754,13 +1760,102 @@ 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. **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
`<stdlib.h>`, `<unistd.h>` and `<signal.h>` 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.
Expand Down
21 changes: 18 additions & 3 deletions sys/include/ape/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
Expand Down
32 changes: 27 additions & 5 deletions sys/include/ape/stdlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 *);
Expand All @@ -66,8 +88,8 @@ extern size_t wcstombs(char *, const wchar_t *, size_t);
#include <bsd.h>

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;
Expand All @@ -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 */

Expand Down
11 changes: 9 additions & 2 deletions sys/include/ape/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<stdlib.h>'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);
Expand Down
81 changes: 64 additions & 17 deletions sys/src/external/edg/NOTE
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ Converted
cmake --preset linux-gcc-release -B build/kencc \
-DCMAKE_CXX_FLAGS="-include <path>/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.
Expand Down Expand Up @@ -270,31 +274,74 @@ 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++.

**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
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 *)`

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
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 `<getopt.h>` 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 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 <stdlib.h>, <unistd.h> and <signal.h>
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/..."`.
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.
Loading
Loading