The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



Вариант для распечатки  
Пред. тема | След. тема 
Форум Разговоры, обсуждение новостей
Режим отображения отдельной подветви беседы [ Отслеживать ]

Оглавление

Началось альфа-тестирование PHP 8.1, opennews (ok), 13-Июн-21, (0) [смотреть все]

Сообщения [Сортировка по времени | RSS]


3. "Началось альфа-тестирование PHP 8.1"  –4 +/
Сообщение от Аноним (3), 13-Июн-21, 09:54 
Статический анализатор в PHP настолько беспомощен, что не может понять, что в функции содержится вызов exit или бросает исключение и поэтому надо переложить написание лишних подсказок на плечи разработчика? А синтаксис перечисления нельзя было взять из Си  или TypeScipt?
Ответить | Правка | Наверх | Cообщить модератору

5. "Началось альфа-тестирование PHP 8.1"  +1 +/
Сообщение от Аноним (5), 13-Июн-21, 10:14 
Нет нельзя! Надо было обязательно слепить что-то среднее между enum'ом и switch'ом, чтобы не как у всех было)
Ответить | Правка | Наверх | Cообщить модератору

9. "Началось альфа-тестирование PHP 8.1"  –3 +/
Сообщение от Варенье (?), 13-Июн-21, 10:40 
> А синтаксис перечисления нельзя было взять из Си  или TypeScipt?

Подозреваю что лексер они сильно переусложнять не хотели, поэтому взяли синтаксис из Swift

Ответить | Правка | К родителю #3 | Наверх | Cообщить модератору

10. "Началось альфа-тестирование PHP 8.1"  +6 +/
Сообщение от Ordu (ok), 13-Июн-21, 10:47 
> Статический анализатор в PHP настолько беспомощен, что не может понять, что в функции содержится вызов exit или бросает исключение и поэтому надо переложить написание лишних подсказок на плечи разработчика?

Если ты вызываешь из кода php функцию на C, которая прерывает выполнение программы, то статистический анализатор должен каким-то магическим образом знать об этом свойстве внешней функции? Или может у него должна быть база данных всех внешних функций? Или может, всё же, проще добавить в синтаксис возможность задекларировать функцию как никогда не завершающуюся?

> А синтаксис перечисления нельзя было взять из Си  или TypeScipt?

Нет. У них внутри определения типа могут быть объявлены методы. В том числе и внутри enum'а. php, как я понимаю, следит за тем, чтобы синтаксис был бы легко парсящимся, в смысле чтобы не требовалось бы расчехлять весь опыт pattern-matching'а, чтобы определить что следующий statement из себя представляет. Например, функция начинается с ключевого слова function. Класс со слова class. Вариант enum'а со слова case. Они не совсем успешны в следовании этой идеи, и функция реально может начинаться с public static, но тем не менее, такие штуки очень помогают держать синтаксис непротиворечивым и избегать сложностей распутывания контекстных зависимостей.

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

Чтобы парсить C и тем более C++, тебе потребуется llvm. В принципе, C можно парсить и при помощи sparse. Либо ты будешь парсить регекспами, смиряясь с тем, что ты твой парсер никогда не поймёт, что означает * в данном контексте: слово "указатель" в названии типа? разадресацию указателя? умножение? Да блин, ты замучаешься отличить декларацию переменной, от умножения: "a * b" может быть умножением a и b, а может быть объявлением переменной b с типом "указатель на тип a".

Ответить | Правка | К родителю #3 | Наверх | Cообщить модератору

95. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Sw00p aka Jerom (?), 14-Июн-21, 13:39 
>Если ты вызываешь из кода php функцию на C, которая прерывает выполнение программы

а смысл тогда в статическом анализаторе для языка который по факту является оберткой другого языка?

Ответить | Правка | Наверх | Cообщить модератору

96. "Началось альфа-тестирование PHP 8.1"  +1 +/
Сообщение от Ordu (ok), 14-Июн-21, 14:51 
>>Если ты вызываешь из кода php функцию на C, которая прерывает выполнение программы
> а смысл тогда в статическом анализаторе для языка который по факту является
> оберткой другого языка?

Я даже не знаю что сказать на это. Что значит "язык является обёрткой другого языка"? Язык C является обёрткой для асма? C++ является обёрткой для C?

Как я не кручу в голове твои обёртки, пытаясь понять, что ты хотел сказать, я не могу понять какое это отношение имеет к полезности статического анализатора. Давай мы лучше замнём тему, сделаем вид, что никаких обёрток и не было, и я просто расскажу тебе, зачем статическому анализатору нужно знать про no-return функции?

