ylibycxx
Contents

Spec sheet · libycxx 0.1.0

A clean-room C++26 standard library

libycxx is a new implementation of the whole C++26 standard library for the latest GCC and Clang, with its own Itanium C++ ABI runtime. It is written from the working draft, not from anyone else’s code, and tested against the standard rather than against itself.

Standard
C++26 (the current working draft), C++26 only
Compilers
the latest GCC and Clang (matrix)
Platforms
Linux and macOS; bare-metal targets freestanding (matrix)
Layers
freestanding core · hosted library · platform abstraction layer
ABI runtime
its own: exceptions, RTTI, dynamic_cast, guards
Modules
import std; and import std.compat;
Version
0.1.0 — no stable ABI yet
hello.cpp
import std;

int main() {
  std::vector<std::string> words{"modules", "import", "std"};
  std::ranges::sort(words);
  // prints ["import", "modules", "std"]
  std::println("{}", words);
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.28)
project(hello CXX)
find_package(libycxx CONFIG REQUIRED)
add_executable(hello hello.cpp)
# import std; with libycxx's modules (brings ycxx::ycxx)
target_link_libraries(hello PRIVATE ycxx::modules)
shell
cmake -S . -B build -G Ninja \
  -DCMAKE_CXX_COMPILER=clang++-23 -DCMAKE_PREFIX_PATH=/opt/libycxx
cmake --build build && ./build/hello

An existing project that knows nothing about libycxx: pass -DCMAKE_TOOLCHAIN_FILE=/opt/libycxx/lib/cmake/libycxx/toolchain.cmake (Getting started).

C++26every headerthe C++26 library, freestanding and hosted
2,824own testsspec-derived, all passing in CI on Linux and macOS
17,098external testslibc++ and libstdc++ suites, run unmodified
19real-world projectsbuilt and tested against libycxx, linkage proved
01

What it is

A standard library that a program selects explicitly, in place of the toolchain’s libstdc++ or libc++, for the latest GCC and Clang on Linux and macOS.

libycxx is layered. A freestanding core needs no operating system, no C library and no heap unless one is supplied. The hosted library adds everything that needs the environment — streams, files, threads, clocks, locales — and reaches the system only through a small C-linkage platform abstraction layer. Underneath sits libycxx’s own Itanium C++ ABI runtime, so a program built with it depends on neither libsupc++ nor libc++abi.

Standard
C++26 (the current working draft), C++26 only
Compilers
the latest GCC and Clang (matrix)
Platforms
Linux and macOS; bare-metal targets freestanding (matrix)
Layers
freestanding core · hosted library · platform abstraction layer
ABI runtime
its own: exceptions, RTTI, dynamic_cast, guards
Modules
import std; and import std.compat;
Version
0.1.0 — no stable ABI yet
Source
github.com/yrashk/libycxx
02

What is implemented

Every header of the C++26 library is provided, with the deprecated features the draft still specifies (Annex D, marked [[deprecated]]) and without the ones it has removed.

  • Language support & utilities

    <type_traits>, <expected>, <optional>, <variant>, the function wrappers including function_ref, indirect and polymorphic, constexpr exceptions on GCC.

  • Containers & ranges

    Every container, constexpr, including <hive>, <inplace_vector>, <flat_map> and <mdspan>; every range factory and adaptor; every std:: and ranges:: algorithm.

  • Text

    <format> and <print> in full, <charconv> with shortest round-trip floating point, <regex> with all six grammars, <text_encoding> with the IANA registry.

  • Numerics

    <random> with philox_engine and all 20 distributions, <simd>, <linalg>, <cmath>, <complex>, <valarray>.

  • Time

    <chrono> in full: the calendar, utc/tai/gps clocks with leap seconds, the time zone database on the system’s zoneinfo, every formatter and parser.

  • Concurrency

    Threads, atomics, latches, barriers and semaphores, <rcu>, <hazard_pointer>, and senders and receivers in <execution> with parallel_scheduler.

  • I/O & locales

    Iostreams, <spanstream>, <syncstream>, <filesystem>, every standard facet, and named locales from the C library.

  • Diagnostics

    <stacktrace> with ELF/DWARF symbolization and its own demangler, <debugging>, <contracts> and <meta> (reflection) where GCC supports them.

