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

T2-A — readonly-параметры ресурсных структур

Реализованный первый срез. T2-A добавляет непотребляющее наблюдение пользовательских ресурсных структур. Проект T2-A/B остаётся отдельным документационным PR; локальные subviews и возвращаемый TextView не входят в эту реализацию.

struct Holder {buffer:memory.Buffer<u8>;length:u32}
fn length(borrow value:Holder)->u32 {
    return value.length
}
fn capacity(borrow value:Holder)->u32 {
    return memory.len(value.buffer)
}

borrow — режим доступа параметра, не свойство типа Holder. На стороне вызова используется length(owner) без move и без отдельного выражения borrow owner. Владелец остаётся доступным; после возврата разрешены move, обновление через потребляющую функцию и завершение ресурса. Функция не обязана и не имеет права закрывать readonly-параметр.

Права и ограничения

Параметр должен иметь конкретный тип пользовательской ресурсной структуры. Скаляр, обычная копируемая структура, голый Buffer/Vec/Region в такой позиции пока не поддерживаются. Можно наблюдать вложенные ресурсные поля такой структуры. Generic-функции допускаются при инстанцировании ресурсным типом; режим и права сохраняются при подстановке. Тело проверяется после конкретизации, как и раньше.

Разрешены чтение доступных копируемых полей и их независимые снимки, наблюдающие builtin-операции и передача readonly-проекции другому readonly-параметру. Ресурсное поле остаётся проекцией исходного корня: его нельзя связать как owned, скопировать, переместить, разобрать, вернуть, поместить в вариант или агрегат. Нельзя обновить поле readonly-параметра. Приватность проверяется на каждом пути независимо от режима доступа; borrow не открывает поля чужого модуля.

Первый срез требует от функции с readonly-параметрами независимого копируемого результата. View нельзя вернуть или сохранить в агрегат, даже вложенный. Временный ресурс в borrowed-позиции запрещён: требуется доступная именованная привязка или проекция readonly-параметра внутри callee. Ресурсные alias-привязки, borrowed-match вариантов и неоднозначные выбираемые пути к ресурсу не вводятся. Прежние ограничения рекурсивных сводок с зависимостями хранилища сохраняются.

Readonly не равно borrowed_params или pure

Существующие trusted-адаптеры могут получать ресурс без передачи владения и изменять его. Поэтому borrowed_params не даёт разрешения работать с readonly- проекцией. Для builtin есть отдельный явный список разрешённых наблюдений:

  • memory.len, memory.view;
  • vec.len, vec.capacity, vec.get, vec.view.

memory.alloc через readonly-регион запрещён, как и изменяющие вызовы File или Reactor. Передать readonly-ресурс потребляющему builtin также нельзя. Пользовательские readonly-функции не помечаются trusted_library: их тела проходят обычные проверки.

Эффекты независимы. Наблюдатель может иметь io и вывести данные через console, не изменяя ресурс. pure не даёт readonly-полномочий автоматически.

Время жизни и происхождение

Аргументы вычисляются слева направо. Заём аргумента активен до возврата всего вызова, включая вычисление следующих аргументов и вложенные вызовы. Например, use(owner, consume(move owner)) запрещён, если первый параметр use — readonly. В обратном порядке обнаруживается use-after-move. Два readonly-аргумента одного владельца допустимы. После вызова заём не сохраняется в caller.

Проекции и reborrow сохраняют корневой параметр и происхождение хранилища по полям. Нельзя закрыть тот же регион через другой аргумент. Права не теряются при generic-инстанцировании, ветвлении или цикле; доступность owned-аргументов по-прежнему проверяется на всех продолжающихся путях.

В Semantic IR пользовательский параметр и соответствующий аргумент вызова имеют режим readonly. Проекции помечаются readonly_projection и ссылкой owner_binding на корень; наблюдающий вызов через проекцию также сохраняет корень. Это не owned-извлечение Buffer, хотя физически используется прежний дескриптор. Сводка отдельно перечисляет readonly_parameters, не объявляя их потреблёнными. Режим borrow старых адаптеров и существующих owned-вызовов не переименовывается.

Непотребляющий String

Обычный модуль examples/text/string.liza теперь предоставляет:

Вызов Результат
text.string.length(current) Логическая длина в байтах
text.string.capacity(current) Вместимость
text.string.byte_at(current,index) Option<u8>; вне длины — None

После любого из этих вызовов можно вызвать append(move current, data), clear(move current) или release(move current). inspect(move current) сохраняется как потребляющая операция T1.

Изменение API: прежний byte_at(move current,index) возвращал структуру Byte с владельцем и результатом. Теперь используется byte_at(current,index) без деструктуризации и без move. Тип Byte удалён. Доступ байтовый, не индексирование Unicode-символов; UTF-8-инвариант строки и атомарность отказа append не меняются.

Внутри byte_at используется существующий полный memory.view и предварительная проверка index < length. View локален и не выходит наружу. Поддержка именно диапазона [0,length) как передаваемого view, subviews и корректное наследование loans при их создании остаются T2-B. Полный capacity нельзя выдавать за текст.

Стоимость и проверка

Для трёх наблюдателей String стоимость не зависит от длины или capacity: передаются дескрипторы, для byte_at читается один байт. Это не обещание O(1) для любого readonly-метода: снимок большого копируемого поля имеет свою стоимость.

python3 tools/check_readonly_cost.py
python3 -m liza run examples/owned_string.liza

Проверка стоимости исследует O0/O2 LLVM конкретных трёх функций: отсутствие аллокатора, bulk-copy и непрозрачных helper-вызовов, наличие runtime-чтения байта. Нативные тесты проверяют независимость большого снимка, вложенные вызовы и циклы. Отрицательные контроли охватывают приватность, owned-извлечение, мутации через legacy borrowed_params, порядок аргументов, region origins и escape view.

V0–V4 и Relay остаются регрессиями прежних программ. Их успешные результаты не доказывают soundness T2-A. Новые правила проверены тестами; отдельная формальная модель readonly-полномочий пока не построена. Многопоточность, mutable borrows, возвращаемые TextView и долгоживущие займы не входят в T2-A.