Всё очень просто: если статический анализатор знает, что функция не возвращает управления и завершает программу, то он может сделать вывод, что "вечный" цикл вовсе не вечный, а значит инкремент индекса не выползет за границы буфера. Или может статический анализатор увидит, что один из путей выполнения функции, набитой ветвлениями, не возвращает значения -- он будет ругаться, но если он увидит, что в конце этого пути выполнения стоит no-return функция, то он не будет ругаться. Он не будет захламлять мне вывод своими ворнингами. Меньше фолс-позитивов в работе статического анализатора -- больше внимания уделяется тру-позитивам, и меньше соблазна просто выключить ворнинги к чертям.

Но кроме того, no-return атрибут на функции добавляет новых срабатываний статическому анализатору: код расположенный после безусловного exit -- явно не нужный код, оставленный там по ошибке. И неплохо было бы, чтобы программист не забыл бы этот код удалить. Или может удалить вызов exit -- может это он был оставлен по ошибке?

Ответить | Правка | Наверх | Cообщить модератору

100. "Началось альфа-тестирование PHP 8.1"  –1 +/
Сообщение от Sw00p aka Jerom (?), 14-Июн-21, 17:13 
> Я даже не знаю что сказать на это. Что значит "язык является
> обёрткой другого языка"? Язык C является обёрткой для асма? C++ является
> обёрткой для C?

Вот что значить вот это "Если ты вызываешь из кода php функцию на C"? пхепешный fopen вызывает lib сишный fopen который вызывает системный сишный сискол open.

https://en.wikipedia.org/wiki/Wrapper_function


> Давай мы лучше замнём тему, сделаем вид, что никаких обёрток и не было, и я просто расскажу тебе, зачем статическому анализатору нужно знать про no-return функции?

Лучше сначала определимся и опишем, что требуется от анализатора, ибо будем говорить о разных вещах.

> Всё очень просто: если статический анализатор знает, что функция не возвращает управления
> и завершает программу, то он может сделать вывод, что "вечный" цикл
> вовсе не вечный, а значит инкремент индекса не выползет за границы
> буфера.

Если нет возврата управления, то либо "вечный цикл", либо аварийный останов.

> Или может статический анализатор увидит, что один из путей выполнения
> функции, набитой ветвлениями, не возвращает значения -- он будет ругаться, но
> если он увидит, что в конце этого пути выполнения стоит no-return
> функция, то он не будет ругаться.

Что он будет выдавать, когда столкнется с проблемой останова? Статический анализатор ведь должен простроить граф потока управления.

> Или может удалить вызов exit -- может это он был оставлен по ошибке?

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

Ответить | Правка | Наверх | Cообщить модератору

102. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Ordu (ok), 14-Июн-21, 18:21 
> Вот что значить вот это "Если ты вызываешь из кода php функцию на C"? пхепешный fopen вызывает lib сишный fopen который вызывает системный сишный сискол open.

Эмм... Любой язык, кроме ассемблера, становится обёрткой, если следовать такому определению. Си ведь тоже не вызывает напрямую сисколлов, а дёргает обёртки из libc. fopen не вызывает сисколл open напрямую, а дёргает обёртку.

> Лучше сначала определимся и опишем, что требуется от анализатора, ибо будем говорить о разных вещах.

Анализ кода. Как правило с целью найти потенциальные ошибки в коде. Или даже не потенциальные, а самые натуральные ошибки. Когда тебе gcc пишет, что ты в условии if'а использовал =, что скорее всего опечатка, и если ты не хочешь видеть этот варнинг, то он предлагает тебе заключить условие в дополнительные скобки: вот этот варнинг -- как раз работа статического анализатора, встроенного в компилятор. Внешний по отношению к компилятору статический анализатор может выискивать больше подозрительных мест.

Но, в теории, я бы назвал статическим анализатором не только то, что ищет подозрительные места, но например программу, которая, допустим, пытается статическим анализом кода выяснить максимальную глубину стека, которая будет использована. Или, скажем, предсказать где будут бутылочные горлышки производительности. Или, скажем, высчитать максимальный latency наихудшего случая. А в случае, если им не удаётся выполнить свою задачу, то сообщают, что помешало.

Я не упомню таких программ, но исходя из их теоретической возможности: если бы они были, они тоже занимались бы статическим анализом кода, для того, чтобы без запуска программы узнать о ней что-то.

Чтобы понятнее было, можно сравнить с динамическим анализом -- со всякими там профайлерами, valgrind'ами, fuzzing-тестерами, и не только fuzzing, -- которые выясняют что-то о программе, посредством запуска её.

Статический анализ имеет некоторые преимущества: он может формулировать и доказывать утверждения о коде, типа "в этом цикле i всегда будет меньше чем arr.len()", и делать выводы типа
"поэтому проверка условия "i < arr.len()" на каждой итерации цикла не нужна". (ну или ровно наоборот, "WARNING: не удалось доказать, что i<arr.len() следовательно проверка нужна"). В динамике можно лишь статистически проверить такое утверждение, а это совсем не то.

> Если нет возврата управления, то либо "вечный цикл", либо аварийный останов.

while(1) {
   exit(0);
}