Freestanding, hosted and modules

Every core header, every header with a freestanding subset and every header of the draft’s freestanding table compiles with -ffreestanding -nostdlib -nostdinc -fno-exceptions -fno-rtti; a smoke test links for x86_64-unknown-none-elf and riscv64-unknown-elf. An optional runtime archive, ycxx::freestanding, supplies allocation, floating-point <charconv> and the atomic wait tables to programs without an operating system.

The standard library modules std and std.compat are generated from the headers and built per project with the project’s own flags, from CMake (ycxx::modules) or with tools/ycxx-modules.

Audited against the draft

Three audits walk the synopses of the working draft and generate a check for every declaration, on both compilers (docs/SPEC_COVERAGE.md):

ClausesDeclarationsChecks
Library, support, concepts, diagnostics, memory, meta, utilities2,029every name, 313 macros
Containers, iterators, ranges, algorithms, strings6,16425,470
Text, numerics, time, I/O, threads, execution, Annex D6,36713,237

What remains open is compiler-blocked: Clang 23 has no reflection or contracts and cannot throw during constant evaluation; GCC 16 lacks is_within_lifetime.

03

How it is built

A handful of rules, written down in DECISIONS.md with their reasons and the alternatives that were rejected.

Clean room

Written from the C++ working draft, WG21 papers, POSIX and the published Itanium C++ ABI — never from the source or headers of another standard library or C++ runtime. The libc++ and libstdc++ test suites are run as oracles, never copied into the repository and never edited.

Hidden namespaces and reserved names

Standard entities are declared directly in std; details live in ycxx::detail, and the bases of standard types in ycxx::adl_free, a namespace with no functions, so argument-dependent lookup never reaches the internals. Every name the headers use for themselves is a reserved identifier: a program may #define any name the standard does not declare, which a test with 5,874 such macros checks. Every namespace block carries hidden visibility, so a program or shared object exports nothing of libycxx and another C++ runtime in the process — Apple’s libc++abi, say — keeps its own.

Strict preprocessor policy

One file, config.hpp, inspects compiler and target macros and turns each answer into a constexpr value or a type; the library then uses if constexpr, requires and templates. It defines no function-like macros, and facts about the target are probed against the real toolchain at configure time, not written down.

Platform layer and hosted layers

The hosted library reaches the system through the C primitives of ycxx/pal.h, implemented on POSIX by default. They are grouped into layers — memory, console, clock, threads, random, files, environment, debug, clib — that an integrator can provide one by one. Containers, exceptions and std::print then run over a heap and a serial port, as in the bare-metal x86_64 example booted by Limine in QEMU. A feature whose layer is absent fails to compile or link, naming the layer, never silently at run time.

Its own ABI runtime

libycxx-abi.a implements exception allocation, throw, catch and rethrow, the personality routine and LSDA parsing, type_info and the RTTI classes, __dynamic_cast, static-local guards, terminate and new handlers and the reference counting exception_ptr needs, from the Itanium C++ ABI documents. Only stack unwinding comes from the toolchain’s unwinder.

Named locales on the C library

std::locale("de_DE.UTF-8") accepts every name the C library’s newlocale accepts, on glibc and Darwin’s libc. Each named category holds _byname facets for char and wchar_t built on one shared locale_t, using the _l functions per call; the global C locale and other threads are never touched.

Deliberate divergences

Where the draft and the established libraries disagree, libycxx follows the draft and says so; where the letter of the draft would break portable code, it follows practice and says that too. The full list is in STATUS.md (“Deliberate divergences”):

  • to_chars without a precision follows P3505 and the draft’s “smallest number of characters”, where older tests encode the C++17 rule.
  • Floating-point from_chars leaves the value unmodified on result_out_of_range, as the draft says.
  • <regex>’s ECMAScript grammar follows ECMA-262 (\a is an identity escape; a back-reference may precede its group).
  • regex_traits::transform_primary in the classic locale returns the whole sort key, so [[=a=]] keeps working there, as portable code expects.
  • expected’s operator== with a value deduces its left operand, to avoid a constraint recursion the literal signature causes.
