Валідація
Поточний базовий рівень
Останні записані перевірки на хості розробки Windows x64 (clang-cl і LLVM SDK
23.1.2, /MT, _ITERATOR_DEBUG_LEVEL=0); наступні кроки записують свої в
.agents/00-finished.md:
| Перевірка | Результат |
|---|---|
| Свіжий комбінований Debug CTest (2026-10-14) | 299/300 пройдено; збій — відома межа розміру IR у codegen_measurements; codegen_resource_cli вимкнено |
check-quality-all (той самий запуск) | Без зауважень; Lizard CCN 10 |
Опційні тести аудиту OTP (2026-10-14, прив'язка d199a49a01a2, еталонний OTP 29.1.1) | 17/17 |
regenerate.py --corpus all --check (те саме) | 19/19 корпусів відтворюються |
Запуск перевірки
З командного рядка розробника Visual Studio x64 з C:\Program Files\LLVM\bin у
PATH (автоматичний вибір SDK повторно використовує thirdparty/):
cmake --preset debug --fresh -DBUILD_TESTING=ON -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
cmake --build build/debug
ctest --preset debug-fast # during development
ctest --preset debug -j 16 # full mode, at feature completion
cmake --build build/debug --target check-quality # changed files, one unit per header
cmake --build build/debug --target check-quality-all # whole tree
CLAUSE_TEST_MODE=fastзапускає еталонні корпуси на O0 positional і проєкт на O2 з вимкненою спеціалізацією, виконує мутації один раз і пропускає тестиfull_only(окремі проєкти-споживачі, як-отruntime_generated_link, вимірювання, аудит хостової OTPlibrary_freshnessі повільні сценаріїlinking_lto,linking_exports,linking_compile_cache,programs_compileтаtransforms_cache). Повний режим (типово) запускає всі комбінації драйверів і політик.- Довгі тести, які керуються скриптом Python або CMake, мають фіксовану
COSTу CTest: CTest визначає вартість обгортки з її власного малого процесорного часу й запускав би їх останніми. - Чутливі до навантаження тести (
CLAUSE_LOAD_SENSITIVE_TESTSу кореневомуCMakeLists.txt:frontend_cli,codegen_examples,linking_debugger,executables_applications) перевищували час очікування або програвали часові перегони лише під час повного паралельного запуску. Вони маютьDEPENDSна всі інші тести, тому починаються після завершення решти вибірки й отримують процесори у своє розпорядження; збій залежності не призводить до їх пропуску. CLAUSE_TEST_TIMEOUT_SCALE=Nмножить обмеження на один запуск для виконавця еталонів (60 с) і виконавця семантичних випадків (15 с) для інструментованих збірок.check-quality,make formatіmake-format.batохоплюють файли, змінені відHEAD, і невідстежувані файли;cmake/quality_scope.pyдодає для кожного зміненого заголовка одну одиницю трансляції, що його включає (власне джерело, інакше першого користувача, з даних залежностей Ninja будь-якої конфігурації; інші залежні одиниці заголовка залишаються дляcheck-quality-allу повній перевірці), а для clang-tidy — одиниці, команда компіляції яких відрізняється від записаної останнім успішним запуском (build/debug/quality/tidy-commands.json), тому зміна правила збирання повторно перевіряє лише змінені нею одиниці. Зміни.clang-tidyчи скриптів tidy вибирають усе, як і зміни правил збирання до першого записаного запуску.CLAUSE_OPTIMIZED_COMPILER(за замовчуванням ON) компілюєclauі бібліотеки компілятора з оптимізацією також у збірках Debug, зберігаючи їхню налагоджувальну інформацію та бюджети виведення типів Debug; тести проводять більшу частину часу вclau. Коли збій компілятора треба пройти покроково, слід конфігурувати з-DCLAUSE_OPTIMIZED_COMPILER=OFF, перезібрати й знову запустити тест, що не пройшов.- Тестові програми не містять налагоджувальної інформації (без PDB); для
перезбирання під налагоджувач слід конфігурувати з
-DCLAUSE_TEST_DEBUG_INFO=ON. Вкладені нативні споживачі компонуються з runtime батьківської збірки черезClauseRuntimeTargets.cmakeзамість повторної компіляції; лишеruntime_linkзбирає runtime окремо. - Пороги та придушення попереджень ніколи не підвищуються, щоб пройти
перевірку. Налаштовано два винятки для сторонніх бібліотек (рішення
користувача):
cmake/tidy_filter.pyвідкидає звіти статичного аналізатора, розташовані в заголовках Boost (хибні спрацювання Boost.Multiprecision щодо сховища лімбів у Linux, які переписування коду, що викликає, лише переносить), і рахує їх у виводі, аbugprone-exception-escapeігноруєbad_array_new_length(конструктори переміщення map/set у MSVC виділяють пам'ять; збій виділення однаково фатальний). - clang-tidy перевіряє заголовки проєкту на кожному хості: фільтр заголовків
приймає роздільники
/і\.
Linux
Та сама перевірка працює в Linux із зафіксованим LLVM 23.1.2 Linux SDK як
компілятором хоста (CC/CXX з його bin/), системною libstdc++, Ninja і
CMake 3.28 або новішим. Boost 1.90 і toml++ 3.4.0 беруться зі встановлених
пакетів або розпаковуються в thirdparty/ (boost_1_90_0/,
tomlplusplus-3.4.0/); лише у Windows вони завантажуються. ld.lld з архіву
релізу компонується з ICU 70 (Ubuntu 22.04), а його lldb — з Python 3.14; на
хостах без них linking_lto потребує ICU 70 у LD_LIBRARY_PATH, а
linking_debugger використовує GDB.
cmake --preset debug --fresh -G Ninja -DBUILD_TESTING=ON
cmake --build build/debug
ctest --preset debug -j 16
check-quality (Lizard, clang-tidy) запускається лише на хості розробки під
Windows; інші хости збирають і запускають тести (одного разу перевірка
пройшла без зауважень у Linux, див. історію).
Інші цілі
32-бітні цілі й цілі ARM перевіряються без компілятора, зібраного для них (для цих хостів немає LLVM SDK):
- Збірка лише runtime (
-DCLAUSE_BUILD_COMPILER=OFF -DBUILD_TESTING=ON) для цілі запускає там тести runtime і ABI: нативно, в userland цілі (chroot) або через емулятор хоста (qemu binfmt). tests/compiler/executables/cross.pyзапускає кожен еталон виконуваних файлів і кожну фікстуру програм зclauцього хоста,--target-triple, архівом runtime тієї збірки й--linker, що досягає бібліотек цілі (run.py --target-optionпередає їх далі).
Фікстури та походження
- Звичайні збірки й тести не потребують ні OTP, ні його вихідного коду. Еталони (goldens) було згенеровано один раз з OTP і закомічено з хешами; перевірки хешів виконуються перед використанням будь-якої фікстури.
- Дев'ятнадцять корпусів зразків/guards містять 67,748 нативних очікуваних значень/помилок і 106 рядків семантичного приймання. Кожен нативний корпус запускає позиційний і проєктний драйвери на O0/O2 з увімкненою/вимкненою спеціалізацією, з локальними та віддаленими викликами.
- Закомічені вхідні файли Erlang написано локально
(
tests/fixtures/patternmatch/fragments/, препроцесорніsemantic/headers/);fixture_sourcesзабезпечує ізоляцію від вихідного коду OTP. - Шість фікстур цільових програм (
tests/fixtures/programs/) містять еталони stdout/коду завершення з OTP та їхні діагностики компіляції;programs_compileперевіряє їх без OTP, аtests/compiler/programs/regenerate.py --checkвідтворює їх під OTP (карта фікстур). - Еталони друку термів (
tests/fixtures/printing/) містять текст OTP~wіerlang:display/1для 9,542 значень, а також stdout OTP для скомпільованих викликів display;tests/compiler/printing/regenerate.py --checkвідтворює їх (нотатки до фікстур). - Еталонні випадки виконуваних файлів (
tests/fixtures/executables/) — це каталоги з вихідним кодом плюс одинgolden.jsonзі stdout/кодом завершення OTP і написаним вручну шаблоном stderr;tests/compiler/executables/run.pyкомпонує й запускає кожен із них у матриці політик/драйверів (CTestexecutables_<case>), аregenerate.py --checkвідтворює їх під OTP (нотатки до випадків). - Перегенерація та живі аудити запускаються явно:
-DCLAUSE_OTP_AUDITS=ONіtests/compiler/patternmatch/regenerate.py(інструкції). Ніщо не оновлює еталони чи прив'язку мовчки.
Дизайн тестів
Поведінка тестується через справжній CLI, згенеровані об'єктні файли, скомпоновані нативні споживачі та еталони OTP. Цільові модульні тести залишаються лише там, де вихідний код не може досягти потрібного стану, і для кожного його призначення вказано в самому тесті:
- Впроваджені бюджети та збої виділення пам'яті/вводу-виводу (
project_limits,project_creation_failure,codegen_limits,codegen_write_failure,runtime_lifecycle_failure, точки впровадження збоїв у згенерованих викликах). - Приватне володіння, недійсні/застарілі дескриптори та інваріанти відкату в синтаксичному аналізаторі, семантичному графі типів і бекенді.
- Валідація сирих слів і межі термів 32/64 біти (
runtime_immediate,runtime_term_tag,abi_integers). - Збої приймачів діагностики та знімок стабільних ідентифікаторів можливостей.
Випадки, що залежать від можливостей файлової системи (посилання, псевдоніми регістру), повідомляють про пропуски окремих випадків; вони ніколи не підміняють проходження цілого тесту.
Стан платформ і санітайзерів
- Нативне виконання згенерованого коду: Windows x64; Linux x86-64. Результати для 32-бітних цілей і ARM (тести runtime та крос-скомпоновані еталони) — в історії.
- Apple Silicon: не перевірявся відтоді, як з'явився бекенд.
- ASan, UBSan і LeakSanitizer для компілятора й runtime: Linux x86-64, одна
комбінована конфігурація Debug
(
-fsanitize=address,undefined -fno-sanitize=vptr -fno-sanitize-recover=undefined), повний CTest зCLAUSE_TEST_TIMEOUT_SCALE=10(інструментований код працює в кілька разів повільніше): жодного звіту санітайзера. Компілятор, runtime і тестові програми інструментовано; програми, які компонуєclau, беруть runtime санітайзера з обгорткиclang++уPATH, а їхній згенерований код не інструментовано. Чотири тести з упровадженою відмовою хоста (codegen_failure_*,runtime_lifecycle_failure,runtime_memory) лежать поза межами ASan: йогоoperator newаварійно завершується на неможливому розмірі замість кидання винятку. - ThreadSanitizer: Linux x86-64, та сама область інструментування з
-fsanitize=thread; повний CTest і тести паралельності, кожен п'ять разів, не повідомляють про гонки. Також поза межами: два тести, що замінюютьoperator new. - У Windows попередньо зібраний LLVM SDK і далі конфліктує з
інструментованим кодом (
annotate_string0 проти 1). - ASan лише для runtime проходить на Windows з Release-пробами,
/EHsc /fsanitize=address,/MT, бібліотекою імпорту ASan від Clang і статичним runtime-перехідником (thunk), а також із DLL ASan уPATH. - Історичні запуски на macOS arm64 (2026-09-19/20) пройшли повні збірки Debug, C++26, ASan+UBSan, лише компілятора та лише runtime для етапів синтаксичного аналізатора та проєктів; вони передують бекенду.
Бенчмарки
Ручне порівняння продуктивності з Erlang/OTP. Воно ніколи не входить до
CTest, перевірки комітів чи check-quality, і лише йому потрібен Erlang/OTP на
хості; результати — це свідчення для конкретного хоста, а не результати
перевірки.
make-benchmark.bat # Windows; set JOBS=N to cap parallel builds
make-benchmark.bat --repeat 9 --only bench_gc,bench_msg
make-benchmark.bat --cpus all # unpinned set; default --cpus performance
make benchmark BENCH_ARGS="--baseline HEAD~5" # other hosts
- Обидва налаштовують і збирають Release
clauі runtime уbuild/bench, а потім запускаютьbenchmarks/run.py(--helpперелічує його опції). - Програми:
benchmarks/bench_*.erlзbench_util.erl, які без змін збираютьerlcіclau -O2. Кожна вимірює свої ядра за допомогоюerlang:monotonic_time/1після одного виклику для розігріву й друкуєBENCH <name> <nanoseconds>: генерація даних, обробка даних, збирання сміття, передавання повідомлень, файловий ввід-вивід (у порожньому тимчасовому робочому каталозі вbuild/bench/runs, усередині репозиторію, щоб на нього поширювалося антивірусне виключення для репозиторію), сокети loopback і бібліотеки (libraries.qlc: перетворення запитів черезqlc_ptі модулі за зразком випадківqlc_SUITE, вимірювані разом). Кількість ітерацій забезпечує тривалість кожного ядра в OTP 1–2 с, тож роздільність таймера й шум хоста малі порівняно з нею; сокетні ядра збільшують роботу на з’єднання, а не кількість сокетів. Поза вимірюванням і розігрів, і вимірювані виклики чекають завершення всіх породжених ними процесів (помилкаprocesses_leftпісля 10 с). Кожне ядро працює у власному новому процесі, який завершується до початку наступного ядра, тому жодне ядро не оплачує купи, збільшені попереднім. - Кожен запуск програми має власний об’єкт завдання Windows або сеанс POSIX; наступний запуск починається після завершення всіх його процесів, а залишки, живі через 30 с після виходу, примусово завершуються й повідомляються як збій.
- CPU: кожен процес запуску, як OTP, так і Clause, працює на наборі
--cpus:performance(типово: ядра з найвищимEfficiencyClassу Windows,/sys/devices/cpu_core/cpusабо найбільшимcpu_capacityу Linux, тобто P-ядра гібридного CPU, інакше всі CPU),all, список на кшталт0-15чи2,4,6або шістнадцяткова маска на кшталт0x5554. У Windows об’єкт завдання запуску містить обмеження спорідненості до відновлення програми, у Linux програма задає його доexec; macOS не підтримує прив’язку й працює без неї. І OTP, і Clause запускають один активний планувальник на CPU набору, без прив’язки до окремих CPU. Заголовок звіту й кожен запис історії (cpus) вказують набір; відношення запусків на різних наборах непорівнянні. - Системи на тому самому хості в одному запуску, почергово: OTP з типовим
варіантом (JIT), OTP
-emu_flavor emu, якщо встановлено (інсталятор для Windows постачає лише JIT, тож ці стовпці показуютьn/a), Clause HEAD на O2 і з--lto, а також попередній коміт Clause (типово: найновіший коміт, старіший за HEAD щонайменше на один день;--baseline REV,--no-baseline), зібраний у кешованому worktree вbuild/bench-base/<hash>, що ділитьthirdparty/. - Звіт: для кожного ядра медіана запусків
--repeat(типово 3) у мілісекундах із шістьма десятковими знаками та розкид, зміна Clause O2 проти кожної системи (+N%: Clause швидший, інша система витрачає на N% більше часу;-N%: Clause витрачає на N% більше;(Fx)— відношення часу) з геометричним середнім,--ltoпроти OTP й O2, повний час програм зі стартом, розміри виконуваних файлів, помилки й тайм-аути (--timeout, 600 с на запуск). Типовий запуск вимірює приблизно 15 хвилин. - Розміри виконуваних файлів у байтах: кожна програма з
-O2, з--ltoта з-O0(додаткова збірка лише вимірюється, ніколи не запускається; обидві пропускаються за--no-lto) і базова версія з-O2, з підсумковим рядком і змінами O2 проти O0,--ltoпроти O2,--ltoпроти O0 й O2 проти базової версії (-N%— менше); а такожclauна HEAD і базовій версії. Історія зберігає їх якsizes_bytes(programs: програма → стовпець → байти,clau:head/baseline→ байти;null, якщо збірка не вдалася). - Історія:
benchmarks/history/<host>.jsonзберігає один запис на день запуску (дату запуску, хеш HEAD і дату коміту, медіани Clause, OTP і базової версії, розміри й відношення часу Clauseotp_ratioтаbaseline.ratioдля кожного ядра),benchmarks/history/reports/<host>/<day>-<hash>.txt— текстові звіти; повторний запуск того самого дня замінює обидва. Записи й звіти запусків більш ніж за 183 дні до найновішого видаляються; запуск із незакоміченими змінами джерел потрапляє до звіту, але не записується. Таблиці історії звіту показують відношення за останні 8 днів запусків, які залишаються порівнянними, коли ядра змінюють обсяг роботи; записи до масштабування на 1–2 с зберігають лише відношення. Каталог історії ігнорується Git: він залишається на хості запуску. - Лістинги: з
--listingsперед вимірюванням запуск записує лістинги--print-asmі--print-irкожної програми (зbench_util,--entry <program>, тож недосяжний бібліотечний код пропускається, як у виконуваному файлі) поруч зі звітами як<program>.debug.asm/<program>.debug.ll(-O0) і<program>.release.asm/<program>.release.ll(-O2, код, який виконує стовпецьclause-o2), замінюючи лістинги попереднього запуску, зокрема з--no-historyі незакоміченими змінами.--print-ir— це IR до конвеєра LLVM; коментарі асемблера цитують IR після нього. Програма, яка не компілюється, втрачає свої застарілі лістинги.
Історія
Стиснуто з колишніх записів для окремих кроків (оригінали в історії Git до
коміту 2777c98). Якщо не зазначено інше: Windows x64, LLVM 23.1.2, прив'язка
21776803ecd1, еталонний OTP 29.1.1 / ERTS 17.1. Кількості тестів — це повні
проходження CTest з нулем пропусків.
| Дата | Віха | Тести | Одиниці якості | Примітки |
|---|---|---|---|---|
| 2026-09-19 | Синтаксичний аналізатор, фаза VI (macOS arm64, OTP 29.1 751f87b7, еталон 29.0.5) | 46 | усі | 344/344 продукцій засвідчено; корпус із 10 файлів |
| 2026-09-20 | Проєкти (macOS arm64) | 64 | усі | Debug, лише компілятор, ASan+UBSan |
| 2026-09-28 | Міграція тестів на робочі процеси CLI; відновлення перевірки на Windows | 75 | усі | Переповнення стека в parser_hardening виправлено стеком 8 MiB; Lizard + clang-tidy без зауважень |
| 2026-09-29 | Віха компілятора, кроки 1–46 | 103 | 182 | Лише компілятор 80, лише runtime 16, runtime ASan 16 |
| 2026-10-01 – 10-03 | План зразків/guards PG1–PG20 | 104–131 | 189–258 | Ревізії ABI 2–4; PG15a зробив еталони незалежними від OTP (аудити опційні, 131 → 118 тестів); закрито на 19 корпусах, 67,634 значеннях, 1,969 засіяних результатах |
| 2026-10-03 | Аудит джерел OTP | 125 | 258 | Скопійовані файли OTP замінено локальними фрагментами; порівняння вікнами з 60 токенів із 4,150 файлами OTP на Erlang і 1,190 файлами C/C++ не знайшло збігів, крім згенерованого кортежу цілих чисел |
| 2026-10-03 | Базовий рівень plan 11 step 1 (коміт b1a471f, прив'язка 21776803ecd1, Lizard 1.24.0, clang-tidy 22.1.8) | 125 (122 швидких) | 258 | Повний 729 с послідовно, 85 с з -j 16; аудити 14/14; корпуси 19/19; інші об'єктні файли O0/O2 перевірено для 7 цілей |
| 2026-10-03 | Steps 2, 4: фікстури програм, друк термів | 126–128 | 265 | Шість еталонів програм OTP; 9,542 еталони ~w/display |
| 2026-10-04 | Steps 8, 8I: засіб запуску виконуваних файлів, класична купа (phases B, C закрито) | 138–144 | 272–276 | Повний -j 16 235–370 с |
| 2026-10-04 – 10-05 | Steps 9–20: case, if, генерування винятків, catch, try, трасування стека, maybe (phase D закрито), рекурсія, фрейми та хвостові виклики | 146–158 | 277–278 | Швидкі й зачеплені тести повного режиму на кожному кроці; повний -j 16 155/155 за 259 с на phase D |
| 2026-10-06 | Steps 21–28: comprehensions (phase E закрито), корені та збирання, обмеження пам'яті й термів (27A–27E), копіювання графів | 159–166 | 278–281 | Повний -j 12 105–275 с; step 27 прибрав типові обмеження пам'яті |
| 2026-10-07 | Steps 29–35: records, native records (ABI 6), funs (ABI 7), замикання, іменовані funs, динамічні виклики (ABI 8) | 167–177 | 283–290 | Повний -j 12 119–152 с |
| 2026-10-09 | Step 63 Linux x86-64 (WSL2 Ubuntu 25.04, glibc 2.41, LLVM 23.1.2 SDK clang, libstdc++ 14, GDB 16.2) | 235 | 338 | Повний CTest 235/235 за 161 с; Lizard і clang-tidy без зауважень у Linux (7 звітів аналізатора в заголовках Boost проігноровано); перевірка Windows 235/235 з перевіреними заголовками |
| 2026-10-09 | Step 64, 32-бітні: Windows x86 (WoW64, clang-cl) і Linux i386 (chroot Debian 13, clang 19) | 42 + 65 еталонів | — | Тести runtime/ABI 42/42 нативно на обох; еталони cross.py 65/65 на обох (запуски integer_limit лише для 64 біт пропущено) |
| 2026-10-09 | Step 65, Linux arm64 і armhf (sysroot Debian 13, qemu-user 9.2) | 42 + 65 еталонів | — | Тести runtime/ABI 42/42 на кожній; еталони 64/65 на кожній (port_spawn enoent: прогалина posix_spawn у qemu), обмеження запуску 900 с |
| 2026-10-10 | Step 67, ASan + UBSan + LeakSanitizer (Linux x86-64) | 235 | — | Жодного звіту санітайзера; чотири тести з упровадженою відмовою хоста поза межами ASan; CLAUSE_TEST_TIMEOUT_SCALE=10 |
| 2026-10-10 | Step 68, ThreadSanitizer (Linux x86-64) | 233 | — | Виправлено одну інверсію порядку блокувань (Executor::clear); далі жодного звіту в повному CTest і 15 тестах паралельності x5 |
| 2026-10-10 | Steps 65A–65C: behaviours, module_info/0,1, імпорти | 234–236 швидких | 342–344 | Еталони lint і виконуваних файлів відтворюються під OTP 29.1.1 |
| 2026-10-11 | Steps 73A–73G, parse transforms | 242/243 швидких | — | Відомий збій codegen_resource_cli, згодом вимкнений |
| 2026-10-12 | Steps 62D1–62E, каталог збірки та кеші | 263/264 | усі | Повний CTest, відомий збій codegen_resource_cli; check-quality-all без зауважень |
| 2026-10-13 | Steps 58U–58W, кеш виведення типів | 278/279 | усі | Повний CTest, відомий збій codegen_measurements; check-quality-all без зауважень |
| 2026-10-14 | Step 41S, prim_eval і повторний запуск luerl (H3 закрито) | 299/300 | усі | Див. поточний базовий рівень |
| 2026-10-17 | Plan 20 OPTM.2 (однослівний Term, persistent terms на місці та звільнення їхньої пам’яті) і OPTM.3 (неперевірювані методи доступу) | 330/332 | усі | Повний CTest: відома помилка codegen_measurements, а library_freshness (хостова OTP) досі очікує тіла заглушок not_implemented, замінені в b0e62a38; check-quality-all без зауважень |
| 2026-10-19 | Plan 21 LIB10F ThreadSanitizer (Linux x86-64): еталони ETS і digraph, runtime_concurrency | 10 | — | Жодного звіту; ets_portions на одному робочому потоці виявив звільнення під час видалення у двох порціях; виправлено обліком звільненого об’єкта як обійденого |
PG = крок плану зразків/guards; записи кроків plan 11 архівовано в
.agents/00-finished.md.
Clause