LINUX.ORG.RU

Constexpr if и числа в Clang

 , , ,


0

2

Есть такой код:

int main()
{
  constexpr int flags = 2;
  
  if constexpr (flags & 3) {
    return 0;
  }
  
  return 1;
}
GCC спокойно проглатывает и работает. Clang же противится:
prog.cc:5:17: error: constexpr if condition evaluates to 2, which cannot be narrowed to type 'bool' [-Wc++11-narrowing]
  if constexpr (flags & 3) {
                ^
https://wandbox.org/permlink/P4JWOgkWj8veamrf

Причем без -Werror. Если подать -Wno-c++11-narrowing то тоже работает (как надо).

Кто прав? Баг ли?

P.S. Наиболее похожее что удалось нагуглить вот: https://stackoverflow.com/questions/41253121/strange-behavior-of-narrowing-in...

Ответ на: комментарий от Deleted

Видимо, не хочет на этапе компиляции неявно переводить int в bool

Ну, собственно

Если сделать if constexpr ((flags & 3) != 0)

Даже больше, вот такой нонсенс работает:

if constexpr (bool(flags & 3))

А с новым синтаксисом уже та же ошибка:

if constexpr (bool{flags & 3})

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

Так конструирование через {} запрещает narrowing

А, ну да.

Хм, может в Clang if constexpr как раз и использует код от «list initialization syntax».

Хотя на cppreference написано, что if statement использует "implicit conversion rules" (== «contextually convertible»). Предварительно выглядит, что GCC прав.

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

Какой же это нонсенс? Это обычные narrowing правила, запрещающие неявные преобразования с потерей точности (implicit truncation). Да, либо bool(flags & 3), либо (flags & 3) != 0. Clang тут абсолютно прав.

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

Это обычные narrowing правила, запрещающие неявные преобразования с потерей точности (implicit truncation).

Так правила введены для «list initialization syntax».

Clang не прав, посмотри по ссылкам в посте выше.

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

Так правила введены для «list initialization syntax».

Не только. «A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only … integral conversions other than narrowing conversions …» http://eel.is/c draft/expr.const#5

Clang не прав

Точно? constexpr if требует «contextually converted constant expression of type bool». А это «is an expression, contextually converted to bool, where the converted expression is a constant expression and the conversion sequence contains only the conversions above.» http://eel.is/c draft/expr.const#def:contextually_converted_constant_express...

Это определение было введено в http://wg21.link/cwg2039 с целью запретить narrowing («… it should instead say something like “a converted constant expression of type bool (8.20 [expr.const]).” This would include the conversion to bool in the determination of whether the expression is constant or not and would also disallow narrowing conversions.»)

Ещё может быть релевантно http://wg21.link/cwg1047

В cpp-core была тема про bool и narrowing 2 недели назад, оттуда ссылки.

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

Как я понимаю, в http://eel.is/c draft/expr.const#5 определяется «converted constant expression of type T». Там же, собственно

where the converted expression is a constant expression and the implicit conversion sequence contains only

  • integral conversions other than narrowing conversions,

Однако ниже доопределяется для T = bool:

A contextually converted constant expression of type bool is an expression, contextually converted to bool, where the converted expression is a constant expression and the conversion sequence contains only the conversions above.

А тут часть о «where the converted expression...» говорит, что само expression (до того как преобразовать его в bool) должно следовать справилам «converted constant expression of type T». А преобразование к bool должно быть contextual: http://eel.is/c draft/conv#def:conversion,contextual_to_bool

An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t ([dcl.init]).

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

тут часть о «where the converted expression...» говорит, что само expression (до того как преобразовать его в bool) должно следовать справилам «converted constant expression of type T».

По-моему, «the converted expression» называют выражение, сконвертированное в bool, а не выражение до конверсии.

Скинь ссылку на багрепорт, посмотрим, что там R. Smith напишет.

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

По-моему, «the converted expression» называют выражение, сконвертированное в bool, а не выражение до конверсии.

Тогда б не было смысла специализировать для bool (разве что для примера, но там нет ключевых слов «e.g.», «for example» и пр.)

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

Тогда б не было смысла специализировать для bool

А где там специализация? Есть «converted constant expression of type T», есть "contextually converted constant expression of type bool" (тут ЛОР облажался с кавычками). Это не специализация. Специализацией было бы «converted constant expression of type bool», без contextually.

Так что

https://www.linux.org.ru/forum/development/14537847?cid=14538681

ниже доопределяется для T = bool

это не «доопределяется».

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

Короче, из обсуждения в сpp-core я так и не понял, к чему там пришли. В [conv.integral]/4 написано, что «If the destination type is bool, see [conv.bool].», что вроде как исключает преобразование в bool из integral conversions (other than narrowing conversions) и тогда вопрос в том, считается ли преобразование к bool сужением.

Надеюсь, обсуждение багрепорта прояснит позицию.

utf8nowhere ★★ ()

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87724

Nicolas Lesser

int main() {
  static_assert(2); // ill-formed, gcc accepts
  if constexpr (2); // ill-formed, gcc accepts
}

Jonathan Wakely

CWG has been discussing this and the consensus seems to be that both examples are OK. We certainly shouldn't make any change to GCC until CWG makes a final decision.

Кек.

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

Это обычные narrowing правила, запрещающие неявные преобразования с потерей точности (implicit truncation).

ага

std::max(0.1,val)
error: no matching function for call to ‘max(double, float&)’

ошибки такие ошибки

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

уточню-компилятор знает что переменная флоат, и статическое число 0.1 переводит всеравно в double

Потому, что есть другая сторона (того что литералы по умолчанию double):

template <typename T>
std::string serialize(const T &value);

serialize(0.1); // T = double

А в твоем примере, на какой основе компилятор должен выбрать «float или double»?

[captain_obvious]

// если с шаблонами
std::max(static_cast<decltype(val)>(0.1), val);
// если статично
std::max(0.1f, val);
[/captain_obvious]

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

ну вот ты написал то что должно быть ПО ДЕФОЛТУ в компиляторе, потому что 0.1 не превышает размер float, и очевидно сконвертировав автоматически никому хуже не будет и не даст абсолютно никаких «новых ошибок»

и «прикол» в том что, это было ВАРНИНГОМ год назад, и сейчас стало ERROR

и такими классными ошибками в C++(clang) можно с головой обмазаться

missxu ()
Ответ на: комментарий от KennyMinigun

Там есть шаблоны? Какого типа там литерал 0.1?

такого какого текущий precision
если precision highp то и 0.1 будет highp
если lowp то и 0.1 lowp

аналогично double/float в С/++

также есть конвертация автоматическая и int во float без варнингов и ошибок(в нвидиевском компиляторе только, в других это ошибка)

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

Как там определен локальный аналог std::max?

такого какого текущий precision если precision highp то и 0.1 будет highp если lowp то и 0.1 lowp

То есть precision регулирует тип литералов и всех floating-point переменных?

также есть конвертация автоматическая и int во float без варнингов и ошибок

В крестах тоже.

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

То есть precision регулирует тип литералов и всех floating-point переменных?

любой флоат может быть иметь любой precision

если сделать max(0.00001,lowp float) то 0.00001 станет lowp тоесть 0
если max(0.00001,highp float) то 0.00001 останется таким

без дополнительных «классных» указаний вручную какого типа это 0.00001

очевидно помоему нет,в 2018 то?

(интересно будет ловить такую ошибку используя auto в C++ да?)

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

если сделать max(0.00001,lowp float) то 0.00001 станет lowp тоесть 0
если max(0.00001,highp float) то 0.00001 останется таким

А для случаев

max(lowp float, 0.00001)
max(highp float, 0.00001)

Что будет?

очевидно помоему нет,в 2018 то?

Зависит от определения сабжевой функции, а std::max еще с мохнатых веков.

(интересно будет ловить такую ошибку используя auto в C++ да?)

Кстати, забавно, если определить тип аргументов как auto (тем самым убрав зависимость между типами), то будет работать как ты хочешь:

constexpr auto max = [](auto a, auto b) constexpr { return a < b ? b : a; };

https://wandbox.org/permlink/5y6lpoeFNuEc6T2y

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

Что будет?

ес че там «не честный» компилятор, он наполовину на шаблонах наполовину интерпретирует и наполовину компилирует

glsl компилятор скорее можно считать как «препроцессор», конвертирующий glsl код в «ассемблер для видеокарт»(который уже драйвер компилирует делая уникальный код для каждой карты)

сравнение с С++ офк не корректное, никто не мешает иметь такойже препроцессор конвертирующий мой «диалект Си» на то что хочет clang/gcc

просто раз уже вводите auto каллбеки и смартптр, то и тут давайте делайте по умолчанию конвертацию

в этом мой посыл был

missxu ()