Зразки, клаузи та зіставлення в тілі
Голови клауз функцій, зіставлення в тілі, клаузи case і guard у if
виконуються для кожного допущеного виду термів (терми).
Допустимість і доступність перевіряються окремо: недійсний Erlang є
семантичною помилкою навіть у недосяжному коді; допустимий код, якому
потрібна відсутня можливість, отримує діагностику можливості;
невідповідність під час виконання ніколи не є помилкою компілятора.
| Контекст | Стан |
|---|---|
| Голови функцій + guard | Реалізовано; вичерпання викликає error:function_clause |
Зіставлення та послідовності в тілі, begin/end | Реалізовано; невдача викликає error:{badmatch, RHS} |
Клаузи case + guard | Реалізовано; вичерпання викликає error:{case_clause, Value} |
Клаузи guard в if | Реалізовано; вичерпання викликає error:if_clause |
maybe з ?= і клаузами else | Реалізовано; невдалий ?= повертає своє значення або вибирає клаузу else, вичерпання яких викликає error:{else_clause, Value}; потребує можливості maybe_expr (препроцесор) |
| Comprehension для списків, binary і map | Реалізовано (нижче) |
catch Expr | Реалізовано; без зразків (ABI) |
try, клаузи of і catch | Реалізовано; вичерпання of викликає error:{try_clause, Value}, незіставлені винятки викидаються повторно, а after виконується на кожному шляху (ABI); Class:Reason:Stack зв'язує трасування стека |
| Клаузи fun | Можливість (F18) |
receive | Можливість (F22/F25) |
Comprehension
[T1, ..., Tn || Q1, ..., Qm], << T || Q1, ... >> і
#{K => V, ... || Q1, ... } дотримуються OTP 29:
- Кваліфікатори виконуються зліва направо; кожен генератор перебирає решту в
циклі. Шаблонні вирази обчислюються в цьому порядку для кожної комбінації, що
пройшла; кілька шаблонних виразів додають кілька елементів на комбінацію.
Шаблонний вираз binary comprehension має бути bitstring (інакше
error:badargна цьому елементі); частини з'єднуються по порядку, включно з неповними байтами. Map comprehension обчислює своє (перше) значення перед ключем, і пізніший дублікат ключа має перевагу. - Генератори:
P <- List,<<Segs>> <= BitsіK := V <- Map, зі строгими формами<:-(списки, map) і<:=. Генератор bitstring зіставляє свій зразок із префіксом і продовжує з рештою. Коли нестрогий генератор відхиляє елемент, він пропускає стільки бітів, скільки описують розміри зразка (значення ігноруються, числа з рухомою комою читаються як цілі, як в OTP); коли й це не вдається або бітів лишилося менше, генератор завершується. Генератор map обходить map у порядку ключів (OTP також перебирає до 32 ключів у порядку ключів, окрім атомних ключів, які він упорядковує за індексом атома; більші map дотримуються порядку хешів OTP, який Clause не відтворює). - Зразок генератора зв'язує нові імена: він затінює зовнішні імена, і ніщо, що
зв'язує comprehension, не видно після нього. Нестрогий генератор пропускає
елементи, які відхиляє його зразок; строгий викликає
error:{badmatch, E}з елементом списку, рештою bitstring або{Key, Value}. - Zip-група (
P1 <- L1 && P2 <- L2) бере на кожному кроці по одному елементу з кожного входу; її зразки зв'язуються разом, тож повторене ім'я має збігатися. Відхилений крок пропускається, якщо його не відхиляє строгий зразок. Нерівномірне вичерпання входів або строге відхилення викликаютьerror:{bad_generators, {L1', L2'}}із входами, що лишилися на цьому кроці (генератор map показує ітератор OTP{K, V, Next}, що закінчуєтьсяnone). Фільтри всередині zip-групи є семантичними помилками. Коли нестрогий і строгий генератори однієї групи мають спільну змінну, правило пропуску може відрізнятися від OTP (відмінності). - Вхід, що не є map, викликає
error:{bad_generator, Input}до початку генератора map, навіть усередині zip-групи. - Вхід списку, що не є списком, або неправильний хвіст, а також вхід
bitstring, що не є bitstring, викликають
error:{bad_generator, Tail}, щойно оброблено елементи перед ним. - Фільтр, що є перевіркою guard (OTP
erl_lint:is_guard_test/3: синтаксис guard, що викликає лише незатінені guard BIF, застарілі перевірки типів на верхньому рівні), відхиляє елемент за будь-якої невдачі, як guard. Будь-який інший фільтр має повертатиtrueабоfalse; інші значення викликаютьerror:{bad_filter, Value}, а його винятки поширюються далі. - Кваліфікатор зіставлення верхнього рівня (
P = E) є семантичною помилкою, якщо не ввімкнено експериментальну можливістьcompr_assign; його виконання в такому разі не реалізовано (можливість).
Кожен генератор — це цикл у тілі функції, курсор входу якого (решта списку чи bitstring або map і позиція) зберігається в слотах термів фрейму; створені елементи накопичуються в зворотному порядку в іншому слоті терма і перетворюються на список, з'єднаний bitstring або map один раз наприкінці, тож довгі входи потребують сталого нативного стека і стека процесу.
Форми зразків
| Форма | Правило | Відхилення |
|---|---|---|
_ | Ніколи не зв'язує; входження незалежні | Читання _ — семантична помилка |
Name, _Name, повтори | Одноразове присвоєння; повтори вимагають точної рівності (1 ≠ 1.0) | Читання незв'язаної/небезпечної змінної — семантична помилка; нерівність — невідповідність |
P1 = P2, дужки | Обидва обмежують те саме значення; без зв'язувань ключів/розмірів між сусідами | Недопустима залежність між сусідами — семантична помилка |
| Атоми, цілі числа, символи, числа з рухомою комою | Точна рівність; знаковий нуль розрізняється | Невідповідність |
| Константна арифметика | Оператори зразків OTP згортаються точно; 1 div 0 і не-константи відхиляються | Семантична помилка |
| Кортежі, списки, неправильні хвости, рядки | Точна арність і форма cons/nil; рядки є списками | Невідповідність |
"prefix" ++ Tail | Лише буквальний рядок або буквальний список цілих чисел як префікс | Змінний префікс — семантична помилка |
#{K := P} | Лише :=; додаткові ключі дозволено; #{} перевіряє тип; ключі — вирази guard над вхідними зв'язуваннями | =>, незв'язаний ключ — семантична помилка; відсутній ключ — невідповідність |
| Bitstring | Перевірені тип/розмір/одиниця; раніші сегменти можуть задавати розмір пізніших; хвіст без розміру останній | Недійсний специфікатор — семантична помилка; закороткі дані — невідповідність |
Кортежні record #r{f = P}, #r.f | Розгортаються в обмеження кортежу; пропущені поля не обмежуються | Невідомий record/поле — семантична помилка |
Локальні native records #r{f = P} | Record цього модуля з назвою r, потім кожне перелічене поле (відсутнє поле дає невдачу) | Невідомий record — семантична помилка |
Кваліфіковані/імпортовані native records #m:r{f = P} | Record модуля m з назвою r; експортований, якщо перелічено поле; потім кожне поле | Жодного під час компіляції |
Анонімні record #_{f = P} | Будь-який native record; експортований або визначений у цьому модулі, якщо перелічено поле | #_{...} як вираз — помилка |
| Виклики, змінна арифметика, інші вирази | Не є зразками | Семантична помилка |
Області видимості
Ключі map і розміри binary читають вхідне середовище, а не сусідів:
#{K := V} = #{key := K} і <<X:N>> = <<N:8>> не можуть зв'язати власний
ключ або розмір. Усередині одного binary розмір може читати раніші сегменти:
<<N:8, X:N>> допустимо; {N, <<X:N>>} вимагає, щоб N було зв'язано
заздалегідь. Неправильна константна арифметика в ключі чи розмірі допустима і
дає невдачу під час зіставлення.
Перевірка binary відхиляє сегменти з вкладеними контейнерами/псевдонімами, конфліктні модифікатори, неправильні діапазони одиниць, одиницю без розміру для типових integer/float, недійсні розмір/одиницю UTF, типізовані рядкові літерали або літерали з розміром і незавершальні сегменти binary без розміру.
Виконання
- Плаский план зіставлення для кожної голови/лівої частини над вихідними слотами аргументів; псевдоніми мають спільний вхід, перші визначення зв'язують значення SSA, повтори генерують перевірки точної рівності. Перевірки форми домінують над вилученням; вилучення використовує перевірені сервіси runtime.
- Клаузи виконуються в порядку вихідного коду, кожна зі свіжим середовищем, що заново завантажує вихідні аргументи. Невідповідність голови або відхилення guard веде до наступної клаузи; помилки тіла та інфраструктурні невдачі ніколи не призводять до спроби пізніших клауз.
caseобчислює свій досліджуваний вираз один раз, потім пробує кожну клаузу по порядку з ним як єдиним входом плану: невідповідність зразка або відхилення guard (зокрема помилки guard) веде до наступної клаузи, вичерпання викликає{case_clause, Value}. Кожна клауза починає зі зв'язувань, наявних перед case; значення case і кожне експортоване зв'язування об'єднуються в одному PHI на значення.if— цеcaseбез досліджуваного виразу і зразків: guard кожної клаузи пробуються по порядку (помилка guard відхиляє клаузу), вичерпання викликаєif_clause, а значення та експорти об'єднуються так само.begin/endвиконує свою послідовність в охопній області видимості й повертає її останнє значення.- Послідовності в тілі виконуються по порядку і повертають останнє значення.
Зіставлення обчислює праву частину один раз, зв'язує нові імена, перевіряє
наявні та повертає праву частину (також для
_ = RHS). Ланцюжки спершу обчислюють найвнутрішнішу праву частину. - Атомні літерали завантажують зв'язування модуля; зіставлення ніколи не інтернує атоми.
- Безумовні голови зі змінних компілюються в компактне проєкційне IR.
Обмеження
Нормалізація зразків використовує спільний із модулем семантичний бюджет у
1 000 000 одиниць (ціле число коштує приблизно стільки, скільки в нього
десяткових цифр); константа понад межу цілих чисел у 4 194 240 біт є
illegal pattern, як в OTP (терми).
Кожен план зіставлення має стелю в 100 000 одиниць роботи. Парсер окремо
обмежує вкладеність до 256 (жорстка стеля — 512).
Clause