Skip to content

RFC-029: Система семантики модулей

Аннотация

Подключение системы модулей к компиляционному конвейеру для реализации мног файловой компиляции и контроля видимости на уровне пакетов.

Основной принцип: проверщик типов запрашивает только предварительно построенный реестр модулей, не обращаясь к диску. Граф модулей строится полностью до проверки типов.

Не включает: кэширование, отслеживание файлов, горячую перезагрузку, инкрементную перекомпиляцию. Это оптимизации жизненного цикла компиляции, относятся к будущим отдельным RFC.

Мотивация

Текущие проблемы

  1. Компилятор поддерживает только один файл: Compiler::compile(name, source) не может обрабатывать межфайловые зависимости
  2. Правила экспорта конфликтуют: типы экспортируются автоматически, константы экспортируются автоматически, методы экспортируются автоматически, функции проверяют pub — четыре набора исключений
  3. Два модуля разрешителя: frontend/module/resolver.rs и package/source/module_resolver.rs с разным порядком поиска
  4. Проверщик типов связан с загрузкой файлов: черновик требовал, чтобы use во время проверки типов вызывал ModuleLoader::load()

Цели проектирования

  • Один проект может компилировать несколько файлов .yx
  • Семантика операторов use понятна и однозначна
  • Правила видимости унифицированы в одно
  • Однофайловый режим продолжает работать, yaoxiang.toml не требуется
  • Проверщик типов — чистая логика, не выполняет файловый I/O

Предложение

1. Идентичность модуля и разрешение путей

Определение модуля

Модуль — это файл .yx. Путь к модулю — это точка-разделённый именованный путь, соответствующий расположению в файловой системе.

math.geometry → src/math/geometry.yx
             → src/math/geometry/mod.yx
             → src/math/geometry/index.yx

Пакет — это проект с yaoxiang.toml, содержащий несколько модулей. Пакет — единственная граница инкапсуляции.

Правила разрешения путей

Порядок поиска (единственное правило, заменяющее существующие два набора):

  1. Стандартная библиотека: std или std.* → встроенные модули, запрашиваются из ModuleRegistry
  2. Директория vendor: .yaoxiang/vendor/<pkg>-*/src/ → зависимые пакеты
  3. Относительный путь текущего файла: относительно директории текущего файла .yx
  4. Директория src проекта: <project_root>/src/

Порядок попыток определения файла:

base/name.yx
base/name/mod.yx
base/name/index.yx

При нахождении первого существующего файла поиск прекращается. Если name.yx и name/mod.yx существуют одновременно, выдаётся ошибка:

Неоднозначность пути модуля: `math.geometry` одновременно соответствует:
  src/math/geometry.yx
  src/math/geometry/mod.yx
Пожалуйста, удалите один из них.

Унифицированный разрешитель

Устраняем два существующих ModuleResolver. Сохраняем frontend/module/resolver.rs как единственную реализацию, удаляем package/source/module_resolver.rs. Поддержка переменной окружения YXPATH объединяется в единственном разрешителе.

2. Семантика импорта

Синтаксические формы

yaoxiang
use math.geometry                          # пространство имён модуля
use math.geometry as geo                   # псевдоним пространства имён модуля
use math.geometry.{Point}                  # выборочный импорт
use math.geometry.{Point, distance}        # множественный выборочный импорт
use math.geometry.{Point as P}             # выборочный импорт с псевдонимом
use math.geometry.{Point as P, distance as dist}  # множественный с псевдонимами

Семантика

Все формы импорта являются правилами разрешения имён на этапе компиляции, а не копированием ссылок во время выполнения. Импортированные имена указывают на идентичность объявления в таблице экспортов модуля.

СинтаксисПривязка к текущей области видимостиСпособ использования
use pathпоследний сегмент path как пространство имёнgeometry.Point
use path as aliasalias как пространство имёнalias.Point
use path.{item}сам itemitem
use path.{item as alias}сам aliasalias

Удалённый синтаксис

  • from path use item: форма Python from-import не принята
  • use path.*: импорт с подстановочным знаком создаёт риск конфликтов, импорт пространства имён модуля достаточен
  • use path.{a, b} as c, d: позиционное сопоставление в параллельных списках — хрупкая структура данных, псевдонимы должны следовать после каждого объявления: use path.{a as c, b as d}

Семантика путей

path в use path всегда является путём модуля, а не объявлением. Если модуль не найден, выдаётся ошибка:

Модуль `math.geometry.Point` не найден.
Если `Point` — это объявление в модуле `math.geometry`, используйте:
use math.geometry.{Point}

Fallback «сначала найти полный модуль, при неудаче последний сегмент как объявление» не применяется.

Конфликты импорта

Импорты с одинаковыми именами вызывают ошибку, без тихого перезаписывания:

Конфликт имени `Point`:
  math.geometry.Point
  graphics.geometry.Point
Пожалуйста, используйте выборочный импорт или псевдоним пространства имён модуля.

3. Видимость

Правила

Пакет — единственная граница инкапсуляции. Модули не несут границ прав доступа.

ЗаписьВ текущем пакетеВ другом пакете
По умолчанию (нет pub)
pub

Одно правило для всех объявлений верхнего уровня: типы, функции, константы, методы.

Устраняем четыре набора исключений из существующего кода:

  • Определения типов всегда экспортируются → единое правило
  • Константы экспортируются автоматически → единое правило
  • Методы экспортируются автоматически → единое правило
  • Только функции проверяют pub → единое правило

Структуры данных

Заменяем is_pub: bool в AST на:

rust
pub enum Visibility {
    Package,  // По умолчанию: видимо в текущем пакете
    Public,   // pub: видимо во всех пакетах
}

Таблица экспортов

