Changelog
Every notable change to SafeC — the compiler, the standard library, safeguard and the install scripts — as recorded in CHANGELOG.md, newest first. Changes that are merged but not yet released are under Unreleased; each release also has its own release-notes post.
Unreleased
1.0.0-beta-7 - 2026-10-27
Fixed
-
Compile time: the memory-flow check grew exponentially with a run of
insert()calls on oneBST<K, struct S, C>(7 calls took 46 s,tests/stdlib/tests/test_collections.sc23.6 s; now 0.2 s). Applying a callee summary nested a node inside a node'svalonce per call and multiplied the caller cells per dereference; cell paths are now bounded and aliased cells merged.compiler/testscan bound a test's compile time (// MAX-SECONDS:). -
A second
#includeof a C header imported through clang (<math.h>twice, or once more from another header) is a no-op instead of pasting the raw/usr/includeheader (16 errors). -
Diagnostics from inside an
#included file carry that file's name (stored diagnostics, e.g. the language server's, pointed at a freed string). -
safeguardlinks libm on Linux and the BSDs: a program using the C library's<math.h>(sqrt,cbrt, ...) withoutstd/math.hfailed to link. -
A use of a dropped or moved value through a method call (
b.len()) is reported at the variable, not at the(; function references print asfn R(Args)/?fn R(Args)in messages (and doc JSON, and scc) instead of&static const R(Args). -
__VA_ARGS__expands to every variadic argument (F(1, 2, 3)with#define F(...) g(__VA_ARGS__)gaveg(1)). -
#line N ["file"]sets__LINE__,__FILE__and the locations of the code after it (it was ignored; compiler and selfhost). -
atomic_cas/atomic_compare_exchange_strongwith"release"or"acq_rel"generate valid IR: the failure ordering is the strongest one allowed (relaxed / acquire), as in C11. -
must_use/warn_unused_resulton one declaration of a function applies to it when another declaration (the definition) doesn't repeat it. -
safec --emit-binno longer prints clang's "overriding the module target triple" warning: host builds name their triple in the IR. -
safeguard --helplists every option (doc --home,--source-ref,--verbose,publish --no-verify,--dry-run,--remote, ...); the smoke test checks it against the parser.safeguard new's template callsprintfwithout an unneededunsafe { }, and the install scripts' help no longer names a stale release tag. -
Compiler build warnings (unused variables, missing field initializers, member initialization order).
-
Warnings on incremental-cache hits: a cache hit skips lexing, parsing, sema and codegen, so a rebuild of an unchanged file printed none of their warnings (e.g. a non-exhaustive
switch), andsafeguard buildshowed them only on the first build. safec now stores the diagnostics a compile printed after preprocessing in a.bc.diagfile beside the cached bitcode and prints them again on a hit. A cache entry without one (from an older safec) is a miss, and--clear-cacheremoves both files. The smoke test compiles a file twice into a fresh cache and compares the warnings. -
A local declaration whose type is a typedef name followed by two or more
*(Foo_t** x = pp;,Foo_t*** w;) parses as a declaration instead of failing with "undeclared identifier"; nested prefix operators in expressions (**pp,a * **pp,-*p,- -x) parse too (compiler and selfhost). -
Containers of
Dropelements:HashMap<K, String>,StrMap<String>,List<String>,Stack<String>,Queue<String>,BST<K, String, C>,BTree<String>,StaticVec<String>andStaticMap<String>failed the ownership check inside std (the value was not dropped on the OOM / full path, or was moved out of a node outsideunsafe). Every container now owns its elements the wayVecdoes: overwriting a key drops the old value, a value that can't be stored is dropped,remove()/pop()move it out,List::removedrops the element it unlinks, andclear()/drop()drop every element still held (before,StrMap::clear()/drop()and the others released the storage but leaked the values).BTree,StaticVecandStaticMaplive in place / over the caller's storage and have nodrop():clear()them. Don't free a container's values yourself before dropping it (m.foreach(free_value); m.drop()): drop the container alone. -
std::Reactoris aDroptype: it had nodrop(), andclose_()left thesigfds/watchedtables allocated (and, on Windows, calledWSACleanupeven ifinit()had failed).Reactor::drop()closes it;close_()now also frees both tables, is idempotent (kq = -1afterwards) and leaves a reactorinit()can reopen. The HTTP server's reactor threads drop their reactor (on theinit()failure path too). -
WsConnnever touches a descriptor it doesn't own: a zero-initialisedWsConn(e.g. after a failedaccept()) sent a close frame to fd 0 and closed it when dropped. NewWsConn::closed()(fd = -1) is the starting value foraccept()and std's own placeholders;accept()leaves the connection closed when it fails;drop()/close()and the send functions do nothing on a closed connection (fd < 0). -
safeguard --helplists thearm-81mtarget alias (Armv8.1-M, Cortex-M55/M85) with the others; the smoke test now checks the help againstsafec --list-targets. -
#include <math.h>is the C library's header in a safeguard build: safeguard put std/ itself on SafeC's include path (-I <std>), so a bare<math.h>(or<time.h>,<stdint.h>,<ctype.h>, ...) found std's header instead andsqrt& co. were undeclared. std is now reached only as<std/...>(its parent directory is on the path) — in builds, tests,check,lint,docand dependency builds, and for std's own units. Breaking for code that included std headers without the prefix (#include <io.h>,"prelude.h"): write<std/io.h>. Thesafeguard newtemplate says<std/prelude.h>; the smoke test builds a project using both<math.h>and<std/math.h>. -
std/time links in the playground: scc's runtime had no
gmtime_r, so a program using std/time failed with "undefined function". The runtime now providesgmtime_r,localtime_r(UTC: the playground has no time zone),mktime,strftime,strptime,clock_gettime(the epoch: no clock) andnanosleep; a difftest corpus case checks them against glibc.tools/genvfs.pynow embeds each bundled file in chunks of at most 32 KiB: scc decodes a string literal into a 64 KiB buffer, so the grown runtime came out truncated in the self-compiled scc (bootstrap stage 2).
Changed — no void* anywhere in std
- The raw memory layer is bytes:
alloc_raw_,dealloc_raw_,realloc_buf_raw_,safe_memcpy_raw_and the otherstd/mem.h_raw_forms take and returnunsigned char*(symbol names and ABI unchanged:new Tstill callsstd_alloc_raw_); the genericalloc/dealloc/safe_memcpywrappers cast insideunsafe. - Breaking: the allocator traits (
Allocate/Deallocate) and every std allocator (BumpAllocator,PoolAllocator,SlabAllocator,TlsfAllocator,SecureAllocator,CountingAllocator) hand out?&heap unsigned charinstead of?&heap void, andheap_alloc/heap_free/heap_reallocuse&heap unsigned char. A custom allocator changes itsalloc/deallocsignatures the same way;alloc_t/alloc_array_t/dealloc_tare unchanged. T x = malloc_defer(n);now expands tostd::alloc(n)plus a deferredstd::dealloc(x), takingTfrom the declaration (autoandvoid*keepalloc_raw_/dealloc_raw_, soautois nowunsigned char*).- libc / OS / C-library declarations use typed handles: C's
FILEis the opaqueCFile_; pthread and Win32 synchronisation objects,HANDLEs,DIR, find handles,timespec,struct tm,sigset_t,pollfd,epoll_event,kevent,fd_set,addrinfoandWSADATAare passed as their (opaque or mirrored) structs; thread start routines,signal()handlers and comparators arefntypes. The TLS backends declare OpenSSL's dlopen'd API (SSL,SSL_CTX,BIO,X509, ...,dlsymreturning a function), CoreFoundation/Security and SChannel/crypt32 with opaque handle structs and onefntype per signature. The channel runtime (__safec_chan_*) takes aSafecChan_*and its 8-byte slot, and__safec_thread_createthe taskfnand its?&Erasedargument. tests/std_modules/api_lint.pyfails on anyvoid*/&void(any region) in std's headers and sources, FFI externs included, with no exemptions (std/ml, std/gui and the CUDA/ROCm FFTs included, below).
Changed — no void* left in std/gui
- std/gui has no
void*at all, internal FFI included (public API unchanged): the X11, Wayland, Win32 and Cocoa backends and the EGL/GLES, D3D11 and Metal presenters declare their system APIs with opaque handle structs (XDisplay_,WlDisplay_/WlProxy_,HWND_,ObjcId_/ObjcSel_,EGLDisplay_, ...), typedstruct wl_interfacetables, Wayland dispatchers taking?&Erased, COM objects through a typed method table, one typedobjc_msgSendcast per call shape, typed dlopen()/dlsym() lookups and byte pointers for pixel and property data; the portable core usessafe_memset_obj/safe_memcpyand byte-pointer identity compares.
Changed — no void* left in std/ml
- std/ml (and std/dsp's
fft_cuda/fft_rocm) has novoid*at all: a tensor'sgradFnis afn void(&DenseTensor<float>)taking the node through its float32 layout (shared by every element type), the coherence internals take thatFloatTensor*, and the GPU bindings (CUDA, NVRTC, cuBLAS(Lt), cuDNN, cuFFT, HIP, hiprtc, rocBLAS, MIOpen, hipFFT, Metal through the Objective-C runtime, Vulkan, WebGPU) declare each handle kind as its own opaque struct (CuEvent_,CudnnTensorDesc_,HipModule_,ObjcSel_,VkDevice_,WGPUBuffer_, ...; the shared declarations in the internalcuda_ffi.h/rocm_ffi.h/mps_ffi.h), device memory asCuDevPtr_/HipDevPtr_, kernel arguments asCuKernelArg_/HipKernelArg_, pNext / nextInChain chains asVkNext_/WGPUChainedStruct, and host buffers asunsigned char*; cuDNN's SDPA entry points are looked up as typed functions.
Changed — no void* left in std/audio; fewer in std's internals
- std/audio has no
void*at all, internal headers and FFI included: the audio/MIDI backend tables are?&static AudioBackendVt_/MidiBackendVt_, each backend's state is an opaque typed handle (?&heap AudioStreamImpl_/MidiPortImpl_), the stream buffers are[]unsigned charslices, the stream thread's body and the MIDI callback slot are?fn, dlopen()/dlsym() return?&AudioLib_/ a function, and libasound, libjack, Core Audio, Core MIDI, WASAPI (COM) and WinMM are declared with opaque handle structs, typed callbacks taking?&Erasedand byte pointers for their buffers. - Breaking:
Audio::open_stream<T>andMidiIn::set_callback<T>requireT: Send + Sync: the callback runs on the audio/MIDI thread while the context stays reachable from its owner's (plain data and atomics pass; raw pointers, stack references and!Sendhandles are compile errors). Reactor::poll_events_raw_,parallel_for_raw_'s job,sys_qsort/sys_bsearch/sys_atexit, the HTTP server's handler context and the fs drivers use typedfn/?&Erasedinstead ofvoid*;dealloc_raw_((void*)p)is the typeddealloc(p)throughout std.
Changed — std's public API has no void* and no pointer + length pairs
std's public declarations no longer take or return void*, raw T*
buffers, (pointer, length) pairs or arrays through pointers.
- Buffers are slices. SafeC has no
&T[]type; the equivalent is the built-in slice:[]Tfor a buffer the function writes,[]const Tfor one it only reads. A slice carries its length, indexing it is bounds-checked, and a fixed-size argument (a key, a nonce, a digest, a tag) is checked on entry (a short one panics instead of being read or written past).std::str_bytes("text")(std/str.h) gives a string's bytes as a[]const char, accepted wherever[]const unsigned charis expected. - Type erasure is typed. Where std must store callbacks with different
context types side by side (executors, wakers, timers, widgets, the VFS,
audio/MIDI callbacks, HTTP handlers, DOM listeners, thread and task entry
points), the slot is a
?&Erased(std/erased.h) with a typedfn, filled only by generic methods that take a matchingfn(?&T)/?&T. Opaque handles are typed too (?&MtlObject,?&CudaNativeStream,?&struct GuiWindowState, ...). Thevoid*halves that remain are private (*_raw_,*_n_, std-internal headers) or FFI externs. tests/std_modules/api_lint.py, run by the smoke test, fails when a raw pointer appears in a public std declaration.
Added — read-only, optional and checked slices; typed erasure
- Compiler:
[]const Tread-only slices ([]Tconverts to it implicitly; writing through one is an error),?[]Toptional slices, slice-returning functions and slice-typed top-level declarations,?fn R(Args)nullable function references, range-checked re-slicing (a[i..j]traps wheni > jorj > len, outsideunsafe),.len/ indexing / re-slicing on a slice-valued call, byte slices interconverting acrosschar,signed charandunsigned char,&s[i]on a slice, slices of typedef/newtype elements ([]Fixed,[]S24), struct fields naming a struct defined later. std/erased.h(Erased),std/audio/s24.h(S24, a packed 24-bit sample),std::str_bytes,struct TimeFields(std/time.h),RpcResult::bytes(),safe_memset_obj/mem_zero_secure_obj,Vec::from_slice/as_slice,Slice::from/as_slice,String::from_bytes/push_bytes/as_bytes,StaticVec::over/StaticMap::over/RingBuffer::over/LFQueue::overon slices.
Breaking changes — slices and typed erasure in std
Every change below is a signature change the compiler reports at each call site (wrong argument count or type); none changes behaviour silently except where noted.
- I/O.
Reader::read([]unsigned char)/Writer::write([]const unsigned char)replaceread(buf, n)/write(buf, n);read_exact(&r, buf),write_all(&w, buf).File,Pipe,BufReader,BufWriter,TcpStream,TlsStream,HttpChunkedWriterfollow.File::gets([]char),File::write_bytes(path, []const unsigned char),file_read/file_writeon slices. - Formatting and strings.
fmt_*/io_fmt_*/io_read_line/io_read_token/wasm_itoa/dom_get_prop_buf/time_formatwrite into a[]char.str_copy([]char, src),str_cat([]char, src)andstr_ncat([]char, src, n)are bounded by the slice, always NUL-terminate and return 1 when everything fit, 0 when truncated (str_copyno longer hasstrncpy's missing-NUL case).mem_chr([]const unsigned char, c).String::from_n/push_n/string_from_nare gone from the public API (usefrom_bytes/push_bytes);String::join(sep, []String),split*(…, []String). - Memory.
safe_memcpy/safe_memmove([]T dst, []const T src)copymin(dst.len, src.len)elements (not bytes) and return the count;safe_memset([]T, val),safe_memcmp([]const T, []const T)(a shorter equal prefix compares less),mem_zero_secure([]T). Callers that passed a byte count must pass a slice of elements now. - Time.
time_gmtime/time_localtimereturn astruct TimeFieldsinstead of filling a 9-int array;time_format([]char, fmt, &tm),time_parse(s, fmt, &tm),time_mktime(&tm). - Sorting.
sys_qsort([]T, cmp);sys_bsearch(&key, []const T, cmp)returns?unsigned long(the index) instead of?&T. - Collections.
Vec::from_arr→Vec::from_slice;Vec::extend([]const T);push_all/enqueue_all/arr_*on slices;StaticVec::over([]T),StaticMap::over([]StaticMapSlot<V>),RingBuffer::over/write/read/peekon slices;LFQueue::over([]T). - Allocators and memory-mapped I/O.
BumpAllocator/PoolAllocator/TlsfAllocator::over([]unsigned char),SlabAllocator::over([]unsigned char, obj_size),DmaBuffer::wrap([]unsigned char, phys). - Async, sync, threads.
Waker,Context,TaskHeader,Task,thread_create,parallel_for, channels: typed?&Erasedcontexts (Waker::empty_()); async net writes take[]const unsigned char. - Audio / MIDI.
AudioIo's buffers are byte slices with typed slice accessors (output_f32()→[]float,input_f32()→[]const float,output_i24()→[]S24, …);audio_convert.hand the MIDI API (MidiMsg::of,midi_build_sysex,MidiParser::new,encode,get_message,send_message,MidiEvent.bytes) on slices. - DSP. FFT / DFT / DCT / windows / convolution / resampling / imaging /
STFT / CQT / CWT / minimum-phase / z-transform take slices (lengths come
from the slices; DFT / DCT / convolution / imaging / STFT / CWT /
minimum-phase now return
int, 0 when an output is too short);FirFilter/IirFilter/ comb / Karplus-Strong constructors take their coefficient and history buffers as slices (a comb's delay ishistory.len);process_block([]const T in, []T out),pluck([]const T),filter_impulse_response/filter_step_response/generate_blockon slices;AudioBufferframes,cuda_/rocm_fft*on slices. - Crypto.
Hasher::update([]const unsigned char)/finish([]unsigned char),sha1/sha256/sha384/sha512(data, digest),Hmac::new(fresh, key),hmac_sha256(key, data, out),hkdf_extract(fresh, salt, ikm, prk),hkdf_expand(fresh, prk, info, out),digest,mgf1_xor, AES (ECB / CBC / GCM), ChaCha20 / Poly1305 and the AEAD,ct_eq/ct_is_zero/ct_copy_if, X25519, EC (keygen, ECDH, ECDSA), RSA,BnMont, X.509,RngCtx::filland the TLS record layer take slices. A user-definedHashermust implement the slice signatures. - Networking, HTTP, RPC.
NetDriver::transmit([]const unsigned char),TcpConn::send/read,ip_checksum,tcp_checksum;http_request,http_request_timeout,http_post,http_fetch,http_post_url,http_build_response,HttpResponse::bytes/set_body_bytes, the chunked coding,MultipartBuilder::add_file,SseParser::feed,WsConn::send_binarytake[]const unsigned charbodies (empty = none);RpcMethodHandlerisfn RpcResult([]const unsigned char)andrpc_call(…, []const unsigned char). Handler/router state and connection readers are typed (?&Erased). - Filesystems.
BlockDriver/BlockDeviceread and write slices;VfsOpsis a table of typed?fnentries over&Erased;VfsNode::read/writeand the drivers'read/writeon slices;VfsNode::readdiris generic over the user data. - Encoding and serialization.
base64_encode, deflate / gzip / zlib,ascii_is_valid_bytes,QueryParams::parse_bytes,PbReader::new([]const unsigned char),PbReader::read_length_delimitedreturns?[]const unsigned char,pb_write_bytes_field(&out, field, bytes). - SIMD and ML.
vm_exp_f32/vm_tanh_f32/vm_sigmoid_f32([]const float, []float);sgemm/sgemm_serialtake slices and return 0 when one is too short for its dimensions. The host-array GPU ops (cuda_/rocm_/mps_/spirv_/webgpu_*_f32, including*_matmul_f32), bufferupload/read/write, the float16 bulk conversions,DenseTensor::from_1d([]const float, grad),from_2d([]const float, rows, cols, grad),SparseTensor::new_csr,HalfTensor/BF16Tensor::gemv/gemv_batchtake slices; tensorgrad_fns are typed; the CUDA*_persistentraw-handle API is removed (useCudaBuf/CudaHalfBuf); Metal / CUDA / ROCm native handles are typed opaque structs. - GUI (signatures only). Widget and window callbacks are typed
?fnwith?&Eraseddata;GuiWindow.platformis a?&struct GuiWindowState;GuiCanvas::set_dash([]const float, offset),fill_text_n([]const char, x, y),GuiPath::polygon([]const float),GuiImage::load_png([]const unsigned char),GuiTypeface::load_memory(_index)/measure_n,GuiTextLayout::build,gui_utf8_next([]const char, &i);gui_window_presenter_namereturnsconst &char. - Low level. I2C (
I2cChip,I2cBus::write/read/write_read) and SPI (SpiDevice::write(tx, ?rx),read(rx)) on slices;Mailbox::send(pid, type, []const unsigned char); syscall and ISR tables hold?fn;signal_handlereturnsint(0 / -1) instead of an opaque previous handler;FuzzTarget::run([]const unsigned char)and fuzz targets arefn void([]const unsigned char). - Compiler: casting a slice to a pointer (
(T*)s) is now an error (it used to reach LLVM and fail verification); uses.ptr. - scc (
selfhost/) follows: it compiles the slice forms above ([]Treturns and fields,[]const T,?[]T,?fn, byte-slice conversions, the slice-to-pointer cast error), and its bundled std (src/vfs.sc) is regenerated from the slice-based headers.
Added — self-hosted compiler (selfhost/)
- scc, a SafeC compiler written in SafeC, is in the new top-level
selfhost/directory. It compiles SafeC straight to a WebAssembly binary, with no LLVM and no linker. It has its own lexer, preprocessor, parser, semantic analysis with safec's safety checks and diagnostics, and wasm backend, plus a small runtime written in SafeC.selfhost/SUBSET.mdlists the supported language subset. - scc builds with the C++
safec, both natively (selfhost/build.sh) and as WebAssembly (selfhost/build-web.sh). It also compiles itself:selfhost/bootstrap.shchecks that the self-compiled compiler reproduces itself byte for byte. - There is a playground API for running SafeC in the browser:
selfhost/web/scc.jsprovidescompile(source)andrun(wasm, { stdout, stdin }), with time and memory limits in a Web Worker. The contract is inselfhost/web/README.md. - A differential test harness,
selfhost/tests/difftest.py, compiles the book and reference programs, a corpus and negative tests with both compilers and compares the results. It also runscompiler/testscompile-only. CI runs all of it on Linux, in the newselfhostjob.
Added — safeguard doc and safeguard publish
safeguard docgenerates a package's API reference intobuild/doc/, like rustdoc: per-item pages (structs, unions, enums, traits, functions, macros, constants, type aliases) with highlighted declarations, Markdown doc comments, intra-doc links ([Vec::push]), code examples and source links; a module tree (one module per header); highlighted source pages; client-side search (a SafeC program compiled to wasm32 onstd/wasm/dom); light and dark themes; links into std's published reference; a link check of the result.--serveserves it overstd/http,--stddocuments std itself.safec --emit-doc-json: the declarations of the inputs — kinds, names, namespaces, generics and bounds, parameters, fields, methods, enumerators, macros, signatures and locations — with their doc comments, as JSON, from the real front end (parse only).--doc-root/--doc-extern/--doc-privateselect what is listed.- Doc comment conventions:
///,/** */, trailing///<,//!module docs, and std's plain//comments above or beside a declaration;@hidden. See the SafeC docs' Doc Comments page. safeguard/doctool: safec-doc, the generatorsafeguard docruns, a SafeC program onstd/serial/json,std/serial/markdown(render hooks), scx templates,std/dir,std/path,std/httpandstd/wasm/dom. Releases ship it prebuilt (bin/safec-doc,share/safec/doctool/); from a source checkout it is built on first use and cached.safeguard publishprepares a library's git repository for publishing:git init,.gitignore, README/LICENSE/metadata checks,--bump(semver), and CI for GitHub Actions (default), GitLab CI, Gitea or Forgejo Actions (--ci=) that builds and tests on Linux/macOS/Windows and, on a tag, attaches prebuilt static and shared libraries per OS/arch to a release and deployssafeguard docto the repository's Pages. A dry run unless--write;--tagcommits and tags,--pushpushes.Package.toml:[package]repository,homepage,documentation,readme,keywords; a[publish]table (ci,branch,tag_prefix,pages,release).safeguard build --crate-type staticlib|cdylib|binoverrides[build] crate_type.examples/doc_demo: a documented example library.
Added — ownership checks (Drop) and thread safety (Send/Sync)
Drop: a type with avoid drop()method, or one holding such a value, must be dropped or moved exactly once on every path (return,break,continue,goto,match,defer, loops). Leaks, use after drop or move, double drops, overwriting a live value, and dropping or moving a value out of borrowed memory are compile errors, with a note at the declaration. Checked per generic instantiation; panicking paths are exempt. No run-time cost: nothing is inserted.consumemethods (int close() consume;) destroy their receiver likedrop(); destructors must take apart every owned field.drop(x)(drop glue for any type),needs_drop(T), conditionalvoid drop() if (cond);, andforget(x)(only inunsafe).std/ownership.h:std::replace,std::swap.- A reference a method returns into its receiver borrows from it: using it
after the receiver is dropped or moved is an error (
MutexGuard::get(),Vec::at()). Send/Sync, derived from each type's structure, overridable withunsafe impl Send/Sync;andimpl !Send/!Sync;, usable as generic bounds.spawntakes only static data that is Sync (or synchronizes its own writes);spawn_scopedlends a local until it is joined (no access meanwhile for a mutable lend; the local must outlive the join);chan_sendneeds a Send value and moves it.std/thread.h:thread_spawn/SpawnedThread<T, R>(moves its argument into the thread,join()returns the result),Mutex<T>/MutexGuard<T>(the value only through the guard, unlocked by itsdrop()),RwLock<T>.std/atomic.h:AtomicInt,AtomicLong.Result::unwrap()/unwrap_err()(consume).
Changed — ownership
- The standard library's owning types implement
Drop: collections,File, directories, sockets, TLS, HTTP, threads, sync primitives, GUI, audio/MIDI, async (futures, tasks, wakers, executors, channels), ML containers.free()/close()/destroy()stay asconsumealiases.Vec::clear()/drop()/set()drop the elements they discard. ThreadPool::spawn/block_on,Chan<T>,AsyncChannel<T>andOneshot<T>require Send values; GUI windows are!Send.- Dropping a spawned, unfinished
AsyncTaskpanics instead of leaving its executor to poll freed memory.
Fixed — found by the ownership checks
- Leaks, double frees and use-after-frees across std, tests and examples,
among them:
TlsStream::client()/TlsAcceptor::new()failures kept their backend state; a failed client-certificate CertificateVerify, the GUI SVG renderer (a use after free of a by-valueString), widget and PNG loaders, static-file and router helpers, URL/header/query removal,Audio::open(a value used after being moved), LLM and LoRA/JiT ML paths (double frees), and safec-doc's link checker.
Changed
safeguard testin a library package (staticlib/cdylib) builds the library and links it into each test, withsrc/on the include path.