Clause
← Уся документація

Перекладено з англійського оригіналу · 06042fa · 2026-10-09 · Читати англійською

Семантичний аналіз

Виконується після синтаксичного аналізу під час типової компіляції та з --print-types; дії лише з синтаксисом його пропускають. Помилки зупиняють відповідний пакет до LLVM; діагностика зберігає походження з макросів і включень, а пізніші вхідні файли все одно діагностуються.

Перевірки модулів і викликів

Зв'язування

Кожне зв'язування має ідентичність відносно функції clause[N].local[M]. Входження — це визначення, читання або перевірки точної рівності, позначені контекстом голови/guard/тіла. Аналіз детермінований.

Обходи ітеративні з бюджетом модуля 1,000,000 одиниць роботи. Вичерпання або будь-яка семантична помилка очищає таблиці зв'язувань і нормалізації модуля.

Типи та специфікації

Приватний граф типів представляє всі розібрані форми типів незалежно від розміщення в runtime: синглтони, діапазони, контейнери, ролі полів map, добутки функцій і нерозв'язані застосування. Об'єднання сплощуються та позбавляються дублікатів; term() — верхній елемент, none() — нижній. Типові значення: 16,384 вузли, 16 членів об'єднання, 100,000 елементів роботи на трансляцію. Вичерпання розширює до term() з видимим прапорцем і ніколи не звужує представлення.

Оголошені метадані (-type, -opaque, -nominal, -export_type, -spec, -callback, -optional_callbacks) відповідають довіднику OTP з typespec і зафіксованій поведінці erl_lint/erl_internal/erl_types:

Виведення типів

Виведення типів відокремлене від оголошених типів і ніколи не довіряє специфікаціям.

Домен виведення

Рішення plan 11 step 58A (semantic/types/lattice). Факт — це множина значень, які може мати змінна чи результат. Факти об'єднуються (join) там, де сходиться потік керування (клаузи, гілки), і розширюються (widening) між раундами рекурсивної компоненти; кожен бюджет нижче коректно розширюється до більшої множини, ніколи не відхиляючи програму. Факти друкуються як типи Erlang, категорії — за своїми вбудованими іменами.

ФактДрукуєтьсяОб'єднанняБюджет і розширення
Нічогоnone()Нейтральний елементФункція, яка ніколи не повертається, залишається none()
Будь-щоterm()Поглинає кожен фактdynamic() і any() є term()
Цілі числа42, 1 | 3 | 7Об'єднання синглтонівПонад 8 синглтонів стають своїм діапазоном
Цілочисловий діапазон1..10, 0..255Найменший діапазон, що містить обидваМежа, яка змістилася між раундами, переходить до наступного порогу: нижня — до 1, потім 0, потім без обмеження; верхня — до -1, потім без обмеження
Необмежені цілі числаpos_integer() (1 і більше), non_neg_integer() (0 і більше), neg_integer() (-1 і менше), integer()Найменший інтервал, що містить обидва, друкується за своєю категорією—
Числа з рухомою комоюfloat()——
Числаnumber()Діапазон чи категорія цілих чисел, об'єднані з float()Синглтонні цілі числа з float() залишаються 1 | float()
Атомиok, error | ok, boolean()Об'єднання синглтонів; рівно false і true друкуються як boolean()Понад 8 синглтонів стають atom()
Ідентифікаториpid(), port(), reference()——
Кортежі{ok, 1}, tuple(), #point{x :: 0, y :: _}Кортежі однакового розміру, перші елементи яких не є двома різними атомами (їхнім тегом), об'єднуються поелементно; інші залишаються окремими членамиПонад 16 елементів стають tuple(), якщо не кожен елемент відомий; понад 8 окремих форм стають tuple(). Кортеж з іменем і розміром видимого record на кортежі друкується як record (step 58J)
Списки[], [T], [T, ...], nonempty_improper_list(H, T), [1, a], [a, b | T]Елементи об'єднуються; [] з непорожнім списком дає, можливо, порожній; неправильні списки об'єднують голови та хвости. Списки з двох або більше відомих елементів зберігають свої позиції (step 58J; нотація Clause, у мові типів такої немає, елементи друкуються через | у дужках): позиційні списки однієї довжини об'єднуються позиція за позицією, інакше об'єднуються як звичайні спискиСписок із 0..1114111 (char()) друкується як string() або nonempty_string(); можливо порожній список із _ друкується як list()
Maps#{}, #{a := 1}, #{1..17 => a}, map()Maps з однаковими ключами об'єднуються значення за значенням; maps з іншими ключами об'єднуються в одну асоціацію їхніх об'єднаних ключів і значень (=>: будь-який ключ може бути відсутнім, step 58J)Понад 16 ключів об'єднуються в одну асоціацію
Funsfun((term()) -> 1), fun()Funs однієї арності об'єднують свої результати; інші арності дають fun()—
Bitstrings<<_:16>>, <<_:3, _:_*2>>, binary()Менший розмір плюс кожна різниця розмірів як unitБаза та unit 0/8, 8/8, 0/1, 1/1 друкуються як binary(), nonempty_binary(), bitstring(), nonempty_bitstring()

Пониження (lowering) використовує ці факти. Згенерований IR ніколи не перетворює ціле число на вказівник у купу; кожен результат сервісу, що може зазнати збою, завантажується лише на шляху його успіху, а перевірки форми домінують над видобуванням. Політика спеціалізації: specialization.md.

--print-types

Друкує кожен модуль пакета (у порядку вхідних файлів/цілей, бібліотечні модулі після них) як вихідний код Erlang (друк вихідного коду) з тим, що знайшло виведення типів. Виведення йде в stdout і призначене для читання людиною, це не Erlang і не формат обміну даними. Попередження залишаються в stderr.

%% module "branches" source="branches.erl" target="" declared=complete inferred=complete
-module(branches).
-export([mixed/1]).

%% inferred: mixed(_) -> 1..2
mixed(X) ->
    case X of
        1 ->
            1;
        _ ->
            2
    end :: 1..2.

Очікування виведення

tests/fixtures/inference/*.erl записують, що виведення має знайти для кожної функції і що воно знаходить сьогодні. Кожен модуль стає CTest inference_<module> (tests/compiler/inference/expectations.py):

%% expect: sum() -> 3
%% today: sum() -> _
sum() -> 1 + 2.

Друк типів

semantic::types::type_source(graph, type) (semantic/types/printing) відображає тип із графа типів у синтаксисі типів Erlang: _ для будь-якого терма (term(), що записується як TERM_SOURCE для стислості; синтаксис типів читає _ як any()), none(), атоми та цілі числа, 1..5, {ok, T}, tuple(), [T], [T, ...], #{K => V, K := V}, #r{f :: T}, <<_:B, _:_*U>>, fun((A) -> R), A | B. Fun із кількома типами функцій друкує їх у нотації Clause, fun((1) -> one; (_) -> other): синтаксис типів Erlang не має перевантаженого типу fun, а об'єднання типів fun означає щось інше. Цілі числа об'єднання друкуються в порядку значень там, де стоїть його перше ціле число, послідовні — як діапазон (1 | 2 | 3 | 5 друкується як 1..3 | 5; факт зберігає синглтони). Попередньо визначені типи erlang втрачають свій модуль; посилання на оголошені типи залишаються іменованими. Бюджет вузлів обмежує текст; понад нього, а також нижче 32 рівнів вкладеності, замість нього стоїть ....

clau --print-types answer.erl client.erl
clau --print-types --project project.toml --target demo --verbose