Каждый модуль поддерживает две таблицы:

  • PackageSymbols: полная таблица символов пакета, содержит все объявления верхнего уровня
  • PublicExports: подмножество объявлений pub для предоставления другим пакетам

Однопакетный use запрашивает PackageSymbols; межпакетный use может запрашивать только PublicExports.

Перекрёстная ссылка на необъявленное pub вызывает ошибку:

`internalHelper` модуля `math.geometry` невидим.
Это не объявление pub, доступно только внутри пакета `math`.

4. Процесс компиляции проекта

Компиляционный конвейер

Входная точка проекта
  → Чтение yaoxiang.toml для получения точки входа
  → Рекурсивный разбор операторов use из точки входа, обнаружение всех зависимых модулей
  → Построение графа зависимостей модулей (ModuleDependencyGraph)
  → Обнаружение циклических зависимостей
  → Топологическая сортировка
  → Последовательное выполнение для каждого модуля: лексический анализ → синтаксический анализ → извлечение экспортов
  → Построение ModuleRegistry (содержит таблицы экспортов всех модулей)
  → Последовательная проверка типов каждого модуля в топологическом порядке (запрос ModuleRegistry)
  → Генерация нескольких ModuleIR
  → Агрегация диагностики

Проверщик типов только запрашивает предварительно построенный ModuleRegistry, не выполняет загрузку файлов, не обращается к диску.

Выбор входной точки

Приоритет:

  1. [run].main (если существует)
  2. [[bin]] первый элемент path
  3. [lib].path
  4. src/main.yx (соглашение по умолчанию)

Однофайловый режим не требует yaoxiang.toml, компилирует указанный файл напрямую.

Циклические зависимости

Обнаружена циклическая зависимость:
  math.geometry → math.transform → math.geometry

Циклическая зависимость — ошибка компиляции, специальная обработка не применяется.

Агрегация ошибок

Ошибки мног файловой компиляции агрегируются в порядке топологической сортировки модулей. Каждая ошибка помечается исходным модулем и позицией в файле:

Ошибка в модуле `math.geometry`:
  src/math/geometry.yx:12:5
  Тип `Circle` не определён

Ошибка в модуле `app.main`:
  src/main.yx:3:1
  Модуль `math.geometry` невидим

5. Изменения компилятора

КомпонентИзменение
compiler.rsДобавлен метод compile_project(project_root)
pipeline.rsСохранение ответственности за одномодульную компиляцию, не становится божественным объектом
typecheck/checker.rsОператоры use запрашивают ModuleRegistry, не вызывают загрузку файлов
typecheck/inference/statements.rsАналогично, process_use_stmt только запрашивает, не загружает
frontend/module/resolver.rsОбъединение поддержки YXPATH из package/source/module_resolver.rs, становится единственным разрешителем
frontend/module/loader.rsРасширение: поддержка рекурсивного обнаружения, построение полного графа модулей
frontend/module/dep_graph.rsУже реализовано, повторное использование топологической сортировки и обнаружения циклов
frontend/module/registry.rsУже реализовано, повторное использование запросов таблицы экспортов
frontend/module/cache.rsУже реализовано, в этом RFC не подключается к компиляционному конвейеру
frontend/module/hot_reload.rsУже реализовано, в этом RFC не подключается к компиляционному конвейеру
AST is_pub: boolЗаменён на перечисление Visibility
package/source/module_resolver.rsУдалён, функциональность объединена в frontend/module/resolver.rs

Стратегия реализации

Разделение на фазы

Фаза 1: Унификация разрешения модулей

  1. Объединение двух ModuleResolver, удаление package/source/module_resolver.rs
  2. Поддержка переменной окружения YXPATH
  3. Обнаружение неоднозначности пути модуля

Фаза 2: Структуры данных видимости

  1. AST is_pub: bool → перечисление Visibility
  2. Разрешитель поддерживает маппинг ключевого слова pub на Visibility::Public
  3. ModuleLoader::extract_exports унифицированно использует Visibility для определения экспорта

Фаза 3: Входная точка компиляции проекта

  1. В compiler.rs добавлен метод compile_project(project_root)
  2. Рекурсивное обнаружение модулей из входной точки, построение ModuleDependencyGraph
  3. Топологическая сортировка, последовательная загрузка модулей и извлечение экспортов
  4. Построение полного ModuleRegistry
  5. Последовательная проверка типов каждого модуля в топологическом порядке
  6. Генерация нескольких ModuleIR, агрегация диагностики

Фаза 4: Синтаксис импорта

  1. Реализация синтаксиса use path.{item as alias}
  2. Устранение fallback-предположения для конца пути

Зависимости

  • RFC-014 (менеджер пакетов) — имя из yaoxiang.toml, структура директории vendor
  • RFC-011 (обобщённая система типов) — trait — структурированный тип, не затрагивает принадлежность модулю
  • RFC-009 (модель владения) — импорт модуля — это разрешение имён на этапе компиляции, не затрагивает копирование ссылок во время выполнения

Планирование под-RFC

Следующие под-RFC в предварительном планировании, ещё не начата подготовка черновиков:

Под-RFCОжидаемая функциональностьОжидаемые предпосылки
029aКэширование модулей и инкрементная перекомпиляцияГраф модулей и таблицы экспортов стабильны
029bОтслеживание файлов и горячая перезагрузкаМеханизм аннулирования кэша из 029a
029cРеэкспорт (pub use)Таблицы экспортов и правила видимости реализованы
029dCLI параметр --entry переопределение выбора точки входаВходная точка компиляции проекта доступна
029eФормат вывода --json мног файловой диагностикиМеханизм агрегации диагностики доступен
pub(package) видимость модуля частнаяНет реальной потребности, пока не включено
Компиляция нескольких пакетов в рабочем пространствеНесётся в RFC-014c

Ссылки