Вот тебе "вечный" цикл, который прекращает быть вечным (и вообще циклом), если мы знаем, что exit -- это no-return функция. Если тебе хочется более реалистичного примера, то...

for(i = 0; ; i++) {
    if(fds[i] >= 0)
        close(fds[i]);
    else
        exit(0);
}

Код закрывает все fds, из предположения что все открытые файловые дескрипторы лежат в начале fds, а все неиспользуемые элементы fds < 0, и что как минимум один неиспользуемый там есть. Он закрывает все, и завершает выполнение программы. Если мы не знаем, что exit завершает выполнение программы, то внезапно мы видим вечный цикл, который рано или поздно выйдет за границы массива, либо потому что i станет слишком большим, либо потому, что случится переполнение целого и i станет слишком отрицательным.

Хотя статистическому анализатору это всё равно, не поможет, потому как он не знает про инварианты fds, но мне не придумать сходу ещё более реалистичного примера. А! Можно анализатору подсунуть инварианты fds в виде логических утверждений, и пускай после этого он проверяет их соблюдение, и правильное использование fds тоже проверяет. А когда не может проверить, пускай кидает варнинги, мы будем править код, так, чтобы анализатор справился бы с доказательством.

Впрочем, в php, я подозреваю всё проще. Я думаю, тут не случайно так совпало, что в одном релизе появились и fiber'ы, и noreturn функции: fiber'ы постоянно завершаются. Программа завершается лишь раз, за всё время работы, а fiber'ов много, и почти все они завершаются. И тут, я полагаю, требуется определённое взаимодействие рантайма (который рулит фиберами) и компилятора, чтобы компилятор no-return функции правильно бы норетурнил, прибивая фибер. Ну или что-нибудь в этом роде.

> Что он будет выдавать, когда столкнется с проблемой останова? Статический анализатор ведь должен простроить граф потока управления.

А где там проблема останова вылезет? Как я это вижу, если компилятор может проследить пути выполнения таким образом, чтобы разложить код в линейную последовательность ассемблерных инструкций, или операций интерпретатора, то значит он может определить моменты, где происходит возврат из функции, а возвращаемое значение не предоставлено, так? А если компилятор может, то и статический анализатор сможет. Особенно если он -- часть компилятора.

А ежели компилятор этого не может сделать, то значит он не может завершить компиляцию разумным образом, и значит это ошибка компилятора. То есть в результате статистического анализа в программе была найдена ошибка.

> А как анализатор это может разрешить, что удалить, ошибочно добавленный exit или анюзед код после него. Это решает программист. А анализатор должен сообщить лишь о том, что у тебе тут exit, а код ниже никогда не исполниьтся.

Да, именно так он и делает, он кидает варнинги. Но для того, чтобы решить, что здесь нужно кинуть варнинг, он сначала должен детектировать код, который не будет выполняться никогда. А для этого ему надо знать, что exit -- это no-return функция.

Ответить | Правка | Наверх | Cообщить модератору

108. "Началось альфа-тестирование PHP 8.1"  –1 +/
Сообщение от Sw00p aka Jerom (?), 15-Июн-21, 01:32 
> Эмм... Любой язык, кроме ассемблера, становится обёрткой, если следовать такому определению.
> Си ведь тоже не вызывает напрямую сисколлов, а дёргает обёртки из
> libc. fopen не вызывает сисколл open напрямую, а дёргает обёртку.

Ну да.

> Анализ кода. Как правило с целью найти потенциальные ошибки в коде. Или
> даже не потенциальные, а самые натуральные ошибки.

Опять задам парочку уточняющих вопросов, ибо не ясно, что значит "анализ кода", "потенциальные или натуральные ошибки", а за "ошибки" мы не раз обсуждали - это очень широкое понятие, что конкретно принимается за ошибку (то есть нужно определить это множество ошибок)? Приведу пример моего понимания:

1) Анализ кода с точки зрения безопасности алгоритма. (???????? тут много вопросов)

2) Анализ кода с точки зрения корректности алгоритма. (работа алгоритма выдает желаемый результат, при описанных условиях)

3) Анализ кода с точки зрения оптимальности алгоритма. (тут имеется ввиду не то, что анализатор увидев алгоритм сортировки "пузырьком" выдавал предупреждение, мол можно использовать квиксорт, а оптимальное использование как временных (быстродействие), так и пространственных ресурсов (использование памяти))

Хотя предположу, тот же анализ безопасности вытекает из корректности и оптимальности алгоритма. Думаю на текущий момент любую проблему (ошибку) безопасности можно свести к корректности и оптимальности. Алгоритм "безопасный", если он "корректный" и "оптимальный".

Далее про "самые натуральные ошибки" ( :), не мог себе представить какую из ошибок могу считать натуральной, ошибки за которые учительница по рукам линейкой бьет?), так вот надо понять какие "ошибки" имеем ввиду.

1) Синтаксические, грамматические, лексические - приводящие к некорректности алгоритма.

