Erlang Dialyzer: принимать только определенные целые числа?
Скажи, у меня есть функция,foo/1
чья спецификация -spec foo(atom()) -> #r{}.
, где #r{}
это запись, определенная как -record(r, {a :: 1..789}).
Однако у меня есть foo(a) -> 800.
в моем коде, когда я запускаю против него диализатор, он не предупредил меня об этом, (800
не является допустимым возвращаемым значением для функции foo/1
), могу ли я заставить диализатор предупредить меня об этом?
редактировать
Изучаю тебя Какой-то Эрланг говорит:
Dialyzer оставляет за собой право расширить этот диапазон в более широкий.
Но я не мог найти, как это отключить.
1 ответ
Начиная с Erlang 18, обработка целочисленных диапазонов выполняется erl_types:t_from_range/2
, Как вы можете видеть, существует много обобщений, чтобы получить "безопасную" избыточную приближение диапазона.
Если вы пытались ?USE_UNSAFE_RANGES
(см. код) вполне вероятно, что ваша конкретная ошибка будет обнаружена, но за ужасную цену: нативная компиляция и набор рекурсивных целочисленных функций никогда не завершатся!
Причина в том, что анализ типов для рекурсивных функций использует простой подход с фиксированной точкой, когда начальные типы принимают базовые случаи и многократно расширяются с использованием рекурсивных случаев для включения большего количества значений. В какой-то момент чрезмерное приближение должно произойти, если процесс должен завершиться. Вот конкретный пример:
fact(1) -> 1;
fact(N) -> N * fact(N - 1).
Первоначально fact/1
предполагается, что имеет тип fun(none()) -> none()
, Используя это для анализа кода, второе предложение "проваливается", и только первое предложение в порядке. Поэтому после первой итерации новый тип fun(1) -> 1
, Используя новый тип, можно успешно выполнить второе предложение, расширив тип до fun(1|2) -> 1|2
, затем fun(1|2|3) -> 1|2|6
это продолжается до ?SET_LIMIT
достигается в этом случае t_from_range
перестает использовать отдельные значения и тип становится fun(1..255) -> pos_integer()
, Следующая итерация расширяется 1..255
в pos_integer()
а потом fun(pos_integer()) -> pos_integer()
это точка фиксации!
Ниже следует неправильный ответ (поясняется первый комментарий ниже):
Вы должны получить предупреждение для этого кода, если вы используете -Woverspecs
вариант. Эта опция не включена по умолчанию, так как Dialyzer работает в предположении, что все в порядке, чтобы чрезмерно приблизить возвращаемые значения функции. В вашем конкретном случае, однако, вы действительно хотите, чтобы любые дополнительные значения генерировали предупреждения.