Существуют ли случаи, когда составители не могли диагностировать пропущенный возврат? - PullRequest
5 голосов
/ 01 апреля 2020

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

   template<typename T>
   T maybe_call(std::function<T(void)> f) {
           if (f)
                   return f();
           else
                   abort_program();

           // Cannot write a return here, because we have no way to create a value of t
   }

Обратите внимание, что этот пример полностью допустим код. Он не имеет возврата во всех филиалах, но все еще нет UB. Это объясняет, почему отсутствие возврата по всем ветвям - это UB, а не ошибка. Пример может быть «исправлен», чтобы объявить abort_program(); как [[noreturn]], чтобы сделать его менее встречным аргументом. Я сомневаюсь, что у компиляторов будут проблемы с правильной диагностикой этого примера. Если пропуск возврата приведет к ошибке, правила, возможно, придется немного изменить, потому что только с [[noreturn]] приведенный выше пример может быть правильно диагностирован.

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

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

Существуют ли действительно случаи, когда компилятор не может обнаружить пропущенный возврат? Это только в патологическом коде, или это может происходить в повседневном коде тоже? Могу ли я рассчитывать на предупреждение?

Чтобы сделать его ответственным, давайте сосредоточимся на g cc (последняя версия): В каком случае g cc не сможет предупредить о пропущенном возврате?

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Нет причин для существования любого такого случая.

Каждый путь кода в функции в четко определенной программе должен содержать только манипуляции с данными (которые не могут прекратить выполнение функции), внутренний поток (циклы et c, которые содержатся в функции по определению), операторы return, которые завершают функцию, и throw s / longjmp s / exit s / abort s, которые также завершают функцию. Все они тривиально видимы для компилятора.

Кроме того, это просто вызовы функций, к которым то же самое относится и рекурсивно.

Пример / ошибка, о которой вы здесь упоминаете, буквально противоположен: получение предупреждение, потому что вы не не написали return, и вы полагаетесь на вызванную функцию для выполнения throw / longjmp / exit / abort, которую компилятор может не знать, и ваша функция будет иметь неопределенное поведение, если вызываемая функция не не выполнит это ... и вы не использовали intrinsi c компилятора, чтобы сигнализировать, что ваш путь к коду "недоступен". Обычно я предпочитаю, чтобы intrinsi c отключался -Werror; это может даже дать вам преимущества в производительности!

1 голос
/ 01 апреля 2020

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

Сначала я подумал о том, чтобы построить замысловатый пример в следующем виде:

int fun() {
    goto exit;
    return 1;
    exit: ;      
}

Однако, как бы я ни старался, g cc предупредит о таком коде. Чем больше я об этом думаю, тем больше убеждаюсь, что проблема не в том, что может быть код, о котором компилятор не будет предупреждать, а в ложных предположениях. Если пример в вопросе изменяется на

   template<typename T>
   T maybe_call(std::function<T(void)> f) {
           if (f)
                   return f();
           else
                   maybe_abort_program();

           // Cannot write a return here, because we have no way to create a value of t
   }

, то компилятор сам решит, вернется ли maybe_abort_program в конечном итоге, и это проблема остановки, которая не может быть решена во всех случаях. Изменение имени функции, конечно, ничего не меняет, но просто для того, чтобы подчеркнуть, что даже мысль, что функция может быть помечена как [[noreturn]], не пометив ее как таковой, код все еще может быть действительным (или нет). ) и компилятор не может надежно диагностировать это.

Однако компиляторы могут предупреждать о таких случаях. И после нескольких экспериментов я не нашел ни одного примера, где компилятор не предупреждает. Отсюда мой вывод: я могу (в некоторой степени) полагаться на предупреждение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...