flang компилятор доказывает, что программа не зациклится 0.7.24 GitHub

Самоприменение: компилятор flang, написанный на flang

Каталог flang/self/ — исходники компилятора языка, написанные на самом языке. Двоичный компилятор собирается не из них напрямую, а из их печати в C99, закоммиченной в bootstrap/ (точка раскрутки): make -C bootstrap требует только cc и make. Второй реализации компилятора в дереве нет: реализация на JavaScript снята 20 августа 2026 (коммит fe8e8a37), и сверять слои теперь есть с чем ровно в одном смысле — с их же печатью, см. «Критерий готовности».

Компилятору тотальность не обязательна: он вправе упереться в предел шагов и сказать об этом. Поэтому в flang/self/ разрешены обычные функции (функция, а не тотальная функция); каждая такая функция — названный долг слоя, см. «Долги».

Состав каталога

64 файла .flang, 117 666 строк Имя модуля — первая строка файла; здесь оно повторено, чтобы читать использует «…» в других файлах без поиска.

Ядро компилятора

файлмодульчто делает
lexer.flang«Лексер flang»текст → токены; отступы значимы
parser.flang«Парсер flang»токены → AST (docs/flang/SPEC.md, раздел 5); словарь ввода-вывода (суммы «Поручение», «Отклик», «Продолжение») выписан здесь данными
types.flang«Проверка типов»проверка и вывод типов, исчерпывающность разбора, типизация постусловий
totality.flang«Тотальность flang»анализ завершаемости: часть значения, числовая мера, точный шаг, объявленная мера; отметка мер («Отметить меры»)
defunc.flang«Дефункционализация»снятие функций первого класса перед печатью; сторожа меры
carriers.flang«Носители обещания»чем несётся обещание завершения каждой функции
link.flang«Связывание flang»связывание модулей по использует в одну плоскую программу («Связать исходники»), отбрасывание недостижимого
builtins.flang«Встроенные формы flang»встроенные формы языка, общие для всех целей
tags.flang«Теги программы»теги функций как значений
declared-properties.flang«Объявленные свойства»общая основа пяти законов (commutative, distributive, idempotent, monotone, partialorder): как берётся сетка значений, её предел, имя значения в тексте отказа. Всё здесь считается на конечной сетке и доказательством не является; устройство свойства (объявлена ли функция, сходятся ли типы) доказано раньше сличением объявлений
cli.flang«Командная строка flang»разбор доводов команды flang (перечень команд — flang --help); ключ согласия --на-веру/--trust у run и io (ADR-0045)

Печать в цели

файлмодуль
emit-c.flang«Печать в C»
emit-cpp.flang«Печать в C++»
emit-go.flang«Печать в Go»
emit-rust.flang«Печать в Rust»
emit-java.flang«Печать в Java»
emit-js.flang«Печать в JavaScript»
emit-elixir.flang«Печать в Elixir»
emit-python.flang«Печать в Python»
emit-csharp.flang«Печать в C#»

Цели те же, что принимает flang emit --target: c|cpp|go|rust|java|js|ts|elixir|python|csharp.

Доказательства

файлмодульчто делает
obligations.flang«Обязательства программы»постусловие и теорема → обязательство: что именно надо доказать, данными
proof-kernel.flang«Сведение к допущениям»ядро: сведение цели к допущениям; аксиом ноль
proof-initial.flang«Начальная алгебра»принцип индукции читается с объявления суммы и строится термом
proofterm.flang«Проверяльщик термов»свёртка терма доказательства в вердикт
proof.flang«Ведомость доказательства»отчёт flang check --proof; в сборку компилятора как слой печати не входит; обычных функций в нём быть не должно (проверка: grep -c '^функция' flang/self/proof.flang → 0)
proof-record.flang«Запись доказательства»запись доказательства для независимого чекера (docs/flang/proof/checker/README.md)
obligations-and-kernel-examples.flang«Обязательства и ядро под примерами»свои примеры слоя обязательств и ядра: flang test считает примеры ввезённых модулей вместе со своими, и слой без единого своего примера выглядит проверенным; ядро здесь спрашивается о цели обязательства напрямую, без подстановки тела по случаям, то есть меньше, чем делает flang check --proof
type-refusals.flang, totality-refusals.flang«Отказы типов», «Отказы завершаемости»отказы проверки типов и анализа завершаемости под примерами: отказ приходит значением с полем «код», и его сверяет обычный пример. Два файла, потому что types.flang и totality.flang ввозят из emit-c.flang разные наборы имён, а связывание отдаёт второму слою список первого

Вычисление, инструменты, службы

файлмодульчто делает
interpret.flang«Вычислитель flang»вычисление AST явным стеком: машина «кадр → значение»; формы вне среза отвечают кодом FLANG_SELF_EVAL_UNSUPPORTED, а не молчат
io.flang«Проверка планов»проверка объявления план (flang io)
repl/repl.flang«Оболочка flang»flang без доводов и flang repl
lsp.flang«Языковой сервер flang»flang lsp --stdio
mcp.flang«Служба MCP»flang --mcp-mode
hotswap.flang«Горячая замена»замена кода работающего процесса
factcheck.flang«Факт-чекинг»flang facts
bounded.flang«Ограниченность»ограниченность по шагам и памяти
bootstrap/corpus.flang«Корпус flang»прогон примеров корпуса
bootstrap/compiler.flang«Compiler flang»точка входа: связать и напечатать, связать и проверить, напечатать связанный AST; связать и запустить с вердиктом о доказанности — «Запуск исходников» (flang run) и «Поиск плана исходников» (flang io) до вычисления печатают «доказано: утверждений N» либо «не доказано: …» о всём замыкании и без --на-веру недоказанную программу не запускают, код 3 (ADR-0045)

