LINUX.ORG.RU
ФорумTalks

Рекурсия: почему GCC такой тупой?

 ,


1

1

Столкнулся тут в работе с такой фишкой GCC: он ничего не имеет против бесконечных рекурсий:

#include <stdio.h>

int f()
{
    return f();
}

int main()
{
    printf("%d\n", f());
}

clang выдает ворнинг. Про компиляторы более надежных ЯП, чем С и С++ я даже молчу.

★★★★★

The freedom to run the program as you wish, for any purpose (freedom 0).

Вот и GCC не ограничивает тебя в твоём праве писать заведомо зависающий код.

te111011010
()

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

п.с. у меня есть ещё запасной, второй вариант ответа: потому что ты не отправил патчи в fsf?

darkenshvein ★★★★★
()

Про компиляторы более надежных ЯП, чем С и С++ я даже молчу.

А это каких? Потому что Haskell спокойно такое глотает. Правда, в рантайме падает.

 ▲ ~/devel/hs cat rec.hs                                  
{-# LANGUAGE TypeApplications #-}
foo :: Show a => a
foo = foo

main = print @Int foo

 ▲ ~/devel/hs ghc-8.10.1 --make rec.hs -o rec -Wall       
[1 of 1] Compiling Main             ( rec.hs, rec.o )

rec.hs:5:1: warning: [-Wmissing-signatures]
    Top-level binding with no type signature: main :: IO ()
  |
5 | main = print @Int foo
  | ^^^^
Linking rec ...

 ▲ ~/devel/hs ./rec                                       
rec: <<loop>>

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Rust с оптимизацией заменяет бесконечный цикл на значение, без оптимизаций падает из-за переполнения стэка. Но ворнинг на рекурсию выдаёт, да.

 △ ~/devel/rs cat rec.rs
fn foo() {
  foo();
}

fn main() {
  println!("{:?}", foo());
}

 ▲ ~/devel/rs rustc rec.rs -o rec -O
warning: function cannot return without recursing
 --> rec.rs:1:1
  |
1 | fn foo() {
  | ^^^^^^^^ cannot return without recursing
2 |   foo();
  |   ----- recursive call site
  |
  = note: `#[warn(unconditional_recursion)]` on by default
  = help: a `loop` may express intention better if this is on purpose


 ▲ ~/devel/rs ./rec
()

 ▲ ~/devel/rs rustc rec.rs -o rec   
warning: function cannot return without recursing
 --> rec.rs:1:1
  |
1 | fn foo() {
  | ^^^^^^^^ cannot return without recursing
2 |   foo();
  |   ----- recursive call site
  |
  = note: `#[warn(unconditional_recursion)]` on by default
  = help: a `loop` may express intention better if this is on purpose


 ▲ ~/devel/rs ./rec               

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
[1]    5987 abort (core dumped)  ./rec
hateyoufeel ★★★★★
()
Ответ на: комментарий от ilovewindows

Ты ещё не знаешь, что будет, если точку с запятой убрать.

fn foo() {
  foo();
}

У этой штуки возвращаемый тип – (), и никакой другой.

fn bar() -> i32 {
  bar()
}

А у этой можно любой написать. Итого:

 △ ~/devel/rs cat rec.rs            
fn foo() -> i32 {
  foo()
}

fn main() {
  println!("{}", foo());
}

 ▲ ~/devel/rs rustc rec.rs -o rec -O
warning: function cannot return without recursing
 --> rec.rs:1:1
  |
1 | fn foo() -> i32 {
  | ^^^^^^^^^^^^^^^ cannot return without recursing
2 |   foo()
  |   ----- recursive call site
  |
  = note: `#[warn(unconditional_recursion)]` on by default
  = help: a `loop` may express intention better if this is on purpose


 ▲ ~/devel/rs ./rec                 
0
hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

А ещё удивительнее, если i32 заменить на String. И от ворнинга заодно избавиться.

 ▲ ~/devel/rs cat rec.rs     
fn foo() -> String {
  if false {
    return "".to_string();
  }
  foo()
}

fn main() {
  println!("{}", foo());
}

 ▲ ~/devel/rs rustc rec.rs -o rec -O

 ▲ ~/devel/rs ./rec                 
[1]    14750 segmentation fault (core dumped)  ./rec

Этот Rust сломался. Несите другой.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Был же багрепорт на эту тему.

Может, ты его нам найдёшь?

Не помню чем там закончилось, но винили llvm.

Да-да, конечно. Может, ты заодно расскажешь, откуда там 0 взялся в примере с i32?

hateyoufeel ★★★★★
()
Ответ на: комментарий от te111011010

The freedom to run the program as you wish, for any purpose (freedom 0).

ни один из компиляторов, которые я пробовал (включая gcc) не запрещали выполнять бесконечный цикл, читай внимательнее пост

seiken ★★★★★
() автор топика
Ответ на: комментарий от seiken

ни один из компиляторов, которые я пробовал (включая gcc) не запрещали выполнять бесконечный цикл

Тащемта, бесконечный цикл с пустым телом в C – это UB, если мне память не изменяет.

hateyoufeel ★★★★★
()
Ответ на: комментарий от seiken

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

Harald ★★★★★
()
Ответ на: комментарий от darkenshvein

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

Реальный пример не такой очевидный, как этот, например, могут быть всякие препроцессорные вызовы, которые раскрываются по-разному в зависимости от режима сборки (в т.ч. и в ничто). Даже если я обнаружу баг до того, как он уйдет в VCS, мне потребуется копаться в коде вместо банального прочтения варнинга компилятора.

seiken ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Про Rust не знаю, с семиколоном все как надо, а остальное - это уже их внутреннее растовское хозяйство. Видимо, косяк, но Rust это же не такая вылизанная десятилетиями технология, как C…

В Ada (которая такое же древнее гуано мамонта, как C++), тем же GCC компилируется, не только варнит что это бесконечный цикл, но и раскрывает, чем именно это чревато в рантайме, как бы намекая, как это можно обработать без падений (сегфолта), если хочется странного:

hello.adb:7:16: warning: possible infinite recursion
hello.adb:7:16: warning: Storage_Error may be raised at run time
seiken ★★★★★
() автор топика
Ответ на: комментарий от stasolog

Да нет, это похоже сам LLVM.

 ▲ ~/devel/c cat rec.c 
#include <stdio.h>

int foo() {
  return foo();
}

int main() {
  printf("%d\n", foo());
  return 0;
}

 ▲ ~/devel/c clang rec.c -o rec -O3

 ▲ ~/devel/c ./rec                 
-452155096

 ▲ ~/devel/c ./rec
2129998408

 ▲ ~/devel/c ./rec
-2077640184
hateyoufeel ★★★★★
()
Ответ на: комментарий от stasolog

Просто LLVM заменяет всю функцию foo() на банальное ret вместо бесконечного цикла.

hateyoufeel ★★★★★
()
Ответ на: комментарий от Harald

где-нибудь в стандарте языка бесконечные рекурсии запрещены?

смотря какого языка
фортран-77 попытку вызвать из процедуры её саму считал ошибкой
так и писал: «ошибка: рекурсия» и не давал запустить программу
но можно было написать две процедуры, которые вызывали друг друга

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

кстати, int f() { return f(); } - это самая простая проверка на наличие tail-call оптимизации в компиляторе/интерпретаторе
в языке с TCO программа зависает,
в языке без TCO ошибка генерится в рантайме («стек не резиновый»),
а если ошибка возникает при компиляции - то у вас фортран )))

Egor_
()
Последнее исправление: Egor_ (всего исправлений: 1)
Ответ на: комментарий от seiken

Все, что экономит время программиста, имеет смысл. Это не проблема останова.

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

darkenshvein ★★★★★
()
Ответ на: комментарий от te111011010

Но он же не зависнет. Хотя тут всё зависит от «ума» оптимизатора.

atrus ★★★★★
()

А в чем проблема? Ну значит можешь использовать clang. Или оставить основным компилятором GCC, а через clang прогонять просто чтоб почитать варнинги, там у них между прочим есть отдельный вариант, чтоб чисто как статический анализатор отрабатывало https://clang.llvm.org/docs/ClangStaticAnalyzer.html. Или использовать какие-то еще статические анализаторы, например coverity, cppcheck или https://github.com/facebook/infer.

SZT ★★★★★
()

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

anto215 ★★
()

Вот что в MSVC при стандартном 4 уровне предупреждений:

#include <stdio.h>

int f()
{
    return f();
}

int main()
{
    printf("%d\n", f());
}
main.c(6): warning C4717: f: рекурсия на всех путях выполнения, функция вызовет переполнение стека
main.c(10): warning C4702: недостижимый код
fsb4000 ★★★★★
()
Ответ на: комментарий от anto215

Зачем? Тут вроде тема о компилятор vs. компилятор, а не человек vs. компилятор.

seiken ★★★★★
() автор топика

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от kirk_johnson

пусть зарабатывают, я хз вообще кто это. мне фиолетово совершенно.

компания, которая памперсы придумала, зарабатывает ещё больше. но я вот памперсы не покупаю. понимаешь, о чём я?

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Бага сто пудов в llvm, по ссылке вроде даже придумали, как это обойти.

kirk_johnson ★☆
()
Ответ на: комментарий от Iron_Bug

Это статический анализвтор для C, который нашел кучу багов в ядре, тулзах и коде NASA. Очень смешно, что «специалист» с 20 годами опыта не знает про самый известный статический анализатор.

kirk_johnson ★☆
()
Ответ на: комментарий от kirk_johnson

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от Iron_Bug

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

Ну вот разработчики из Linux Kernel и NASA не такие умные, как ты, и им приходится пользоваться разными статическими анализаторами. Дошло до того, что в gcc10 тоже встроили.

P.S. А где можно посмотреть примеры твоего прекрасного безбажного кода?

kirk_johnson ★☆
()
Последнее исправление: kirk_johnson (всего исправлений: 1)

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

BceM_IIpuBeT ★★☆☆☆
()

с чего компилятор должен warning про бесконечный цикл ? пример для форума явно утрированный, но вы написали хвостовую рекурсию, сиречь цикл.. Может вам так надо, вы камень греете, или для протирки участка стека.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Пример специально такой бесполезный (но реальный пример примерно такой же по размеру, но для разраба он может быть уже неочевиден, см. ранее про препроцессорные вызовы). Разогравать так разве что мелкоконтроллеры можно, а остальные полновесные CPU, с многоуровневым кешем, FPU, SIMD и проч. блоками предсказаний… Ну не знаю, так себе стресс-тест. Протирка стека… тоже как-то сранно. Но самое главное, все это можно было бы делать и с желаемым ворнингом.

А собирать систему каждый раз сразу несколькими компиляторами C++… ну тогда проще выкинуть GCC и перейти на более сообразительный clang.

seiken ★★★★★
() автор топика
Ответ на: комментарий от seiken

ну тогда проще выкинуть GCC и перейти на более сообразительный clang.

Статический анализ это не основная функция компилятора. Лучше использовать статический анализ отдельно, компиляцию саму по себе отдельно, к тому же статический анализ из Clang как раз можно использовать отдельно, я об этом в Рекурсия: почему GCC такой тупой? (комментарий) указал. И я б не сказал, что Clang однозначно умнее GCC. Вполне возможно, что есть случаи, когда на какую-то фигню GCC жаловаться будет, а Clang нет, это надо дополнительно исследовать. Ну и к тому же Clang не умеет в некоторые архитектуры, в которые умеет GCC, так что варианта «заменить GCC на Clang» может просто не быть.

Менять компилятор A на компилятор B потому что компилятор B лучше варнинги пишет ... ну такое себе

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.