2) Логические - приводящие как к некорректности, так и к неоптимальности.  

Что тут еще добавить?

> Когда тебе gcc пишет,
> что ты в условии if'а использовал =, что скорее всего опечатка,
> и если ты не хочешь видеть этот варнинг, то он предлагает
> тебе заключить условие в дополнительные скобки: вот этот варнинг -- как
> раз работа статического анализатора, встроенного в компилятор.

А зачем тут ворнинг? какой в этом смысл, если реально мне не запрещает ЯП использовать в if присваивание =. Ибо результат этого присваивания тем же ЯП будет трактоваться как булево (логическое) значение, а оператор if именно с такими значениями (операндами) работает. Да конечно в большинстве случаев это "банальная" логическая ошибка, но ни как не синтаксическая, грамматическая, лексическая. И решение этой проблемы, в  синтаксическом переосмыслении условного оператора. Зачем компилятору ворнинг, если конкретно эта ситуация допустима в ЯП?


> Внешний по отношению к
> компилятору статический анализатор может выискивать больше подозрительных мест.

Только если он будет следовать каким-то заранее заданным шаблонам (паттернам логических ошибок), ибо не существует анализатора который бы разрешил бы проблему останова, так как это проблема банальный логический парадокс. Если человеческая логика впадает в ступор перед логическими парадоксами, думаете анализатор с ними справится (можно на это не отвечать)?


> Но, в теории, я бы назвал статическим анализатором не только то, что
> ищет подозрительные места, но например программу, которая, допустим, пытается статическим
> анализом кода выяснить максимальную глубину стека, которая будет использована. Или, скажем,
> предсказать где будут бутылочные горлышки производительности. Или, скажем, высчитать
> максимальный latency наихудшего случая. А в случае, если им не удаётся
> выполнить свою задачу, то сообщают, что помешало.

Выше описал это, ток в данном контексте надо отделать понятия профайлинга (Анализ кода с точки зрения оптимальности алгоритма) и статического анализатора (Анализ кода с точки зрения корректности алгоритма.) в обще принятом смысле этого слова. Тут просто мне не ясно, как "статически" произвести профайлинг (без выполнения программы), то есть как оценить оптимальность без исполнения, и сравнения результата с ожидаемым?

> Я не упомню таких программ, но исходя из их теоретической возможности: если
> бы они были, они тоже занимались бы статическим анализом кода, для
> того, чтобы без запуска программы узнать о ней что-то.

Как выше написал, как без запуска программы сравнить ее результат с ожидаемым?

> Чтобы понятнее было, можно сравнить с динамическим анализом -- со всякими там
> профайлерами, valgrind'ами, fuzzing-тестерами, и не только fuzzing, -- которые выясняют
> что-то о программе, посредством запуска её.
> Статический анализ имеет некоторые преимущества: он может формулировать и доказывать утверждения
> о коде, типа "в этом цикле i всегда будет меньше чем
> arr.len()", и делать выводы типа
> "поэтому проверка условия "i < arr.len()" на каждой итерации цикла не нужна".
> (ну или ровно наоборот, "WARNING: не удалось доказать, что i<arr.len() следовательно
> проверка нужна"). В динамике можно лишь статистически проверить такое утверждение, а
> это совсем не то.

Вот тут немного не ясно, хочу увидеть всю конструкцию цикла, не могу представить.

>[оверквотинг удален]
>     else
>         exit(0);
> }
> Код закрывает все fds, из предположения что все открытые файловые дескрипторы лежат
> в начале fds, а все неиспользуемые элементы fds < 0, и
> что как минимум один неиспользуемый там есть. Он закрывает все, и
> завершает выполнение программы. Если мы не знаем, что exit завершает выполнение
> программы, то внезапно мы видим вечный цикл, который рано или поздно
> выйдет за границы массива, либо потому что i станет слишком большим,
> либо потому, что случится переполнение целого и i станет слишком отрицательным.

Смотрите, что такое тип ? - область (диапазон) допустимых значений, из примера выше переменная i имеет тип целочисленного значения (не важно пока со знаком или без) и инкрементируется в бесконечном цикле. Отсюда делаем вывод, что переменная i в данном случае может принять все допустимые значения (допустим MAX_UINT). Далее эта переменная используется в качестве индекса массива fds, размер (длина) которого не превышает MAX_UINT по определению, что индексация массивов находится в области допустимых целочисленных значений без знака. Но если мы обращаемся по индексу, который больше размера (длины) массива, то происходит аварийный останов, из-за выхода за границы массива. Собственно вопрос, как в случае когда размер (длина) массива заранее не известна, статический анализатор скажет (без исполнения), мол тут выход за границы массива? А выход из цикла разве не будет если допустим что аварийного останова нет и после переполнения инта он становится обратно в 0, то по условию это уже освобожденный файловый дескриптор и выполниться exit().

