Skip to content

RFC-025: Расширяемый механизм примитивных типов

Краткое описание

В данном документе определяется расширяемый механизм примитивных типов (Primitive::Extension) компилятора YaoXiang. Он позволяет внешнему коду регистрировать пользовательские примитивные типы в компиляторе, чтобы компилятор мог поддерживать типы, специфичные для предметной области (кубиты, GPU-буферы, SIMD-векторы, аппаратные регистры и т.д.), без жёсткого кодирования.

Мотивация

Зачем нужен этот механизм?

В текущей реализации компилятор жёстко кодирует все примитивные типы: Int, Float, String, Bool, Unit. Добавление каждого нового примитивного типа требует изменения нескольких мест в исходном коде компилятора — проверщика типов, кодогенератора, анализатора владения, оптимизатора.

Это нарушает принцип открытости/закрытости: открыто для расширения, закрыто для модификации.

Границы проектирования

Жёстко закодированные базовые типы (основа языка): Int, Float, String, Bool, Unit
Динамически расширяемые типы (доменные плагины):   через регистрацию в Primitive::Extension

Базовые типы жёстко закодированы, потому что компилятор глубоко зависит от их семантики (арифметические операции, условные переходы, хэширование, сравнение). Расширяемые типы являются для компилятора непрозрачными значениями — компилятор знает только их размер, выравнивание и атрибуты владения, но не внутреннюю семантику.

Это не "унификация всех типов в динамическую загрузку". Базовые и расширяемые типы — это две разные вещи.

Предложение

Основной дизайн

1. Атрибуты типа Extension

При регистрации каждого расширяемого примитивного типа необходимо объявить следующие атрибуты:

rust
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. Интерфейс регистрации

rust
// Внутренний 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 == falseMove-семантика, исходная переменная становится недействительной
Присваивание при is_copy == trueCopy-семантика, исходная переменная остаётся действительной
Неявное копирование (множественное использование в функции)Зависит от is_copy
Сравнение ==, !=❌ Ошибка компиляции (нет встроенного сравнения)
Арифметика +, -❌ Ошибка компиляции (нет встроенных операций)
Обобщённое ограничение T: CopyВыполняется только при is_copy == true

4. Поведение кодогенератора

Расширяемые примитивные типы обрабатываются кодогенератором как непрозрачные значения:

  • LLVM IR: генерируются как {size} x i8 или структура соответствующего размера
  • Специальные инструкции не генерируются — семантика остаётся на стороне бэкенда или библиотеки
  • Если бэкенд требует специальной обработки (например, квантовые вентили QIR), это реализуется через механизм регистрации бэкенда (не входит в данный RFC)

Примеры

Регистрация типа с семантикой Move

rust
// Квантовый бит: не копируемый, размер 0 (физическое состояние на QPU)
compiler.register_primitive(PrimitiveExtension {
    name: "Qubit".into(),
    size: Some(0),
    align: Some(1),
    is_copy: false,
    allow_zst: true,
});
yaoxiang
# Код пользователя
q: Qubit = qubit(0)
q2 = q          # ❌ Ошибка компиляции: Qubit — тип Move, q уже недействителен
q = H(q)        # ✅ Потребляет q, возвращает новый q

Регистрация типа с семантикой Copy

rust
// SIMD-вектор: копируемый, фиксированный размер
compiler.register_primitive(PrimitiveExtension {
    name: "Vec128".into(),
    size: Some(16),
    align: Some(16),
    is_copy: true,
    allow_zst: false,
});
yaoxiang
# Код пользователя
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::ExtensionFFI
НазначениеРегистрация новых типовВызов внешних функций
УровеньСистема типов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 реализовать пользовательский trait QuantumGate)?
  • [ ] Нужны ли хуки жизненного цикла (например, on_drop) для поддержки расширяемых типов с RAII-семантикой?
  • [ ] Формат конфигурационного файла: TOML, YAML или собственный синтаксис конфигурации YaoXiang (см. RFC-015)?

Журнал решений по дизайну

РешениеРешениеПричинаДата
Базовые типы не динамически загружаютсяЖёсткое кодированиеКомпилятор глубоко зависит от семантики базовых типов, динамизация даёт нулевую выгоду2026-06-05
Расширяемые типы — непрозрачные значенияБез внедрения семантикиСемантика остаётся на стороне бэкенда/библиотеки, компилятор обеспечивает только безопасность типов и владения2026-06-05
Ортогонально FFIНе объединятьРегистрация типов и вызов функций — разные уровни абстракции2026-06-05