Record that a write through a string literal fails two ways
At -O0 it stores into read-only memory and takes SIGSEGV; at -O2 LLVM deletes it as undefined and the program prints the unmodified string and exits 0. Same source, and which way it fails depends on a flag, which is worse than either outcome on its own. Nothing refuses it and nothing cheaply can: bytes turns a string into a [u8], the language lets you write through a slice, and by then nothing records where the bytes came from. That is provenance, which plan.org defers as open decision #3. Emitting literals as mutable globals is not a fix - it moves which flag misbehaves and costs their read-only placement. Written down rather than half-fixed, with the rule the string lane already follows: a function over a string must not write through it.
This commit is contained in:
parent
ee5abd40fc
commit
0cac22a5ef
21
NEXT.md
21
NEXT.md
@ -446,6 +446,27 @@ so wasm32 needs no exception proposal.
|
||||
|
||||
## Sharp edges
|
||||
|
||||
- **Writing through a string literal is undefined, and the two build modes
|
||||
disagree about how.** `(let [s (bytes "Hi")] (set (at s 0) \h))` stores into
|
||||
a `private unnamed_addr constant`. At `-O0` that is a store to read-only
|
||||
memory and the program takes SIGSEGV; at `-O2` LLVM deletes it as undefined
|
||||
and the program prints `Hi` and exits 0. Same source, and which way it fails
|
||||
depends on a flag — the worst shape available, and worse than either outcome
|
||||
alone.
|
||||
|
||||
Nothing refuses it. `bytes` turns a `string` into a `[u8]`, the language lets
|
||||
you write through a slice, and by then nothing records that the bytes came
|
||||
from a constant. The honest fix is provenance — knowing a slice's origin —
|
||||
which is plan.org open decision #3 and deliberately deferred. A cheaper one
|
||||
that is *not* a fix: emitting literals as mutable globals only moves which
|
||||
flag misbehaves, and costs their read-only placement.
|
||||
|
||||
Found by the string lane while deciding whether `lower-ascii` should mutate
|
||||
in place. It ships the copying version for exactly this reason, and that is
|
||||
the rule to follow until provenance exists: **a function over a `string` must
|
||||
not write through it.**
|
||||
|
||||
|
||||
Most of these are edges the language keeps and you should know about. Two — the top-level namespace and the shift count,
|
||||
both found by review after milestone 4 — were bugs that reached LLVM or ran wrong, and are **fixed**; each says so. They
|
||||
stay written down because each one is now a rule the checker enforces, and a later change could quietly drop it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user