Процессы

файлмодуль
conc.flang«Планировщик конкурентности»
processes.flang«Проверка процессов»
failures.flang«Множество отказов процесса»
distributed.flang«Узел flang»

Контракт этого слоя — docs/flang/concurrency/SPEC.md.

Законы и категории

файлмодуль
setoid.flang, setoid-oracle.flang«Законы категории», «Оракул категорий»
functor.flang, functor-oracle.flang«Законы функтора», «Оракул функтора»
monoid.flang«Законы моноида»
monad.flang, monad-expand.flang«Монада», «Разворачивание монад»
iso.flang«Законы изоморфизма»
sets.flang, sets-oracle.flang«Отношения множеств», «Оракул множеств»
commutative.flang, distributive.flang, idempotent.flang, monotone.flang, partialorder.flangзаконы коммутативности, дистрибутивности, идемпотентности, монотонности, частичного порядка
oracle.flang, law-oracle.flang«Оракул свойств», «Оракул законов»
grid.flang«Поиск нарушений на сетке»

Контракт этого слоя — docs/ct/spec.md. Законы считаются на сетке с названным числом значений; это счёт, а не доказательство, и отчёт говорит об этом словами.

Точка входа — bootstrap/compiler.flang

Компилятор целиком — один модуль «Compiler flang», связывающий слои в одну программу. Порядок использует в нём — требование, а не оформление: связывание сливает объявления в плоское пространство имён, и что видно из файла, решает первый импорт этого файла в обходе; поэтому parser.flang идёт первым, печать в цели — целиком, link.flang — последним (довод — в комментарии шапки файла).

Чтения файлов в языке нет. Исходники приезжают списком записей «Исходник» (путь и текст), и использует … из "…" разрешается по этому списку, а не по файловой системе. Ввод-вывод — у хозяина: двоичного flang (bootstrap/flang_cli.c).

AST — контракт между слоями

Форма AST задана в docs/flang/SPEC.md, раздел 5, и другой нет. Печатает её flang ast <файл>; печать в JSON — модуль «Печать JSON» (flang/core/json.flang). Если форма и печать расходятся, расширяется печать, а не заводится вторая.

Критерий готовности — неподвижная точка

0. bootstrap/ (в дереве)                → cc, make → flang₁
1. flang₁ печатает flang/self/…         → C → должен совпасть с bootstrap/ байт в байт

Совпадение означает, что компилятор воспроизводит сам себя. Сверяет:

make -C bootstrap                        # ярлык: bootstrap/flang run-script build
sh scripts/bootstrap-reprint.sh --check  # ярлык: bootstrap/flang run-script reprint:check

Проверка печатает файлы заново и сравнивает с закоммиченными; расхождение называется файлом, байтом и строкой. Она краснеет на всех четырёх видах расхождения: правка исходника без перепечатки, правка bootstrap/ руками, пропавший файл печати, лишний файл от прошлой печати. Что печатает сам двоичный, а не что-то другое, стережёт bootstrap/flang run-script bootstrap-point:check (scripts/bootstrap-point-by-binary.flang).

Пределы печати (MAX_STEPS, MAX_DEPTH) записаны один раз — в scripts/bootstrap-reprint.sh — и попадают в напечатанный байт (FL_MAX_DEPTH в bootstrap/flang_runtime.h), то есть участвуют в совпадении. Печать с другими пределами разойдётся. Довод по величине пределов — в шапке scripts/bootstrap-reprint.sh; устройство круга для читателя — docs/guide/bootstrap-circle.ru.md.

В CI сверку выполняет .github/workflows/reprint.yml.

Порядок обновления точки раскрутки

  1. Правка любого файла, входящего в замыкание печати bootstrap/compiler.flang (файлы flang/self/, flang/core/json.flang и модули flang/stdlib/, которые они используют).
  2. sh scripts/bootstrap-reprint.sh тем же коммитом — перепечатать bootstrap/.
  3. sh scripts/bootstrap-reprint.sh --check отвечает 0.

Править bootstrap/ руками нельзя: правка потеряется при первой перепечатке, а до того валит сверку. Устройство каталога — docs/bootstrap-point.md.

Долги

Долг слоя — каждая функция, объявленная функция, а не тотальная функция. Счёт по файлу и по каталогу:

grep -c '^функция' flang/self/<файл>.flang
git ls-files 'flang/self/*.flang' | xargs grep -c '^функция'

Причины, по которым обычная функция допускается, две, и любая другая — ошибка:

Слои, где обычных функций быть не должно и сегодня нет: proof.flang, lexer.flang, builtins.flang, cli.flang, factcheck.flang, hotswap.flang, bootstrap/corpus.flang (проверка — команда выше; появление обычной функции в любом из них — регресс, а не долг).

Долги вычислителя: формы, которые interpret.flang не вычисляет, отвечают кодом FLANG_SELF_EVAL_UNSUPPORTED; молчаливого пропуска нет по построению.