объявление функции в функции: баг компилятора?
От: slava_phirsov Россия  
Дата: 15.10.13 11:41
Оценка:
Доброго времени суток всем читающим!

Вот такой вопросик: имеем минимальный пример

#include <iostream>

using std::cerr;

namespace
{

void foo()
{
    int bar();

    if (bar())
        cerr << "preved\n";
}

int bar()
{
    return -1;
}

}

int main(int argc, char* argv[])
{
    foo();

    return 0;
}


компилируется

g++ -ansi -Wall main.cpp


на "ура", но стоит добавить оптимизацию:

$g++ -O2 -ansi -Wall main.cpp
/tmp/ccZgQSZ3.o: In function `main':
main.cpp: (.text+0x3a): undefined reference to `(anonymous namespace)::bar()'


устраняется, если объявление bar() вынести за пределы функции foo().

Проверено на Centos 6, и на MinGW под Win. Беглый просмотр асм показал, что для функции bar() при включенной оптимизации просто не генерируется код. Это, лыжи не едут баг компилятора, или как?

Заранее благодарю
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: объявление функции в функции: баг компилятора?
От: Zhendos  
Дата: 15.10.13 13:47
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Доброго времени суток всем читающим!


_>Вот такой вопросик: имеем минимальный пример


_>[cpp]


_>namespace

_>{

_>void foo()

_>{
_> int bar();

_>}


_>int bar()

_>{
_> return -1;
_>}

_>Проверено на Centos 6, и на MinGW под Win. Беглый просмотр асм показал, что для функции bar() при включенной оптимизации просто не генерируется код. Это, лыжи не едут баг компилятора, или как?


Проблема в том, что согласно пункту 3.5.6 C++ стандарта,
при определении функции "int bar();", если она до этого не описывалась и не объявлялась,
то компилятор считает, что она имеет external linkage.
В то время как, согласно 3.5.4 описанная внутри безымянного namespace она имеет internal linkage.
Т.е. внутри foo bar символ имеющий external linkage, а описанная позже bar имеет interal linkage.
В случае без оптимизации, компилятор компилировал как есть, а потом линковщик состыковал bar и bar.
В случае же с оптимизацией, компилятор выкидывает bar, на который никто не ссылается, т.к. в foo ссылаются
на bar с external linkage, можно это проверить, например назвав как-то это namespace.
Re: объявление функции в функции: баг компилятора?
От: Tilir Россия http://tilir.livejournal.com
Дата: 15.10.13 13:49
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Вот такой вопросик: имеем минимальный пример


Проверил на gcc 4.8.1
Воспроизвелось и пример можно даже уменьшить:

namespace
{
int foo()
{
    int bar();
    return bar();
}

int bar()
{
  return -1;
}
}

int main(int argc, char* argv[])
{
  return foo();
}


Посмотрел дампы компилятора, на 003t.original ещё всё хорошо, уже на следующей 004t.gimple функции bar нет. То есть bar выкинули где-то на гимплификации. Воспроизводится от O1 и выше. То что оно работает на O0 объясняется тем, что в функции cgraph_decide_is_function_needed, стоит явная проверка вида (TREE_PUBLIC (decl) || (!optimize) || ... ) так что без оптимизаций ни одна функция выкинута не будет.

Для меня это очень похоже на баг. Я поискал по багзилле, но ничего подобного там нет, так что можете зафайлить.

Workaround очень прост:

(1) либо объявите функцию в самом anonymous namespace а не внутри функции в нем.

namespace
{
int bar();

int foo()
{
    return bar();
}

int bar()
{
  return -1;
}
}

int main(int argc, char* argv[])
{
  return foo();
}


(2) либо помогите компилятору понять, что bar -- нужен:

namespace
{

int foo()
{
    int bar();
    return bar();
}

int __attribute__ ((used)) bar()
{
  return -1;
}
}

int main(int argc, char* argv[])
{
  return foo();
}


Оба варианта вполне рабочие.
Re[2]: объявление функции в функции: баг компилятора?
От: Tilir Россия http://tilir.livejournal.com
Дата: 15.10.13 13:57
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Т.е. внутри foo bar символ имеющий external linkage, а описанная позже bar имеет interal linkage.


GCC с вами не согласен. Дамп с одной из стадий компиляции:

int {anonymous}::foo() ()
{
  int D.2212;
  int bar (void);

  D.2212 = {anonymous}::bar ();
  return D.2212;
}


Forward-decl действительно выглядит как external linkage, но реально используется bar из того же anonymous namespace. В таких условиях кажется багом, что компилятор не сохраняет этот bar.

Вы можете убедиться что он ждёт именно internal bar, сделав такой эксперимент:

foo.cpp:

namespace
{

int foo()
{
    int bar();
    return bar();
}

int bar()
{
  return -1;
}
}

int main(int argc, char* argv[])
{
  return foo();
}


bar.cpp:

int bar(void)
{
  return -2;
}


Теперь линкуем их вместе:

g++ -O1 foo.cpp bar.cpp
/tmp/ccEsziw4.o: In function `main':
ce.cpp:(.text+0x5): undefined reference to `(anonymous namespace)::bar()'
Re[2]: объявление функции в функции: баг компилятора?
От: Lorenzo_LAMAS  
Дата: 15.10.13 14:04
Оценка:
Z>Проблема в том, что согласно пункту 3.5.6 C++ стандарта,
Z>при определении функции "int bar();", если она до этого не описывалась и не объявлялась,
Z>то компилятор считает, что она имеет external linkage.

непонятна диагностика, например, кланга:

warning: function 'bar' has internal linkage but is not defined [-Wundefined-internal]
int bar();
^

Of course, the code must be complete enough to compile and link.
Re[2]: объявление функции в функции: баг компилятора?
От: slava_phirsov Россия  
Дата: 15.10.13 14:39
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Для меня это очень похоже на баг. Я поискал по багзилле, но ничего подобного там нет, так что можете зафайлить.


Я бы сделал багрепорт, но только ради этого заводить аккаунт
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.