# Step: UString concat/LEN + IOChan position/seek Tag `v3-polish-ustr-iochan`. Suite **137/137**; fixpoint **OK** (image **2,156,095 bytes**). Two of the three remaining polish items; Clarion `CLASS` lowering is scoped separately (`docs/summary_class-lowering.md`). ## UString concatenation and length - `U"a" + U"b"` concatenates descriptors: `QbeGen.UStrCat` → shim `m2ustrcat` (static buffer; consume before the next concat). - A **single-codepoint** operand (`U"x"` is a `UCHAR`) becomes a 1-codepoint UString via `QbeGen.UStrFrom` → `m2ustrfrom`. The shim uses a small rotating pool so an expression can supply both operands before `m2ustrcat` reads them. - `LEN`/`LENGTH`/`HIGH` of a `U"…"` literal read the descriptor header: `QbeGen.UStrLen` → `m2ustrlen` (and `DecQ` for `HIGH`). A single-codepoint literal is `LEN = 1`, `HIGH = 0`. - `+` handles `UString+UString`, `UString+UCHAR`, `UCHAR+UString` and `UCHAR+UCHAR`; the result type is `ClUStr`. Note: `LEN(expr)` where the operand is a computed expression (e.g. `LEN(U"a" + U"b")`) is not accepted — `LEN`/`HIGH` take a designator or a literal, as before. ## IOChan positioning - `Position(cid):INTEGER`, `Seek(cid, pos):BOOLEAN`, `Rewind(cid):BOOLEAN`. - Over `SysShim.ftell`/`fseek` → shim `m2ftell`/`m2fseek` (`fseek(..., SEEK_SET)` + `clearerr` on rewind). ## Tests - `t_ustrcat.mod` (63): `LEN`/`LENGTH`/`HIGH` of literals, `U"ab"+U"cd"`, `U"x"+U"é"`. - `iochanseek_prog.mod` (15): write 6 bytes, `Position`, reopen, `Position` after reads, `Rewind`, `Seek(3)`, read line back. ## Files `compiler/src/{QbeGen.def,QbeGen.mod}` (`UStrCat`/`UStrFrom`/ `UStrLen`/`DecQ`), `compiler/src/{SymTab.def,SymTab.mod}` (`UStrCompat`), `compiler/src/M2.atg` (UString `+` and `LEN`), `runtime/syslib/shim.c` (`m2ustrcat`/`m2ustrfrom`/`m2ustrlen`/ `m2ftell`/`m2fseek`), `runtime/syslib/SysShim.def`/`.mod`, `stdlib/iochan.def`/`.mod`, `compiler/tests/{t_ustrcat,iochanseek_prog}.mod`, `compiler/run_tests.sh`, `docs/features.md`.