Словарь языка
Термины kola на одной странице — в том значении, в каком их используют сообщения компилятора и обе карты: SPEC-CARD.md для языка и STD-CARD.md для стандартной библиотеки (kola help spec-card, kola help std-card).
Значения и типы
данные (data): простые копируемые значения, внутри которых нет ничего владеющего: целые числа,
bool,(),char,f64, ссылки, а также составленные из них структуры, перечисления и кортежи. Копируются свободно и сравниваются через==по значению, в том числе через ссылки (по содержимому, никогда по адресу).владеющее значение (owning value): значение, которое перемещается и никогда не копируется: владеющий
Vec[T](Vecпо значению),Map, значение, содержащее&mut, и всё, что несёт что-то из этого в поле или в полезной нагрузке. Передача по значению его перемещает, и читать источник после этого — ошибка. Вместо этого его заимствуют через&vили&mut v.ресурс (resource): линейное значение, объявленное через
resource struct: файл, сокет, дескриптор потока, капабилити. Его нельзя скопировать и нельзя молча потерять. Многие ресурсы оборачивают дескриптор операционной системы, например файловый дескриптор (небольшое целое число, которым ОС обозначает открытый файл или сокет), и закрыть его нужно ровно один раз.линейность (linearity): правило, по которому ресурс расходуется ровно один раз на каждом пути выполнения. Забыть о нём — ошибка компиляции (
resource `t` is not consumed on this return path), израсходовать дважды — тоже.расходование (consumption): способы завершить жизнь ресурса: передать его функции, принимающей его по значению (
self: T), вернуть, переместить дальше или разобрать.разбор (destructuring): разбиение значения на поля с помощью образца, как в
let Wal { entries: _ } = self;. Для ресурса так расходующая функция заканчивает значение. Каждое поле должно быть названо;..нет.перемещение (move): передача значения новому владельцу. Любая позиция, типизированная по значению, перемещает владеющее значение или ресурс: аргумент вызова,
let, поле в литерале структуры, полезная нагрузка варианта вродеSome(v), сообщение в канал, контекст потока, присваивание в поле, возврат. Данные в таких позициях копируются.unit
(): тип с единственным значением, которое тоже пишется(). Его возвращает функция без-> T.литерал (literal): значение, записанное прямо в исходнике:
42,0x2A,0o644,0b1010,1.5,"text",f"n={n}",b"bytes",r"raw",[1u8, 2u8],Point { x: 1, y: 2 }._между цифрами — разделитель. Символьного литерала нет.суффикс типа (type suffix):
3u64— это число 3 типаu64. Литерал без суффикса берёт ширину из контекста (аннотация, параметр, другой операнд, позиция индекса, где этоusize). Если ширину ничто не задаёт, этоu64, илиi64, если туда когда-нибудь попадает отрицательное значение.widen / narrow / wrap: единственные преобразования между числовыми типами, все явные.
widen[u64](x)расширяет без потерь,narrow[u8](x)возвращаетOption[u8],wrap[i16](x)переосмысливает младшие биты и никогда не вызывает трап.asнет.срез (slice): представление массива или
Vec, тип&[u64]или&r [u8]. У него есть.len, индексацияxs[i]и подсрезыxs[a..b]. Ничего не копируется.правило представлений (view rule): пока представление места (
&v[i], подсрез, обходvцикломfor) ещё используется, это место нельзя записывать, увеличивать, уменьшать или перемещать ни под каким другим именем. Исправление: сначала закончить чтение, скопировать значение или сохранить индекс и прочитать заново после изменения.str: представление текста в UTF-8 в виде пары (указатель, длина)..len— длина в байтах,.bytes— то же представление как&[u8]. Обычный литерал"..."— статические данные и подходит любому региону.подстановка (interpolation): литерал
f"..."подставляет выражения в текст:f"n={n} sum={a + b}". Обычный"..."ничего не подставляет. Привязанный к имени f-литерал выделяет память: в регионе места назначения, если тип назначения его называет, иначе в ближайшем открытомregion, а если нет и его, компилятор литерал отвергает. Переданный прямо в параметр&strна время вызова, он ничего не выделяет.дженерик (generic): параметр в квадратных скобках. Имена с заглавной буквы обозначают типы, со строчной — регионы:
fn first[T: data](xs: &[T]) -> Option[T]. НеограниченныйTсчитается линейным;T: dataделает его копируемым;T: moveдопускает данные и владеющие значения, но не ресурсы. Параметр[const N: usize]связывает число времени компиляции, которое можно использовать как длину массива. Угловых скобок нет.мономорфизация (monomorphization): компиляция дженерика созданием отдельной специализированной копии для каждого набора типовых аргументов — примерно так, как если бы вы вручную написали
first_u64иfirst_u8. Каждый вызов идёт прямо к известной функции; никакого поиска нужной функции во время выполнения, как с виртуальными методами C++, нет.fn-ссылка (fn reference): имя функции верхнего уровня, использованное как значение, как в
seq::sort(xs, less_u64). В её тип входит ряд эффектов:fn(u64) -> u64 !{}. Она ничего не захватывает, потому что замыканий нет.тип уровня модуля (module-scoped type):
structилиenumпринадлежит своему модулю, как функция. Внутри модуля тип называют просто по имени; снаружи — черезuse M::T;и затемT, либоM::Tв позиции типа.
Память
арена (arena): блок памяти, который раздаёт выделения по порядку, сдвигая указатель за каждое из них, и освобождается целиком. Поэтому выделение очень дёшево. По отдельности в арене ничего не освобождается.
регион (region): лексический блок
region r { ... }со своей ареной. Всё, что в нём выделено, освобождается на закрывающей скобке.region r from m { ... }берёт память для арены из бюджетаm.alloc in: выделение в регионе:
alloc in r mut Vec::new()даёт&r mut Vec[T]. Изменяемость выбирается там, где значение выделяется.heap: встроенный регион, который живёт столько же, сколько программа. Сборщика мусора нет; память
heapвозвращается, когда процесс завершается. Обычный строковый литерал — статические данные и может иметь тип&heap str. Хранилище владеющегоVecвсегда лежит вheap, поэтому его рост — эффектalloc(heap).ссылка в регион
&r T: ссылка, в тип которой входит её регион. Её можно свободно копировать, но она не может пережить регион, и это проверяется при компиляции.ссылка на время вызова
&T/&mut T: ссылка, которая бывает только типом параметра. Вызываемая функция пользуется ею, пока идёт вызов, но не может её сохранить или вернуть. Это форма параметра по умолчанию.параметр-регион (region parameter): имя со строчной буквы в квадратных скобках, как в
fn body[r](b: &r [u8]) -> &r [u8]: функция работает с тем регионом, который есть у вызывающего. При вызове регион не пишут, он выводится.lend: разделяемое представление ресурса на время региона:
let v: &r Wal = lend wal in r;. Ресурс заморожен, пока регион не закроется.lend mutнет.Pool[T]: слэб (фиксированный массив ячеек одного размера) для долгоживущих данных со структурой графа, где ячейки можно освобождать по одной, чего регион не умеет. Ёмкость резервируется вnewи целиком списывается с бюджета, но страницы памяти затрагиваются, только когда ячейка используется впервые.insertпри заполненном пуле вызывает трапTrapKind::Capacity;try_insertвместо этого возвращает элемент обратно.drain_nextизвлекает произвольную занятую ячейку при разборе пула.Poolне может пересечь границу потока.поколенческий дескриптор
Gen[T]: квиток, который возвращаетinsert. Он отвечает на два вопроса: та ли это ещё ячейка, которую вам дали (послеremoveстарый дескриптор даётNone, даже если ячейка занята снова), и из этого ли пула вообще дескриптор (дескриптор, использованный с другим пулом, детерминированно промахивается).владеющий
Vec:Vec::new(), привязанный по значению, с хранилищем вheap. Его буфер переходит между потоками без копирования. Если память должна вернуться на скобке,Vecвыделяют в регионе.
Эффекты и капабилити
эффект (effect): какого рода действия функция может совершать. Их пять:
alloc(r)(выделяет память в регионеr),io(обращается к внешнему миру через капабилити),hw(привилегированная или чувствительная к порядку машинная операция),block(может заблокироваться или уснуть) иlog(пишет в журнал процесса). Эффекты говорят, что за действие; капабилити — над чем.ряд эффектов (effect row):
!{...}в конце сигнатуры, после типа результата:fn f(x: &T) -> u32 !{io}. Каждая публичная функция его указывает; закрытая может оставить его на вывод.чистая функция (pure function): функция с рядом
!{}: доказуемо не выделяет память, не выполняет ввода-вывода и не блокируется.hw: машинный эффект — для операций, которые ядро или драйвер выполняют над самим железом: маскирование прерываний, обслуживание кэшей и TLB (кэша процессора для трансляции адресов), барьеры памяти (инструкции, не дающие процессору переставлять обращения к памяти через них), MMIO и атомики. Вct fnон запрещён, а сами операции никогда не переставляются и не удаляются. Обычные чтение и запись черезRamWindowостаются подio.капабилити (capability): значение, дающее доступ к одной части мира. Полномочий из воздуха нет, поэтому каждая библиотечная операция с эффектом принимает капабилити. Маркеры полномочий вроде
Clock,Rng,Fs,Dir,Dial,Stdoutможно просто бросить.Memи линейные дескрипторы вродеFile,Conn,Childнужно израсходовать.Sys: корневой набор, который среда выполнения передаёт вmainи больше никуда:sys.clock,sys.rng,sys.mem,sys.fs,sys.dial,sys.listen,sys.stdout,sys.stdin,sys.stderr,sys.args,sys.env,sys.tty,sys.ping,sys.proc,sys.netlink,sys.raw_ip,sys.mmio,sys.cpu,sys.dev,sys.windowиsys.inherited.mainпринимает его по значению или не принимает ничего.полномочия из воздуха (ambient authority): власть, доступная откуда угодно, — глобальный
print, файловая система или часы. В kola их нет.ослабление (attenuation): сужение полномочий при передаче вниз по вызовам.
mainдержит всё и передаёт помощнику только&sys.clock. Часть сужения выражена в типах:dir.ro()даётDirRoбез операций записи,w.atomic()оставляет окну только атомарные операции, аdial.to_host(host, port)достаёт до одного адреса. Капабилити перемещается один раз, поэтомуclock.split(),rng.split()иdial.split()выдают рабочему потоку его собственную.выдача (grant): полномочия за пределами рабочего каталога и самого процесса (каталог, бинарник, устройство, аппаратное окно), которые выдаёт по имени тот, кто запускает программу.
kola run --fs-root db:ro=/absвыдаёт, аsys.fs.root(b"db")ищет, возвращаяNone, если ничего не выдано. У каждого флага есть двойник в окружении, напримерKOLA_FS_ROOT_db. Выдачи исходят от оператора и никогда — от родительского процесса.log: единственный эффект, которому не нужна капабилити, потому что приёмник работает только на запись: программа не может прочитать написанное и, значит, не может через него управлять ходом выполнения. Приёмник и уровень выбираются один раз — флагами--logи--log-levelили переменнымиKOLA_LOGиKOLA_LOG_LEVEL. Функции —std::log::{error, warn, info, debug}.бюджет памяти (memory budget): часть капабилити
Mem, отрезанная черезsys.mem.split(n). Она питает регион (region r from m), а линейность заставляет вернуть её черезsys.mem.absorb(m). Превышение бюджета вызывает трапTrapKind::Budget.окно (window): выданный диапазон памяти или адресного пространства с длиной и непрозрачной базой. kola работает со смещениями, а не с указателями.
RamWindow— память, общая с железом, например кольцо дескрипторов буферов, которое читает сетевая карта.MmioWindow— регистры устройства, отображённые в память (MMIO, memory-mapped I/O: чтение или запись по такому адресу обращается к устройству, а не к RAM).IoWindow— портовый ввод-вывод x86, старое отдельное адресное пространство для устройств. У каждого обращения есть контракт границ, и прувер может снять его так же, как для индекса массива.модель устройства (device model): при обычном
runилиtestза окном или выданным устройством нет настоящего железа, поэтому его поведение задаёт модель на kola, подключаемая через--sim-mmio,--sim-ioили--sim-dev. Выдача без модели отвергает тест при запуске.Proc/Child:sys.proc.spawn(bin, argv, env, disp, slots)запускает бинарник, выданный оператором по имени (--proc-bin ffmpeg=/abs).envзадаётся явно, так что ничего неявного в дочерний процесс не попадает.disp: Stdio— этоInherit,PipedилиNull.Childлинейный: его расходуетwaitилиkill.ExitStatus— этоCode(u8)илиSignaled(Signal), и сигнал — это имя, никогда не число.
Ошибки и контракты
трап (trap): детерминированная остановка, когда нарушено допущение: переполнение, выход за границы, ложный контракт,
unwrapотNone, явныйtrap("msg"). Никакой раскрутки стека и никакого продолжения с мусором.домен отказа (fault domain): то, что убивает трап. По умолчанию — весь процесс (в тесте — тест). Блок
domain d { ... } recover(t: Trap) { ... }сужает его до блока: трап в теле разбираетdи запускаетrecover. Домен — это регион, который ещё и перехватывает трап. Трап внутриrecoverфатален.Trap: простые данные, которые получаетrecover.t.kind— одно изBounds,Arith,Req,Ens,Inv,Assert,Unwrap,Explicit,BudgetилиCapacity, аt.site—module:line, никогда не путь в файловой системе.контракт (contract): проверяемое условие в сигнатуре. Соблюдается всегда: либо доказывается при компиляции, и тогда проверка исчезает, либо проверяется во время выполнения с трапом. Режима сборки, который его убирает, нет.
req: предусловие, обязанность вызывающего:req i < xs.len.ens: постусловие, обещание функции;retобозначает результат:ens ret <= 100.inv: инвариант структуры-ресурса, записываемый последним элементом её тела:resource struct Ring { head: usize, inv head < 8 }. Проверяется при создании и на границах функций.assert: проверяемое утверждение внутри тела, с тем же правилом снятия, что и у контрактов. Его выражение ничего не должно менять.Option/Result: ожидаемые исходы в виде значений.Option[T]— есть или нет;Result[T, E]— успех или ожидаемая ошибка.?: досрочный возврат. ДостаётvизOk(v), а приErrвозвращает ошибку из текущей функции. Живой ресурс в точке?— ошибка линейности.отброшенный
Result: ошибка компиляции. Его обрабатывают черезmatch,?или.unwrap()либо отбрасывают намеренно:let _ = ...;.unwrap: достаёт значение изOptionилиResultи вызывает трап наNoneилиErr. Перемещает своего получателя.соглашение о результатах ОС: операция, обращающаяся к ОС, отвечает
Result[T, (E, OsErr)], гдеE— семейство действия (FsErr,NetErr,IoErr,DevErr,ProcErr,NetlinkErr), аOsErr— собственное обозначение ошибки в ОС, напримерNoEnt. «Пока ничего нет» никогда не ошибка: операцияtry_*отвечаетOk(None).layout_assert: элемент уровня модуля,layout_assert[Point](8, 4);, утверждающий размер и выравнивание типа в C. C-бэкенд превращает его в_Static_assert; интерпретатор считает его пустой операцией.size_ofв kola нет.
Данные, которым нельзя доверять, и данные, которые нельзя раскрыть
untrusted: маркер параметра для входа от атакующего: байтов из сети, файла, стандартного ввода или от другого процесса. От него не может зависеть ниreq, ниens, ниinv, потому что контракт — утверждение о вызывающем, а у этих байтов вызывающего нет. Значение проверяют во время выполнения и возвращаютResultилиOption.kola report tainted-reqsнаходит кандидатов в существующем коде.Secret[T]: непрозрачная линейная коробка для данных, которые нельзя раскрыть.Secret::wrap(x)кладёт значение в коробку,Secret::expose(&s)— единственный способ прочитать,Secret::wipe(s)расходует и затирает,Secret::eq(&a, &b)сравнивает за константное время, аSecret::wipe_bytes(&mut buf)затирает буфер, коробкой которого вы не владеете. Секрет длины, известной только во время выполнения, — этоSecret[Vec[u8]]. Ссылку в коробку положить нельзя: затёртый указатель оставил бы байты за ним нетронутыми.Entropy[[u8; N]]: коробка, которая помнит происхождение байтов: энтропия ОС, непредсказуемая для противника.Entropy::draw[[u8; N]](&mut rng)создаёт её,Entropy::expose(&e)читает.Entropy::of(x)для тестовых векторовkola auditотвергает на любом пути, который начинается не вtest. Она не линейна и не являетсяSecret; ключу может понадобиться и то и другое.ct fn: функция константного времени. Время её работы и порядок обращений к памяти могут зависеть от формы параметров (ширины, длины), но не от их значений. Для криптографии это важно: если сравнение пароля или ключа завершается быстрее, когда неверен первый байт, атакующий, способный засекать время, восстановит секрет байт за байтом. Внутри такой функции каждый параметр секретен, если не помеченpub, и чекер отвергает над производными от секрета значениями ветвление, индексацию, деление, арифметику с трапом и секретную величину сдвига, а также вызовы функций безct. См. Константное время.
Инструменты и проект
SPEC-CARD: весь язык на одной карте и источник истины. Диагностики ссылаются на её разделы, как в
error[card §8]. Чего нет на карте, того нет в языке. Её спутник, STD-CARD, описывает стандартную библиотеку: типы, операции капабилити и SIMD.golden: каталог
golden/в репозитории с эталонными примерами программ — от hello world до песочницы и диспетчера выходов виртуальной машины. Все они компилируются и проходят тесты.kola: компилятор, один бинарник со встроенной стандартной библиотекой. Команды:check,run,test,build,ir,report,query,audit,pack,security,cc,lsp,mcpиhelp. См. Под капотом.интерпретатор (interpreter): исполнитель за
kola runиkola testи эталон, с которым сравнивается каждый бэкенд. Работает вместе с JIT;--no-jitдаёт чистый обход дерева.дифференциальный тест (differential test):
kola testзапускает каждый тест в интерпретаторе и через C-бэкенд и сравнивает ответы; расхождение даёт код выхода 3.--interpили--nativeзапускают только одну сторону.KIR: проверенное промежуточное представление, которое потребляет каждый бэкенд; в нём записано решение по каждой проверке. Его печатает
kola ir. См. IR-0.report checks: список всех обязательств в файле с пометкой, доказано ли каждое при компиляции или оставлено проверкой времени выполнения. «0 runtime» — планка для кода уровня ядра. Родственные отчёты —reachable,leaks,regions,effects,secrets,tainted-reqsиstack-frames; полный список даётkola help report.прогон с зерном (seeded run): прогон, в котором весь недетерминизм (случайность, время, порядок ввода-вывода, планирование потоков) выводится из одного числа.
kola test --sim --seed Nповторяет тот же прогон бит в бит.симулятор (
--sim): капабилити, подменённые детерминированными двойниками: виртуальные часы, источник случайности от зерна, симулированные файловая система и сеть с внесением сбоев, пустое окружение. УProcдвойника нет; выданный запуск процесса действительно выполняется.kola.pack: манифест пакета: имя, версия, корень исходников, зависимости и бюджет капабилити[caps] allowedс поимёнными списками (fs_roots,proc_bins,devices,dial_hosts,netlink, списки окон и другие).[caps.tests]и[caps.examples]расширяют его только для тестов и примеров.kola auditзавершается ошибкой, если код выходит за бюджет. Бюджет ограничивает то, что пакет может взять из корневого набора; полномочия, полученные параметром, перечисляются отдельно, в строкеreaches:отчёта аудита.журнал путаницы (confusion log):
docs/confusion-log.mdв репозитории — запись конструкций, в которых люди или модели устойчиво ошибаются. Конструкция, которая сбивает с толку, считается дефектом самой конструкции или карты.