Подтвердить что ты не робот

Действительно ли этот код undefined, как указывает Кланг?

Я включил -fsanitize=undefined в моем проекте, который использует библиотеку тестирования модулей Catch. Одна строка из Catch была указана как вызывающая поведение undefined этим флагом. Мне удалось сделать изолированный пример:

#include <iomanip>
#include <sstream>

int main()
{
    std::ostringstream os; 
    os << "0x" << std::setfill('0') << std::hex;
}

Скомпилировано с помощью

clang++ -fsanitize=undefined main.cpp

Если я запустил это, выдается следующая печать:

/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:76:67: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'

Это происходит для меня на clang 3.6.0 и для друга с clang 3.4-1ubuntu3. Это не происходит для меня в версии gcc 4.9.2

Итак, что здесь? Действительно ли этот код плохой, или есть что-то подозрительное, продолжающееся на конце clang?

4b9b3361

Ответ 1

Это ошибка в libstdС++ из потока cfe-dev списка рассылки с заголовком -fsanitize = undefined и разделяемыми библиотеками:

Это ошибка в libstdС++. Вы сможете обойти его с помощью sanlister blacklist file, как только будет патч для этих земель, но для теперь фильтрация их вручную, вероятно, будет вашим лучшим вариантом.

Вот исправление для исправления; Я буду искать в этом libstdС++ вверх по течению в течение следующих нескольких дней. [...]

Как я уже отмечал dyp в комментариях, не редко встречаются системы, в которых clang использует libstdc++ в отличие от libc++, и если мы проверим это на Coliru явно использует libstdС++ через -stdlib=libstdc++, мы действительно можем воспроизвести проблему.

Следующий отчет об ошибке libstdc++: плохие значения перечисления, рассчитанные оператором ~ в ios_base.h, охватывают эту проблему и говорят:

Перегруженный оператор ~ s, определенный для перечислений в ios_base.h имеют следующую форму:

Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); }

~ создает значения вне диапазона значений перечисления типа, поэтому для возврата к типу Enum задано неопределенное значение (см. [expr.static.cast] p10), и на практике он производит значение Enum вне диапазона представляемых значений для типа Enum, поэтому поведение undefined.

Для справки [expr.static.cast] p10 говорит:

Значение интегрального или перечисляемого типа может быть явно преобразовано в тип перечисления. Значение не изменяется, если исходное значение находится в диапазоне значений перечисления (7.2). В противном случае результат значение не указано (и может быть не в этом диапазоне). Значение типа с плавающей запятой также может быть преобразовано к типу перечисления. Полученное значение совпадает с преобразованием исходного значения в базовый тип перечисления (4.9), а затем тип перечисления.

а hvd говорит, что это формально неопределенное поведение, но Ричард указывает, что на практике это поведение undefined.

T.C. указывает, что это было изменено с неуказанного на undefined поведение DR 1766: значения вне диапазона значений перечисления:

Хотя проблема 1094 пояснила, что значение выражения типа перечисления может не находиться в пределах диапазона значений перечисления после преобразования в тип перечисления (см. 5.2.9 [expr.static.cast], пункт 10), результатом будет просто неопределенное значение. Вероятно, это должно быть усилено для создания поведения undefined в свете того факта, что поведение undefined делает выражение непостоянным. См. Также пункт 9.6 [class.bit].

Новая редакция появится в проекте стандарта в N4431.