> Хотя статистическому анализатору это всё равно, не поможет, потому как он не
> знает про инварианты fds, но мне не придумать сходу ещё более
> реалистичного примера.

Таки да.

> А! Можно анализатору подсунуть инварианты fds в виде логических
> утверждений, и пускай после этого он проверяет их соблюдение, и правильное
> использование fds тоже проверяет. А когда не может проверить, пускай кидает
> варнинги, мы будем править код, так, чтобы анализатор справился бы с
> доказательством.

А не легче сильно статически это определять? Вспомним язык паскаль, и мнение Кернигана о нем (Почему Паскаль не является моим любимым языком программирования). Помните, мы обсуждали строки сишные и паскалевы их плюсы и минусы, тоже самое и с анализаторами Вывод мой таков, статический анализатор применим в основном в сильно статических (система типов) ЯП, а для "динамических" - динамический анализатор. Не вижу смысла статическим анализатором пытаться анализировать код динамического ЯП. Кстати про no-return функции, что нам предоставляет паскаль? - процедуры (которые ничего не возвращают) и собственно функции. Статический анализатор способен в таком случае распознать no-return "функцию"? А в С что? оператор return. А если его нет?

https://stackoverflow.com/questions/4260048/c-function-defin...

https://stackoverflow.com/questions/45981545/why-does-noretu...

> А где там проблема останова вылезет? Как я это вижу, если компилятор
> может проследить пути выполнения таким образом, чтобы разложить код в линейную
> последовательность ассемблерных инструкций, или операций интерпретатора, то значит он
> может определить моменты, где происходит возврат из функции, а возвращаемое значение
> не предоставлено, так? А если компилятор может, то и статический анализатор
> сможет. Особенно если он -- часть компилятора.

Стоп, стоп, тут не надо путать понятия компиляции с понятием исполнения, компилятор не исполняет!

Ибо как вы представляете себе компиляцию такой программы?

while (true)
{
   [code block] - а тут нет никакой функции или оператора прерывающий цикл.
}

Тоже самое с unused кодом, во многих случаях ни компилятор ни статический анализатор не сможет принять решение является ли код не используемым, исключая некоторые частные случаи, как отметил выше, паттерны заложенные в анализатор или компилятор.

> А ежели компилятор этого не может сделать, то значит он не может
> завершить компиляцию разумным образом, и значит это ошибка компилятора. То есть
> в результате статистического анализа в программе была найдена ошибка.

нет, нет - процесс компиляции и непосредственного выполнения - разные понятия.


> Да, именно так он и делает, он кидает варнинги. Но для того,
> чтобы решить, что здесь нужно кинуть варнинг, он сначала должен детектировать
> код, который не будет выполняться никогда. А для этого ему надо
> знать, что exit -- это no-return функция.

И детектирует только по заранее известному паттерну, то есть вы должны создать статический анализатор все-возможного говно-кода (грубо говоря). Статический анализатор это не симулятор исполнения, тогда ему необходимы входные данные, а если ему эти данные дать, то это динамический анализатор. Надо просто четко знать какие задачи выполнимы статическим анализатором.

Ответить | Правка | Наверх | Cообщить модератору

112. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Ordu (ok), 15-Июн-21, 03:20 
>> Анализ кода. Как правило с целью найти потенциальные ошибки в коде. Или
>> даже не потенциальные, а самые натуральные ошибки.
> Опять задам парочку уточняющих вопросов, ибо не ясно, что значит "анализ кода",
> "потенциальные или натуральные ошибки", а за "ошибки" мы не раз обсуждали

if(a = b) {
бла-бла-бла
}

Это есть потенциальная ошибка. С точки зрения статического анализатора. Он не знает задумку программиста, он не знает, хотел ли программист сравнить a и b (и написать a == b), или он хотел выполнить присвоение, а полученное значение в a использовать как булевское значение.

Это, судя по интернетам, довольно распространённая ошибка в C, вызванная опечаткой. Но, я повторю, статический анализатор, не знает, ошибка ли это или нет. Я это назвал "потенциальной" ошибкой.

Что же касается "натуральной" ошибки -- я может неудачно слово подобрал, но я "натуральный" использовал в том контексте противопоставляя "потенциальной", наверное, лучше было бы назвать её "актуальной"?

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

Сначала без ошибки, но с лишней проверкой:

for(i = 0; i < arr.len(); i ++) {
    if(i < arr.len()) {
        sum += arr[i];
    }
}
Проверка лишняя, это математически доказуемо, и оптимизаторы эту проверку легко устраняют. Статический же оптимизатор может кинуть варнинг о том, что проверка лишняя. Зачем он будет так делать -- это другой вопрос, отдельный большой и длинный.