04

How it is tested

Against the specification, by several independent oracles, in every configuration a user can select — and every run explains itself.

Its own suite, written from the draft

2,824 tests in tests/ycxx, written by an author who may read only the working draft and cppreference — never libc++’s or libstdc++’s tests, nor any implementation, libycxx’s included. A failing test is a library bug until the draft shows otherwise; tests are never weakened to fit. Tests that must not compile name the diagnostic they expect, and hardened preconditions have death tests. Running the suite against libstdc++ validates the tests themselves: every difference is listed and triaged.

Own suite on Linux (pass / fail / expected failure): default from CI, 2026-10-07; the others as recorded in STATUS.md, 2026-10-05/06
ConfigurationGCC 16.2Clang 23.1
Default2,723 / 0 / 152,698 / 0 / 40
Hardened (YCXX_HARDENED=1)2,425 / 0 / 132,418 / 0 / 20
No exceptions (-fno-exceptions)1,941 / 0 / 71,941 / 0 / 7
AddressSanitizer—2,685 / 0 / 39
ThreadSanitizer2,722 / 0 / 152,695 / 0 / 39

libc++ and libstdc++ conformance suites

Both suites run unmodified from pinned releases (LLVM 23.1.2’s libcxx/test/std, GCC 16.2.0’s libstdc++ testsuite), fetched on demand and never vendored.

Full runs of 2026-10-05/06 (pass / fail / unsupported)
SuiteGCC 16.2Clang 23.1
libc++ (8,543 tests)7,494 / 211 / 8367,495 / 215 / 832
libstdc++ (8,555 tests)6,206 / 13 / 2,3356,161 / 13 / 2,344*

Every failure is categorised in tests/libcxx/TRIAGE.md and tests/libstdcxx/TRIAGE.md. Most are tests that rely on headers libstdc++ and libc++ include from one another, on their own specifics or pre-C++26 values, or on not running as root. Skipped tests carry a category and a reason and name the libycxx test that covers the subject. *Projected from the full run with the final lists.

Sanitizers, hardening and library modes

All three suites run under AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer, linked with a libycxx built with the same sanitizers — an uninstrumented library would hide every hand-off through its own mutexes and reference counts. The own suite also runs hardened, with -fno-exceptions and at -O2, each with its own report.

Nineteen real-world projects

tools/realworld builds open-source projects against libycxx with both compilers and runs their own test suites; then it proves the build was libycxx’s alone: every translation unit’s command, every object’s headers, every image’s needed libraries and symbols, and libycxx’s allocation table in every image. A self-test built with the toolchain’s library is rejected on every count.

Runs of 2026-10-07: every project built and passed with both compilers (CTest tests passed, GCC / Clang)
ProjectReleaseGCCClang
GoogleTestv1.18.06363
Abseil20260817.0242242
Google Benchmarkv1.9.58383
Catch2v3.16.08181
CLI11v2.7.28282
doctestv2.5.3191191
EnTTv4.0.01515
{fmt}12.2.02121
glazev9.0.0145140
Microsoft GSLv5.0.11515
nlohmann/jsonv3.12.0101101
libcorov0.16.01†1†
magic_enumv0.9.812 + 3 xfail15
oneTBBv2023.1.0141141
range-v30.12.0234236
simdjsonv5.0.2153153
spdlogv1.17.01†1†
Taskflowv4.1.02,9092,909
yaml-cpp0.8.01†1†

†One CTest test runs the project’s whole suite. All 19 also pass under AddressSanitizer, and five under ThreadSanitizer; neither reported anything in libycxx. The projects found three libycxx bugs (each fixed with a test) and four compiler bugs; their patches are to their own non-standard code, each with a category and reason.

Continuous integration, without baselines

