{"name":"core/marker.nu","source":"// stdlib/core/marker.nu — Send / Sync, the thread-safety marker traits.\n//\n// Two questions a thread boundary asks about a type, and nothing else:\n//\n//   Send — may a value of this type MOVE to another thread?\n//   Sync — may two threads reach ONE value of this type at once?\n//\n// Both are marker traits: no methods, no runtime representation, no\n// dispatch. A `% Send T { }` block is not code — it is an assertion,\n// NURL's spelling of Rust's `unsafe impl Send for T`.\n//\n// You rarely write either. The compiler DERIVES both structurally over\n// a type's whole graph — struct fields, enum payloads, generic\n// arguments, aggregate members, closure captures — and checks them\n// where a value actually crosses a thread boundary:\n//\n//   ( thread_spawn f )   every value `f` captures must be Send\n//   ( spawn f )          same (fibers migrate across M:N workers)\n//   ( chan_send ch v )   `v` must be Send\n//   ( Arc T )            T must be Send AND Sync — an Arc exists to be\n//                        shared, so its payload faces the harder question\n//\n// The derivation has three levels, worst-wins across the graph:\n//\n//   Send + Sync    the default — scalars, strings, handles, structs of them\n//   Send, !Sync    `Cell` — a raw byte buffer with unsynchronised writes\n//   !Send, !Sync   `Rc`   — a non-atomic refcount; two threads touching\n//                           the count is a data race on the count itself\n//\n// Those two leaves are language-level facts and live in the compiler.\n// Everything else follows from them by construction: `( Vec ( Rc i ) )`,\n// `( Arc ( Rc i ) )`, `( Box ( Rc i ) )`, a struct with an `( Rc i )`\n// field, an enum variant carrying one, an option holding one, and a\n// closure capturing any of those are all !Send for the same one reason,\n// and the diagnostic names that reason rather than the spelling.\n//\n// ── When you DO write one ──────────────────────────────────────────\n//\n// The derivation is structural, so it is wrong in exactly two ways,\n// and there is one marker for each.\n//\n// **The compiler is too pessimistic.** A type whose innards look unsafe\n// but which is safe by construction — a lock, a channel, an atomic\n// handle. `Mutex` is literally `{ Cell c }`, and a bare `Cell` is !Sync,\n// yet a Mutex is the thing that MAKES its contents shareable. Assert it:\n//\n//     % Sync Mutex { }        // and `% Send Mutex { }`\n//\n// An explicit impl STOPS the walk at that type: nothing inside is\n// examined, because you have taken responsibility for it.\n//\n// **The compiler is too optimistic.** A type that derives clean but\n// wraps thread-hostile state the compiler cannot see — an FFI handle.\n// A `sqlite3*` is an `s` and a `FILE*` is an `s`, and `s` is Send;\n// neither connection is. Say so:\n//\n//     : Db { s handle }\n//     % NotSend Db { }        // never crosses a thread boundary\n//\n// `NotSend` / `NotSync` propagate exactly like the built-in leaves: a\n// struct holding a `Db`, a `( Vec Db )`, a closure capturing one are\n// all rejected at the boundary, naming `Db`.\n//\n// A negative marker always wins over a positive one on the same type —\n// if both are written, the type is rejected. That is the safe direction\n// for a contradiction to resolve in.\n//\n// ── As a bound ─────────────────────────────────────────────────────\n//\n// Because they are ordinary traits, they work as ordinary bounds, and\n// the bound is satisfied by the DERIVATION — not by hunting for an\n// impl. `[T: Send]` accepts `i`, `s`, `( Vec i )` and rejects\n// `( Rc i )`:\n//\n//     @ run_on_worker [T: Send] ( Channel T ) ch T v → b {\n//         ^ ( chan_send [T] ch v )\n//     }\n//\n// ── What this does NOT prove ───────────────────────────────────────\n//\n// Send/Sync answer \"may this value cross?\", never \"is this program\n// race-free\". Two threads mutating one `( Vec i )` they both hold a\n// handle to is a race that no marker forbids — `Vec` is Send and Sync,\n// and correctly so, because sharing it read-only is fine. That race is\n// caught separately, at the mutation, by the shared-mutation check\n// (`docs/MEMORY.md` §6.5). The two checks are complementary and\n// neither subsumes the other.\n//\n// Like every other check in this compiler the derivation can only MISS\n// (an unmarked FFI handle, a type graph deeper than the walk's cap),\n// never invent: it is a sound lint, not a proof (`docs/MEMORY.md` §6.3).\n\n// A value of `T` may be moved to another thread.\npub %Send [T] {}\n\n// Two threads may reach one value of `T` at the same time.\npub %Sync [T] {}\n\n// `T` must never cross a thread boundary, whatever its fields suggest.\npub %NotSend [T] {}\n\n// Two threads must never reach one `T` at the same time.\npub %NotSync [T] {}\n","bytes":5009}