RFC-025: Расширяемый механизм примитивных типов
Краткое описание
В данном документе определяется расширяемый механизм примитивных типов (Primitive::Extension) компилятора YaoXiang. Он позволяет внешнему коду регистрировать пользовательские примитивные типы в компиляторе, чтобы компилятор мог поддерживать типы, специфичные для предметной области (кубиты, GPU-буферы, SIMD-векторы, аппаратные регистры и т.д.), без жёсткого кодирования.
Мотивация
Зачем нужен этот механизм?
В текущей реализации компилятор жёстко кодирует все примитивные типы: Int, Float, String, Bool, Unit. Добавление каждого нового примитивного типа требует изменения нескольких мест в исходном коде компилятора — проверщика типов, кодогенератора, анализатора владения, оптимизатора.
Это нарушает принцип открытости/закрытости: открыто для расширения, закрыто для модификации.
Границы проектирования
Жёстко закодированные базовые типы (основа языка): Int, Float, String, Bool, Unit
Динамически расширяемые типы (доменные плагины): через регистрацию в Primitive::ExtensionБазовые типы жёстко закодированы, потому что компилятор глубоко зависит от их семантики (арифметические операции, условные переходы, хэширование, сравнение). Расширяемые типы являются для компилятора непрозрачными значениями — компилятор знает только их размер, выравнивание и атрибуты владения, но не внутреннюю семантику.
Это не "унификация всех типов в динамическую загрузку". Базовые и расширяемые типы — это две разные вещи.
Предложение
Основной дизайн
1. Атрибуты типа Extension
При регистрации каждого расширяемого примитивного типа необходимо объявить следующие атрибуты:
pub struct PrimitiveExtension {
/// Имя типа, например "Qubit", "Buffer", "Vec128"
pub name: String,
/// Размер в байтах (типы фиксированного размера)
/// None означает, что размер неизвестен на этапе компиляции (определяется в runtime)
pub size: Option<usize>,
/// Требования к выравниванию
pub align: Option<usize>,
/// Допускается ли неявное копирование
/// false = семантика Move (присваивание перемещает, как Qubit)
/// true = семантика Copy (присваивание копирует, как Vec128)
pub is_copy: bool,
/// Допускаются ли пустые значения (zero-sized type)
pub allow_zst: bool,
}2. Интерфейс регистрации
// Внутренний API компилятора
compiler.register_primitive(PrimitiveExtension {
name: "Qubit".to_string(),
size: Some(0), // Логический размер 0, физическое состояние на квантовом процессоре
align: Some(1),
is_copy: false, // Семантика Move, соответствует no-cloning
allow_zst: true,
});После регистрации Qubit становится легальным примитивным типом в системе типов и может использоваться для объявления переменных, параметров функций, полей структур.
3. Поведение проверщика типов
Расширяемые примитивные типы следуют следующим правилам при проверке типов:
| Сценарий | Поведение |
|---|---|
Объявление переменной q: Qubit = ... | ✅ Допустимо |
Параметр функции fn(q: Qubit) | ✅ Допустимо |
Поле структуры { q: Qubit } | ✅ Допустимо |
Присваивание при is_copy == false | Move-семантика, исходная переменная становится недействительной |
Присваивание при is_copy == true | Copy-семантика, исходная переменная остаётся действительной |
| Неявное копирование (множественное использование в функции) | Зависит от is_copy |
Сравнение ==, != | ❌ Ошибка компиляции (нет встроенного сравнения) |
Арифметика +, - | ❌ Ошибка компиляции (нет встроенных операций) |
Обобщённое ограничение T: Copy | Выполняется только при is_copy == true |
4. Поведение кодогенератора
Расширяемые примитивные типы обрабатываются кодогенератором как непрозрачные значения:
- LLVM IR: генерируются как
{size} x i8или структура соответствующего размера - Специальные инструкции не генерируются — семантика остаётся на стороне бэкенда или библиотеки
- Если бэкенд требует специальной обработки (например, квантовые вентили QIR), это реализуется через механизм регистрации бэкенда (не входит в данный RFC)
Примеры
Регистрация типа с семантикой Move
// Квантовый бит: не копируемый, размер 0 (физическое состояние на QPU)
compiler.register_primitive(PrimitiveExtension {
name: "Qubit".into(),
size: Some(0),
align: Some(1),
is_copy: false,
allow_zst: true,
});# Код пользователя
q: Qubit = qubit(0)
q2 = q # ❌ Ошибка компиляции: Qubit — тип Move, q уже недействителен
q = H(q) # ✅ Потребляет q, возвращает новый qРегистрация типа с семантикой Copy
// SIMD-вектор: копируемый, фиксированный размер
compiler.register_primitive(PrimitiveExtension {
name: "Vec128".into(),
size: Some(16),
align: Some(16),
is_copy: true,
allow_zst: false,
});# Код пользователя
a: Vec128 = load_vec128(data)
b = a # ✅ Copy-семантика, a остаётся действительным
c = add_vec128(a, b) # ✅ a и b оба доступныДетальный дизайн
Изменения в компиляторе
| Компонент | Изменения |
|---|---|
| Система типов | Добавлен новый вариант Ty::Extension, хранящий метаданные PrimitiveExtension |
| Проверщик типов | Расширяемые типы не участвуют в解析 встроенных операций, не удовлетворяют встроенным trait-ограничениям (если не реализованы явно) |
| Анализатор владения | В соответствии с is_copy определяет семантику Move или Copy |
| Кодогенератор | Генерирует непрозрачные значения по size/align, без специальных инструкций |
| Сообщения об ошибках | Ошибки для расширяемых типов ссылаются на name из регистрации |
Связь с FFI
Primitive::Extension ортогонален RFC-021 (FFI):
| Primitive::Extension | FFI | |
|---|---|---|
| Назначение | Регистрация новых типов | Вызов внешних функций |
| Уровень | Система типов | Runtime |
| Пример | Qubit — это тип | native("sin") — это вызов функции |
Предметная область может одновременно нуждаться в обоих: тип Qubit регистрируется через Extension, а функции квантовых вентилей — через FFI.
Обратная совместимость
- ✅ Полная обратная совместимость
- Семантика существующих типов не изменяется
- Расширяемые типы — это новая возможность, не влияющая на существующий код
Компромиссы
Преимущества
- ✅ Компилятору не нужно изменять исходный код для каждой новой предметной области
- ✅ Эксперты предметной области могут самостоятельно регистрировать типы, не полагаясь на команду компилятора
- ✅ Базовые типы остаются жёстко закодированными, без потери глубокой оптимизации компилятором
- ✅ Простой интерфейс — одна структура определяет все атрибуты
Недостатки
- ⚠️ Расширяемые типы не поддерживают встроенные операции — для реализации семантики требуются дополнительные функции или механизмы бэкенда
- ⚠️ При отладке расширяемые типы отображаются как непрозрачные значения, менее наглядно, чем базовые типы
Альтернативные решения
| Решение | Почему не выбрано |
|---|---|
| Все типы с динамической загрузкой | Базовые типы (Int/Float/Bool) требуют глубокой оптимизации компилятором, динамическая загрузка лишит этих возможностей |
| Жёсткое кодирование для каждой предметной области | Каждая новая предметная область требует изменений в компиляторе, не масштабируется |
| Чисто библиотечное решение (без регистрации типов) | Невозможно гарантировать семантику на уровне системы типов (например, no-cloning),只能运行时检查 —只能运行时检查 |
Стратегия реализации
Этап 1: Основной интерфейс
- [ ] Добавить вариант
Ty::Extensionв систему типов - [ ] Реализовать API
register_primitive - [ ] Расширить проверщик типов для обработки семантики
is_copy - [ ] Расширить кодогенератор для обработки непрозрачных значений
- [ ] Модульные тесты
Этап 2: Момент регистрации
- [ ] Поддержка пакетной регистрации при инициализации компилятора (конфигурационный файл или builder API)
- [ ] Поддержка предварительной регистрации стандартной библиотекой (модуль
std.primitiveэкспортирует определения расширяемых типов)
Зависимости
- Нет жёстких зависимостей. Может быть реализовано независимо от других RFC.
Открытые вопросы
- [ ] Должны ли расширяемые типы поддерживать реализацию trait (например, позволить
Qubitреализовать пользовательский traitQuantumGate)? - [ ] Нужны ли хуки жизненного цикла (например,
on_drop) для поддержки расширяемых типов с RAII-семантикой? - [ ] Формат конфигурационного файла: TOML, YAML или собственный синтаксис конфигурации YaoXiang (см. RFC-015)?
Журнал решений по дизайну
| Решение | Решение | Причина | Дата |
|---|---|---|---|
| Базовые типы не динамически загружаются | Жёсткое кодирование | Компилятор глубоко зависит от семантики базовых типов, динамизация даёт нулевую выгоду | 2026-06-05 |
| Расширяемые типы — непрозрачные значения | Без внедрения семантики | Семантика остаётся на стороне бэкенда/библиотеки, компилятор обеспечивает только безопасность типов и владения | 2026-06-05 |
| Ортогонально FFI | Не объединять | Регистрация типов и вызов функций — разные уровни абстракции | 2026-06-05 |