Теперь с не лишней проверкой:
for(i = 0; i < arr.len() * 2; i ++) {
    if(i < arr.len()) {
        sum += arr[i];
    }
}
Тут уже не удастся доказать, что проверка лишняя, то есть что устранение этой проверки не изменит результат выполнения кода. Более того, если мы проверку устраним, то статический анализатор сможет доказать, что i выходит за границу arr.len() и кинуть нам варнинг. Но это сработает, только если компилятор представляет себе инварианты массивов. Язык C не представляет: для него массив это просто указатель, ты можешь объявлять переменную как int arr[5], но C потом с радостью позволит тебе индексировать arr числом больше пяти или отрицательным числом. Даже если в коде будет написано arr[-1000], компилятор C не увидит в этом ничего странного. Но статический анализатор может возбудиться от такого. И если он сможет доказать, что индекс достигает 5 и превосходит его, он тоже может возбудиться.

> - это очень широкое понятие, что конкретно принимается за ошибку (то
> есть нужно определить это множество ошибок)?

Естественно это очень широкое понятие, и именно поэтому мы не будем его определять. Всё что ты в разговоре можешь назвать ошибкой, и всё что может быть детектировано как ошибка посредством разглядывания кода и размышлений -- это всё потенциальная цель поиска для статического анализатора.

Хотя, быть может, синтаксические ошибки стоит из этого списка выкинуть, статический анализатор всё ж заточен на работу с "синтаксически корректными" программами, которые компилируются.

>> Внешний по отношению к
>> компилятору статический анализатор может выискивать больше подозрительных мест.
> Только если он будет следовать каким-то заранее заданным шаблонам (паттернам логических
> ошибок), ибо не существует анализатора который бы разрешил бы проблему останова,
> так как это проблема банальный логический парадокс. Если человеческая логика впадает
> в ступор перед логическими парадоксами, думаете анализатор с ними справится (можно
> на это не отвечать)?

Я не видел ни одного статического анализатора, который бы обещал выполнять роль серебряной пули и находить вообще все проблемы. Теоретически мы можем нафантазировать такой, но практически таких нет. Они все решают вполне конкретные задачи, и да, наверное, посредством сопоставления с образцом... Не, не всё можно. И вообще можно зайти и с другой стороны. Можно объявлять инварианты -- логические утверждения, которые должны сохранять истинность на протяжении выполнения программы. Скажем, что индекс в массиве >=0 и меньше длины этого массива. А дальше статический анализатор ищет все индексации массивов, и пытается доказать это утверждение про индекс. Иногда он доказывает его, и всё ок. Иногда он опровергает его, и это можно рассматривать как ошибку. Иногда он не может ни доказать, ни опровергнуть -- тут можно вести себя по разному, можно выкидывать варнинг, ибо нехрен писать код, про который невозможно доказать инварианты.

> Выше описал это, ток в данном контексте надо отделать понятия профайлинга (Анализ
> кода с точки зрения оптимальности алгоритма)

Весь профайлинг, с которым мне приходилось сталкиваться был динамическим. Но если ты настаиваешь, давай разделять профайлинг на динамический и статический. Динамический требует запуска программы для измерения времени, мы не об этом говорим совершенно.

> Тут просто мне не ясно, как "статически" произвести профайлинг (без выполнения
> программы), то есть как оценить оптимальность без исполнения, и сравнения результата
> с ожидаемым?

Я не знаю, как оценить "оптимальность", я не понимаю, что ты имеешь в виду. А вот как провести профайлинг -- это, например, в случае ассемблера, можно попытаться делать так: берёшь мануал к процессору, выковыриваешь оттуда информацию о том, сколько времени занимает выполнение той или иной инструкции, после чего разбиваешь программу на линейные куски без ветвления, считаешь время для каждого куска, а потом начинаешь анализировать ориентированный граф ветвления, высчитывая сколько раз какая итерация цикла выполнится.

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