Every push runs the policy checks, both library builds, the freestanding check, the CMake package test and the own suite on Linux (the gcc:16 container, Clang 23 from apt.llvm.org) and macOS (Apple Silicon, Homebrew’s GCC 16, Clang 23), and on Linux a sample of both external suites. Nightly, the whole external suites run on both compilers and both platforms, with the sanitizer, hardened, no-exceptions and -O2 configurations and the real-world projects. The platform and compiler matrix lists what each combination has passed.

There is no list of tolerated failures. A test reported FAIL fails the run, and every failure is resolved where its cause is decided: a libycxx bug is fixed; a test that does not apply is skipped with its category and reason; a cause outside the test and the library — a compiler bug, an ABI limit, a draft defect — is an expected failure with its reason, and an unexpected pass fails the run so the mark goes when the cause does.

Every test, passing ones included, records its commands, exit statuses and output; each run writes an HTML report with its provenance, and a Markdown copy ready to paste to a person or an assistant.

05

Platform and compiler matrix

Exactly what has been confirmed, where, and with which compiler. A ✓ means a CI run or a run recorded in STATUS.md passed with no unexpected failure; anything less is said in words.

Hosted library. Dates are those of the recorded run.
What is verifiedLinux x86_64Ubuntu 24.04, gcc:16 containermacOS 15 arm64Apple Silicon
GCC 16.2.0Clang 23.1.2apt.llvm.orgGCC 16.2.0HomebrewClang 23.1.2LLVM release
Build, freestanding check, CMake package✓ every push✓ every push✓ every push✓ every push
Own suitea✓ 2,723 pass15 xfail✓ 2,698 pass40 xfail✓ 2,743 pass16 xfail✓ 2,717 pass42 xfail
libc++ and libstdc++ suites, sample✓ every push✓ every push——
libc++ and libstdc++ suites, wholeb7,494 / 6,206 passfailures triaged7,495 / 6,161 passfailures triaged; libstdc++ projectedNightly job, no recorded resultfNightly job, no recorded resultf
AddressSanitizere—no ASan runtime in this GCC build✓ own suiteUBSan with it nightly——
ThreadSanitizere✓ own suite✓ own suite——
Hardened, -fno-exceptionsc✓ ✓✓ ✓——
Real-world projectsd✓ 19 of 19✓ 19 of 19under ASan: 19; under TSan: 5——

Linux on aarch64 and other architectures: not built or tested. — means not run.

Freestanding and bare metal
TargetCompilerWhat is checked
x86_64-unknown-none-elf, riscv64-unknown-elfClang 23.1✓ every freestanding header compiles; the smoke test links
x86_64, freestandingGCC 16.2✓ every freestanding header compiles; the smoke test links
Bare x86_64 kernel, Limine in QEMU 8.2GCC 16.2, Clang 23.1✓ boots and passes: containers, std::print and exceptions over the program’s own heap and consoleg
Darwin arm64 and x86_64Clang 23.1Cross-compiled only: every core header and the freestanding runtime compile for both Darwin targets
  1. a CI run of 2026-10-07 (commit d48ba32), 2,824 tests; no failures. Expected failures carry their cause in the test (compiler gaps, ABI limits, a draft defect); the rest are unsupported in that configuration.
  2. b Full runs of 2026-10-05/06 recorded in STATUS.md: 211 and 215 libc++ failures, 13 libstdc++ failures per compiler, each categorised in the suites’ TRIAGE.md. The Clang libstdc++ figure is projected from the full run with the final skip lists. The CI sample (selected directories) passes on every push.
  3. c Own suite, runs of 2026-10-05 recorded in STATUS.md: hardened 2,425 / 2,418 pass and -fno-exceptions 1,941 pass, no failures. The nightly also runs -O2, for which STATUS.md records no result.
  4. d Runs of 2026-10-07 recorded in STATUS.md, with the linkage proof for every project.
  5. e ASan and TSan: whole own suite, 2026-10-06, no failures (GCC TSan with a GCC built with libsanitizer). The nightly ASan job also enables UBSan; STATUS.md records the ASan run.
  6. f The nightly workflow runs both external suites on macOS; STATUS.md records no macOS result yet, and named locales and <stacktrace> symbolization are less exercised there.
  7. g examples/hosted-layers, run by the CMake package test on Linux.
