Формальные языки
Теорема Райса
2004 год. Facebook запускает статический анализатор Infer. Они обнаруживают тысячи реальных багов в коде iOS и Android приложений. Но Infer также выдаёт ложные срабатывания. Инженеры тратят время на разбор предупреждений, которые не являются ошибками. Это не недостаток реализации Infer - это математическая теорема 1953 года.
- **Facebook Infer:** статический анализатор на базе Bi-abduction (форма abstract interpretation). Используется в Meta для анализа миллионов строк кода. Находит реальные утечки памяти и null pointer. Ложные срабатывания неизбежны по теореме Райса
- **Антивирусные движки (VirusTotal):** более 70 антивирусных движков проверяют файлы. Разные движки дают разные ответы на одном файле - потому что каждый использует разную аппроксимацию неразрешимого свойства
- **TypeScript strict mode:** без strict mode TypeScript часто выводит 'any', пропуская ошибки типов (liberal). С strict mode требует явных аннотаций там, где вывод невозможен (conservative). Выбор между полнотой и звучностью
- **Rust compiler error messages:** Rust borrow checker - возможно лучшие сообщения об ошибках в компиляторах. Команда Rust вложила огромные усилия именно потому, что borrow checker отвергает больше программ чем необходимо - ложные 'ошибки' нужно объяснять
Семантические свойства программ
**Семантическое свойство** программы P - свойство, зависящее от того, что P вычисляет (её языка L(M)), а не от того, как она написана (синтаксиса). Примеры: 'P принимает строку w', 'P принимает хотя бы одну строку', 'P вычисляет функцию f'. Эти свойства не зависят от конкретного кода - только от поведения.
Какое из свойств является семантическим (а не синтаксическим)?
Нетривиальные свойства
Свойство P называется **нетривиальным** если существует хотя бы одна программа с этим свойством и хотя бы одна программа без него. Тривиальные свойства: 'программа является программой' (все программы), 'программа сортирует числа эффективнее O(1)' (ни одна программа). Тривиальные свойства разрешимы прямолинейно - всегда 'да' или всегда 'нет'.
**Теорема Райса (1953):** любое непрямолинейное свойство языка МТ (семантическое свойство) неразрешимо. Формально: пусть P - непрямолинейное свойство частично вычислимых функций (т.е. существует МТ M1 с этим свойством и M2 без него). Тогда язык {<M> : M имеет свойство P} неразрешим.
Свойство: 'программа M останавливается на входе длины < 10'. Непрямолинейно ли оно?
Доказательство теоремы Райса
Доказательство через сведение от A_TM. Пусть P - непрямолинейное свойство. Пусть M_empty - машина с L(M_empty) = ∅. Два случая: ∅ не имеет свойства P (или имеет). В обоих случаях строится вычислимая функция f такая, что M принимает w ⟺ f(<M, w>) имеет свойство P.
Идея: конструкция M_f «переносит» вопрос «принимает ли M строку w» на вопрос «имеет ли M_f свойство P». M_f - это программа, которая 'запоминает' M и w в своём коде и использует их чтобы принять решение о своём языке. Та же техника что в доказательстве неразрешимости E_TM.
Теорема Райса говорит о свойствах ЯЗЫКА МТ (L(M)), а не о самой МТ. Почему это важное разграничение?
Приложения теоремы Райса
Теорема Райса - это теоретическая граница для **всей индустрии инструментов анализа кода**. Она объясняет фундаментальные ограничения, с которыми разработчики сталкиваются ежедневно, не зная их причины.
| Инструмент | Семантическое свойство | Ограничение |
|---|---|---|
| Антивирус | Является ли программа вредоносной? | Нет идеального детектора |
| Статический анализ (Coverity) | Есть ли null pointer dereference? | False positives неизбежны |
| Type inference (TypeScript) | Является ли выражение типа T? | Иногда нужна явная аннотация |
| Компилятор-оптимизатор | Эквивалентна ли оптимизация оригиналу? | Нет полной автоматической оптимизации |
| Формальная верификация | Удовлетворяет ли программа спецификации? | Ограничена разрешимым подклассом |
| Deadlock detector | Имеет ли программа дедлок? | Динамический анализ, не полный |
Практический вывод из теоремы Райса: **полнота, звучность, завершаемость** - выбрать можно не все три. Звучный (sound) анализатор: не пропускает реальных ошибок, но может давать ложные. Полный (complete): не даёт ложных, но может пропустить. Завершаемый: всегда останавливается. Нет инструмента, обладающего всеми тремя для произвольных программ.
С развитием ИИ можно создать инструмент, точно определяющий все баги в произвольном коде
Теорема Райса - математическая теорема о любом алгоритме (включая ИИ). Для произвольного кода точная проверка любого непрямолинейного свойства неразрешима. ИИ может улучшить эвристики, но не преодолеть математический предел
ИИ - это вычислимая функция (нейросеть реализует МТ). Тезис Чёрча-Тьюринга: любая вычислимая функция ограничена возможностями МТ. Значит ИИ-анализатор кода ограничен теоремой Райса так же, как любой другой алгоритм. Улучшение возможно, предел неизменен.
Coverity предупреждает о null pointer на строке 42, но программист проверил - там нет ошибки. Это баг Coverity?
Ключевые идеи
- **Теорема Райса:** любое непрямолинейное семантическое свойство программы неразрешимо. Непрямолинейное = есть программы с этим свойством И без него
- **Доказательство:** сведение от A_TM. Строится M_f, которая 'переносит' вопрос о принятии w на вопрос о свойстве языка
- **Семантическое vs синтаксическое:** синтаксические свойства (структура кода) разрешимы. Семантические (поведение) - по теореме Райса нет
- **Практическое следствие:** нет идеального (звучного + полного + завершимого) анализатора для произвольных программ. Инструменты выбирают компромисс
Связанные темы
Теорема Райса - обобщение неразрешимости для всех свойств программ:
- Сведения — Теорема Райса доказывается через mapping reduction от A_TM
- Разрешимость — Синтаксические свойства разрешимы; теорема Райса - граница для семантических
- Формальная верификация — TLA+, Coq, SPIN работают в разрешимых подклассах - обход ограничений теоремы Райса
Вопросы для размышления
- TypeScript иногда выводит тип 'any' вместо конкретного. Является ли это нарушением теоремы Райса или её подтверждением?
- Rust borrow checker отвергает правильный код. Является ли это семантическим или синтаксическим свойством? Почему Rust не может быть одновременно sound и complete?
- Можно ли обойти теорему Райса, ограничив язык программирования? Какие языки это делают и какую цену платят?