diff --git a/lib/prelude.ml b/lib/prelude.ml index 1e4e72a..988914f 100644 --- a/lib/prelude.ml +++ b/lib/prelude.ml @@ -637,11 +637,22 @@ let source = {flan| ;; to_upper both allocate a new string (core/strings/conversion.odin), which ;; is not available here; the obvious substitute — lowering a [u8] in place — ;; is a trap, and it is worth saying why rather than shipping it. A string -;; literal is emitted `private unnamed_addr constant` (emit.ml), so it lives -;; in read-only memory, and (bytes "Hello") is a [u8] pointing straight at it. -;; An in-place lower-ascii! would type check against that slice and segfault -;; on the store. Given a byte function, a caller that really does own its -;; buffer writes the two-line loop itself and can see what it is writing to. +;; literal is emitted `private unnamed_addr constant` (emit.ml), so (bytes +;; "Hello") is a [u8] pointing straight into read-only memory. An in-place +;; lower-ascii! type checks against that slice, and what happens next depends +;; on the optimiser — which is the worst of the available answers. Measured, +;; with (set (at (bytes "Hi") 0) \h): +;; +;; -O0 the store is emitted against the constant and the program takes +;; SIGSEGV. +;; -O2 LLVM deletes the store as undefined behaviour and the program +;; carries on and prints "Hi". +;; +;; So the same source either dies or silently does nothing depending on a +;; flag, and the -O2 half is the quiet-wrongness class this file keeps +;; refusing elsewhere. Given a byte function instead, a caller that really +;; does own its buffer writes the two-line loop itself over storage it can +;; see the declaration of. ;; ;; ASCII only, and only the 26 letters: case outside ASCII is not a byte ;; operation at all — it is per-code-point, it is not length-preserving (ß diff --git a/test/programs/utf8.flan b/test/programs/utf8.flan index db5b269..a45d2d2 100644 --- a/test/programs/utf8.flan +++ b/test/programs/utf8.flan @@ -190,6 +190,19 @@ (show-opt (encode-rune! (slice scratch 0 1) 0x41)) ; 1, exactly enough (newline) + ;; "Nothing is written when it refuses" is a claim about the buffer, not + ;; about the return value, and the None cases above do not test it: an + ;; encoder that lays down the lead byte and only then notices the buffer is + ;; short returns None exactly as this one does, and every line above still + ;; passes. So put a known byte in scratch, ask for an encoding that must be + ;; refused, and read the byte back. + (show-i32 (round-trip 0x41)) ; 65, scratch[0] = A + (show-opt (encode-rune! (slice scratch 0 2) 0x65e5)) ; -1, needs 3 bytes + (show-i32 (i32 (at scratch 0))) ; 65 still + (show-opt (encode-rune! (slice scratch 0 4) 0xd800)) ; -1, surrogate + (show-i32 (i32 (at scratch 0))) ; 65 still + (newline) + ;; Splitting. n separators give n+1 fields, always: an interior empty field ;; survives, a leading and a trailing one do too, and an input with no ;; separator at all is one field rather than none. The empty input is the diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 63e5ea8..e47dc10 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -913,6 +913,7 @@ ERR@7 unexpected token: not the kind the caller was reading -1 1 1 2 2 3 3 -1 -1 3 3 4 4 -1 \n\ 0 65 127 128 2047 2048 65535 65536 1114111 \n\ -1 -1 -1 -1 -1 1 \n\ + 65 -1 65 -1 65 \n\ [a][b][c] [a][][b] [abc] [] [][] [][a] [a][] \n\ 60\n\ 97 122 97 64 91 65 90 65 96 123 53 \n\