변경 내역
SafeC의 컴파일러, 표준 라이브러리, safeguard, 설치 스크립트에 대한 주요 변경 사항을 CHANGELOG.md에서 가져와 최신순으로 보여 줍니다(영어 원문). 병합되었지만 아직 릴리스되지 않은 변경은 Unreleased 아래에 있고, 릴리스마다 릴리스 노트 글이 있습니다.
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.