06

Getting started

Build libycxx once per compiler, install it, and point a project at the installation. docs/BUILDING_PROJECTS.md covers CMake, Meson, make, autotools, Bazel, Conan and vcpkg.

1. Toolchains

tools/toolchain/provision finds GCC 16.2 and Clang 23.1 or installs them; activation puts them on PATH.

shell
git clone https://github.com/yrashk/libycxx && cd libycxx
# finds GCC 16.2 and Clang 23.1, or downloads/builds what is missing
source tools/toolchain/activate.sh --provision

2. Build and install

shell
cmake -S . -B build/gcc -G Ninja \
      -DCMAKE_C_COMPILER=$YCXX_GCC -DCMAKE_CXX_COMPILER=$YCXX_GXX
cmake --build build/gcc
cmake --install build/gcc --prefix /opt/libycxx-gcc

3. Use it

A project of your own links ycxx::ycxx, which compiles against libycxx’s headers and links its archives and ABI runtime instead of libstdc++ or libc++:

CMake
find_package(libycxx CONFIG REQUIRED)
add_executable(app main.cpp)
# ycxx::modules instead, for import std;
target_link_libraries(app PRIVATE ycxx::ycxx)

An existing project that knows nothing about libycxx is built with the installed toolchain file, the compiler wrappers, a Meson native file, or pkg-config (with PKG_CONFIG_PATH set to the installation’s lib/pkgconfig):

shell
P=/opt/libycxx-gcc
cmake -S proj -B build -DCMAKE_TOOLCHAIN_FILE=$P/lib/cmake/libycxx/toolchain.cmake
make CXX=$P/bin/ycxx-c++ CC=$P/bin/ycxx-cc
meson setup build --native-file $P/share/libycxx/meson-native.ini
g++-16 $(pkg-config --cflags libycxx) main.cpp $(pkg-config --libs libycxx)

ycxx-c++ is the compiler libycxx was built with, plus its headers, C++26 and its archives. Each C++ dependency must be built from source the same way: libycxx has its own ABI, so a distribution’s prebuilt C++ libraries cannot be linked in. C libraries are fine.

4. Check the result

shell
# was everything built against libycxx, and nothing else?
/opt/libycxx-gcc/bin/ycxx-check-binary build
# libycxx's own gate: policy checks, builds, freestanding, own suite
tools/test
07

Status and limitations

Every header of C++26 is in place and the own suite passes on both compilers, on Linux and macOS. What libycxx is not yet, plainly:

  • Young, without a stable ABI

    Version 0.1.0. Type layouts change between commits: rebuild everything, dependencies included, when libycxx changes, and keep one installation per compiler.

  • C++26 only

    Older -std= modes are raised to C++26, so features C++26 removed (auto_ptr, <strstream>, …) and libstdc++/libc++ extensions do not compile. Code relying on headers those libraries include from one another needs a few #includes.

  • One runtime per image

    libycxx links statically and hides its symbols, so each shared library has its own runtime state: terminate and new handlers, throw; and current_exception do not cross shared-library boundaries.

  • Compiler gaps

    <meta> needs GCC with -freflection and <contracts> GCC’s -fcontracts; Clang 23 has neither and cannot throw during constant evaluation. GCC 16 cannot mix #include and import std; in one translation unit.

  • macOS is newer

    The macOS job runs the gate on every push, but parts written for Darwin are less exercised: named locales there, and <stacktrace> symbolization, which reads ELF and DWARF only. Modules are tested on Linux only.

  • Open work

    Remaining external-suite failures are triaged; two libycxx bugs are open among them. <filesystem> and the time zone database have no hosted-layer primitives yet. About ten benchmarks remain over 1.5× slower than libstdc++.

STATUS.md is the full record: per-area results, known compiler gaps, the draft defects noticed along the way, and what comes next.