> Смотрите, что такое тип ? - область (диапазон) допустимых значений, из примера
> выше переменная i имеет тип целочисленного значения (не важно пока со
> [...]
> находится в области допустимых целочисленных значений без знака. Но если мы
> обращаемся по индексу, который больше размера (длины) массива, то происходит аварийный
> останов, из-за выхода за границы массива.

Нет. Это зависит от языка. C не выполняет никаких аварийных остановов из-за такой мелочи, как выход за границы массива.

> когда размер (длина) массива заранее не известна, статический анализатор скажет (без
> исполнения), мол тут выход за границы массива?

Во-первых, длина может быть известна -- это зависит от языка. Во-вторых, если она не известна, то статический анализатор может исходить из самой общей идеи: неограниченные значения индекса -- это потенциальная ошибка. А здесь он не сможет наложить на значения индекса никаких ограничений, кроме ограничений типа.

> А выход из цикла
> разве не будет если допустим что аварийного останова нет и после
> переполнения инта он становится обратно в 0,

Он становится в INT_MIN, который при четырёхбайтовом инте равен -2^31. А это значит переполнение буфера вниз. Плюс, кстати, это переполнение целого, что в случае C есть UB.

> то по условию это уже освобожденный файловый дескриптор и выполниться exit().

Не, во-первых, про освобождёные фд анализатор будет знать только если ему каким-то образом сообщили про инварианты. Если не сообщили, он вообще никаких допущений о содержимом fds делать не может. Во-вторых, в цикле не зануляются значения fds. Они остаются прежними. Их можно назвать "dangling file descriptors". Повторный проход по массиву повторно вызовет все close.

> А не легче сильно статически это определять? Вспомним язык паскаль, и мнение
> Кернигана о нем (Почему Паскаль не является моим любимым языком программирования).
> Помните, мы обсуждали строки сишные и паскалевы их плюсы и минусы,
> тоже самое и с анализаторами Вывод мой таков, статический анализатор применим
> в основном в сильно статических (система типов) ЯП, а для "динамических"
> - динамический анализатор. Не вижу смысла статическим анализатором пытаться анализировать
> код динамического ЯП.

Да, динамический сложнее анализировать. Но потенциально можно извлечь больше, если анализировать не только функции отдельно, и забывать этот анализ, переходя к другой функции, а строить общую блоксхему выполнения программы, и отслеживать какая функция откуда вызывается, какого типа аргументы туда передаются, и соответственно _доказать_, что например в локальной переменной a функции foo может быть только четырёхбайтовое целочисленное значение, потому что a заполняется из аргумента arg1 строчкой a = arg1 + 3; а все вызовы функции foo кладут в arg1 целочисленное значение.

То есть, можно статически восстановить информацию о типах, и... А дальше можно взять и сказать, что если foo почти всегда вызывается с целочисленным аргументом, лишь один раз туда передаётся строковый, то это подозрительно похоже на ошибку программиста, который задумал foo с целочисленным аргументом, но один раз нечаянно сунул туда строку.

> Кстати про no-return функции, что нам предоставляет паскаль?

Ничего.

> - процедуры (которые ничего не возвращают) и собственно функции. Статический анализатор
> способен в таком случае распознать no-return "функцию"?

Нет.

> А в С что? оператор return. А если его нет?

Если в сишной функции нет оператора return, то возвращаемое значение функции неопределено, то есть это UB. Функция без return'а в C -- это не no-return функция. По-крайней мере в том определении "no-return", которое мы используем в данном разговоре. И паскалевская процедура -- это тоже не no-return функция в этом смысле. no-return функция -- это функция которая не возвращает управление в код, вызвавший её. В стандарте C нет ничего такого, так же как и в pascal'е. Но в gcc, если мне не изменяет память, есть атрибут noreturn или типа того, который можно повесить на функцию, сообщив компилятору, что она noreturn.

>> А где там проблема останова вылезет? Как я это вижу, если компилятор
>> может проследить пути выполнения таким образом, чтобы разложить код в линейную
>> последовательность ассемблерных инструкций, или операций интерпретатора, то значит он
>> может определить моменты, где происходит возврат из функции, а возвращаемое значение
>> не предоставлено, так? А если компилятор может, то и статический анализатор
>> сможет. Особенно если он -- часть компилятора.
> Стоп, стоп, тут не надо путать понятия компиляции с понятием исполнения, компилятор
> не исполняет!
> Ибо как вы представляете себе компиляцию такой программы?

В смысле? Ты ждёшь, что я разложу эту функцию здесь в линейную последовательность команд ассемблера? Ты можешь сделать это без моей помощи: gcc/clang вызванные с аргументом -S выдадут тебе ассемблерный дамп, то есть линейную последовательность ассемблерных команд.

> Тоже самое с unused кодом, во многих случаях ни компилятор ни статический
> анализатор не сможет принять решение является ли код не используемым, исключая
> некоторые частные случаи, как отметил выше, паттерны заложенные в анализатор или
> компилятор.

Да. Есть три варианта:
1. анализатор доказал, что код used
2. анализатор доказал, что код unused
3. анализатор не смог доказать ни того, ни другого.

Первые два случая тривиальны, с точки зрения того, что анализатору делать столкнувшись с ними. Третий случай сложнее, и анализатор может вести себя по-разному. Это зависит от языка, от целей анализатора, возможно от опций запуска анализатора.

>> Да, именно так он и делает, он кидает варнинги. Но для того,
>> чтобы решить, что здесь нужно кинуть варнинг, он сначала должен детектировать
>> код, который не будет выполняться никогда. А для этого ему надо
>> знать, что exit -- это no-return функция.
> И детектирует только по заранее известному паттерну, то есть вы должны создать
> статический анализатор все-возможного говно-кода (грубо говоря).

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

Ведь инварианты можно задавать спорадически, допустим комментариями специального вида. В принципе, даже объявления типов переменных -- это своего рода инварианты, типа "эта переменная хранит только значения типа int". Можно задавать инварианты чаще, и, скажем, для структуры динамического массива с полями buf, size, len, писать инвариант len <= size, а статический анализатор потом будет обращать внимание на все изменения len и size, и пытаться доказать или опровергнуть инвариант, с опорой на код меняющий их. Получать один из трёх вариантов доказано/опровергнуто/хз, и выдавать реакцию вида кинуть варнинг, ошибку, или ничего не кидать.

Можно писать инварианты ещё чаще -- на каждую функцию. Можно вообще на каждый чих: каждый раз, когда можно написать простой инвариант (когда не надо две страницы покрывать математической вязью, когда достаточно простого условия). Ну и, наконец, можно полную спецификацию кода задать. То есть, разница между ubsan и проверкой спецификации скорее количественная, чем качественная.

> Статический анализатор
> это не симулятор исполнения, тогда ему необходимы входные данные, а если
> ему эти данные дать, то это динамический анализатор.

Нет, есть такая штука, как символьные вычисления. Алгебра, короче. Зная какие-то истинные утверждения о значениях переменных, можно доказывать другие. Например, можно сказать, что sqrt(a) <= a, для любого a>=0. А это значит, что, например:

for(i = 0; i < sqrt(arr.len()); i ++) {
    arr[i] = 0;
}

никогда не выйдет за границы массива, вне зависимости от значения arr и arr.len().

Можно взять баблсорт:

for(i = 1; i < arr.len(); i++) {
    for(j = i; j > 0; j --) {
        make_them_ascending(&arr[j-1], &arr[j]);
    }
}

И доказать, что здесь не случится выхода за границы массива, хотя нигде нет непосредственного сравнения j с arr.len(). С одной из границ он сравнивается, а с другой нет, но статический анализ, применив немного логического вывода, может доказать, что всё ок. И для этого вовсе не обязательно выполнять.

Да, вот логический вывод, как раз, сталкивается с проблемой останова, но это будет означать лишь то, что статический анализатор иногда будет долго думать, прерывать свои размышления по таймеру (или по используемой памяти, или ещё по чему), и выводить варнинг "я не могу доказать X в строке N сорца SOURCE_NAME, но, допустим, я доказал, и вот я дальше разбираю остальной код". Или может мы запустим его в sloppy режиме, и он будет нам сообщать только о тех местах, где ему удалось опровергнуть какой-нибудь инвариант. Или может ещё как-нибудь выкрутимся: сделав отрицательный результат поиска багов ненадёжным; или заставив программистов принять такой стиль написания кода, чтобы всё доказывалось...

Ответить | Правка | Наверх | Cообщить модератору

80. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Аноним (80), 14-Июн-21, 09:25 
Что еще ожидать от недоразвитых вебмакак неспособных даже написать помощьный статический анализатор. Анонимным эксперам всё ясно с php, давно. Единственное что анонимным экспертам не ясно, что же такое статический анализатор и зачем он нужен. А так же зачем тип Nothing в kotlin, never в swift и typescript.
Ответить | Правка | К родителю #3 | Наверх | Cообщить модератору

81. "Началось альфа-тестирование PHP 8.1"  +1 +/
Сообщение от Онаним (?), 14-Июн-21, 10:07 
Статический анализатор в динамическом языке...
Печалька.
Оно вообще может управление отдать через call_user_func(['ex'.chr(105).'t']);, и хрен ты это анализатором разберёшь, грубо говоря. Для продвинутых анализаторов можно пример ещё усложнить, обвешав вызовами C'шного API.
Ответить | Правка | К родителю #3 | Наверх | Cообщить модератору

82. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Онаним (?), 14-Июн-21, 10:07 
// квадратные скобки лишние, сорян
Ответить | Правка | Наверх | Cообщить модератору

84. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Аноним (80), 14-Июн-21, 10:37 
в php вообще-то есть статическая типизация для аргуметов функций и полей классов
Ответить | Правка | К родителю #81 | Наверх | Cообщить модератору

89. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Онаним (?), 14-Июн-21, 12:07 
Там скорее тайпчек частично на этапе трансляции, а в основном - в рантайме.
Ответить | Правка | Наверх | Cообщить модератору

90. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Онаним (?), 14-Июн-21, 12:08 
(потому что не для констант и предсказуемых вызовов финальный тип становится известен только в рантайме)
Ответить | Правка | Наверх | Cообщить модератору

91. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Онаним (?), 14-Июн-21, 12:10 
(ну то есть к реальной статический типизации это имеет слабое отношение, хотя часть оптимизаций применима, конечно же)
Ответить | Правка | К родителю #84 | Наверх | Cообщить модератору

109. "Началось альфа-тестирование PHP 8.1"  +/
Сообщение от Sw00p aka Jerom (?), 15-Июн-21, 01:42 
> Статический анализатор в динамическом языке...
> Печалька.

Блин аааа, мою портянку выше можно было уместить в ваши две строчки :)

Ответить | Правка | К родителю #81 | Наверх | Cообщить модератору

Архив | Удалить

Рекомендовать для помещения в FAQ | Индекс форумов | Темы | Пред. тема | След. тема




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру