Перейти к содержанию

M0 — изменяемое заимствование структур

Первый реализованный срез M0: вызов может получить исключительное право изменять существующую копируемую структуру. Владение не передаётся; после возврата владелец видит изменения. Ресурсные структуры, ссылки-значения и многопоточность не входят в этот срез. Контракт M0 проектировался отдельно от A3 — оптимизации функций с прежней семантикой значений.

struct Counter {value:u32}
fn increment(borrow mut state:Counter)->u32 {
    state.value=state.value+1
    return state.value
}
fn main()->u32 {
    var state=Counter(7)
    snapshot=copy state
    result=increment(borrow mut state)
    if state.value!=8 || snapshot.value!=7 {return 1}
    return 0
}

Параметр объявляется borrow mut, аргумент отмечается явно. Подходит var или mutable-параметр вложенного вызова. Временное значение, immutable binding и readonly-параметр не дают mutable-права. Существующий синтаксис функций сохраняет явный тип результата и return: этот этап не вводит void или отдельные выражения-вызовы в позиции statement. Для операции без полезного результата можно вернуть u32 со статусом.

Типы, чтение и независимые снимки

Корневой тип — пользовательская копируемая структура. Вложенные структуры, фиксированные массивы, целые и bool поддерживаются; ресурсные поля, Text, View, варианты и хранимые займы исключены. Generic-инстанцирование проверяет те же ограничения. Приватность полей не меняется.

Чтение поля или целого объекта даёт значение-снимок. copy не создаёт второй mutable-доступ. Два живых снимка сохраняют старые значения после изменения владельца. Физическое копирование большого снимка может быть необходимо и не считается бесплатным. Передача самого mutable-права не копирует объект обратно после вызова: запись выполняется непосредственно в исходное storage.

Поддержаны изменения полей, вложенных элементов массивов и целого значения. Индексы проверяются до вычисления адреса элемента и до RHS соответствующей записи. Checked-арифметика, порядок аргументов и краткое замыкание сохраняются.

Readonly-параметры borrow теперь допускают также эти копируемые структуры, в дополнение к ресурсным структурам T2-A. Их нельзя изменять или повышать до mutable. Возврат независимого копируемого результата разрешён; возврат займа нет.

Время жизни и исключительность

Первый срез заимствует целый именованный объект. Заимствование отдельного поля, разделение одной структуры на независимые mutable-части и ссылки в локальных переменных пока не поддерживаются. Это позволяет однозначно сохранять корень storage через вызовы: у разных параметров не может быть конфликтующего займа одного корня.

Имена разрешаются в текущей лексической области; конфликты и права привязаны к идентификаторам деклараций. Две одноимённые декларации в независимых блоках не разделяют ни идентичность, ни mutable-право. При присваивании существующей привязке её идентичность сохраняется.

Заём активен от вычисления аргумента до возврата всего вызова. Аргументы вычисляются слева направо. После borrow mut state последующий аргумент не может читать, копировать, перемещать или заимствовать state. Нужный скаляр следует вычислить заранее. Несколько readonly-читателей допустимы; mutable исключает всех остальных читателей и писателей того же storage.

Вложенный mutable reborrow временно передаёт исключительное право callee. Readonly reborrow разрешает только чтение. До возврата вложенного вызова родительский доступ приостановлен в конфликтующей части, затем восстанавливается. Выход за пределы вызова, сохранение займа в структуре и move borrowed-параметра запрещены. После нормального возврата владелец снова доступен.

Эффекты и Semantic IR

pure по-прежнему означает отсутствие внешних эффектов io и alloc. Изменение аргумента — отдельная ось анализа. Два вызова mutable-функции нельзя объединять только потому, что их скалярные аргументы равны или эффект указан pure.

IR содержит режим mutable, операции storage.local, storage.field, storage.index, storage.read, storage.write, loan.begin и loan.end. Проекции и loans сохраняют корень; порядок инструкций и CFG задают порядок конфликтующих обращений. storage.read создаёт независимый снимок. Нижний уровень использует pointer ABI для borrowed копируемых структур любого размера, без пользовательских указателей и без безусловных LLVM noalias/readnone/readonly.

A3 консервативно не специализирует функции с явным storage/loans: это разные механизмы, их совместная оптимизация требует отдельной проверки.

Нормальный Err не откатывает записи. Если библиотеке нужна атомарность отказа, она должна проверить условия до изменения. Trap остаётся терминальным; M0 не обещает unwinding или восстановление владельца после trap.

Mutable JSON и измерения

json.feed_mut(borrow mut state, data) изменяет JsonState на месте и возвращает sticky-код ошибки (0 при успехе). json.status и json.finish сохраняют прежние интерфейсы. Mutable-переход находится в отдельном json_mut.liza; исходный json.feed и неизменный baseline b320611 остаются доступны.

Правила разбора, UTF-8 и смещения ошибок совпадают с исходным автоматом. UTF-8 helper пока остаётся value-based. M0 не подразумевает автоматического ускорения всех функций или паритета с C++/Rust.

Первый результат на Apple M4: 170–272 МиБ/с, 2,22–3,49× относительно A3; паритет с C++/Rust пока не достигнут.

Отдельный benchmarks/mutable/run.py сравнивает A0, закреплённый A3, M0, C++ in-place/by-value и Rust. Он сначала проверяет общий корпус и границы чанков, а затем сохраняет сырые серии, Semantic IR, LLVM IR, assembly, frame и отдельные нетаймируемые bulk-copy counters. Результат измерения не является доказательством корректности lowering.

Проверки и границы

Отдельный аудит M0 фиксирует матрицу доступа, наблюдаемый порядок traps/I/O и исправление ложного отказа для одноимённых привязок в разных блоках. Это конечный регрессионный аудит, не универсальное доказательство.

Тесты M0 охватывают small/large структуры, generic и вложенные вызовы, несколько снимков, аргументы, ветви, циклы, Err и traps на O0/O2. Отрицательные контроли снимают исключительность, подменяют снимок alias и удаляют overflow-trap; нарушения воспроизводятся нативно. Прежние Verified-наборы остаются регрессиями, не универсальным доказательством soundness M0.

T2-B, ресурсные mutable borrows, возвращаемые TextView, scoped threads, mutex/atomics и memory ordering остаются отдельными задачами. Лексическое исключительное право само по себе не обеспечивает синхронизацию потоков.