Откуда компиляторы знают, что вычисления C ++ constexpr не вызывают неопределенное поведение? - PullRequest
5 голосов
/ 31 октября 2019

Стандарт C ++ предписывает компиляторам проверять наличие неопределенного поведения в C ++ constexpr вычислений .

В этот разговор Чендлер Каррут заявляет, что «при проверке UB у вас не будет возможности обнаруживать ошибки», и что в общем случае обнаружение UB связано с проблемой остановки , так что доказуемо невозможно решить .

Он не говорит об UB в constexpr, но вычисления constexpr являются такими же общими, как и обычные программы начиная с C ++ 14 , так что это все еще применимо.

Так что же делают компиляторы, когда они не могут решить, является ли программа UB или нет ? Они все еще принимают программу и продолжают компилировать со скрещенными пальцами? Или они более консервативны и отвергают программу, даже если она потенциально верна? (Мое личное чувство, что они это делают)

Для меня это имеет практическое значение, так как у меня есть оценка constexpr с нетривиальной арифметикой указателей, которая прекрасно компилируется с Clang, но не работает с GCC, и я уверен, что этоне UB. Вы можете сказать, что это ошибка GCC, но если UB неразрешима, все компиляторы будут и будут содержать ошибки в этом отношении.

Более фундаментально, , почему стандарт UB запрашивает без 10B по стандарту? Есть ли техническая причина? Или, скорее, философский («если компилятор не может проверить, программист может вызвать UB, и это приведет к плохим вещам»)?

Я думаю, что это несовместимо с остальной частью C ++, которая никогда не мешает вамстреляя себе в ногу. Я бы предпочел, чтобы GCC принимал мой код constexpr и вылетал, или генерировал мусор, если UB;вместо того, чтобы не компилировать, когда он не знает, является ли он UB.

====== EDIT ======

Как указано MMи Николь Болас, стандарт определяет ограничения (даже в C ++ 14), чтобы мы никогда не сталкивались с проблемой типа остановки UB. Тем не менее, мне все еще интересно, если проверка UB может быть слишком сложной, и если эвристика компилятора не удалась, они помечают ее (возможно, неправильно) как non-constexpr.

Но у меня есть ощущение, что этоЕще одна проблема незрелых реализаций.

Ответы [ 3 ]

4 голосов
/ 31 октября 2019

В этом выступлении Чендлер Каррут заявляет, что «при проверке UB у вас не хватит способности обнаруживать ошибки», и что в общем случае обнаружение UB связано с проблемой остановки, что, очевидно, невозможнорешить.

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

Оценка константы ... оценка . Вы выполняете программу, а не просто просматриваете исходный код.

Неопределенное поведение происходит, когда выполнение вашей программы делает что-то неопределенное. Большинство случаев UB не могут быть определены как четко определенные или не только из проверки исходного кода. Рассмотрим этот код:

void foo(void *ptr)
{
  *reinterpret_cast<int*>(ptr) = 20;
}

Это UB или нет? По-разному;если бы указатель на int был передан в foo, он был бы четко определен. Правильно ли определен этот код или нет, можно определить только по тому, как он выполняется .

Для вычисления константы требуется выполнение кода;Вот почему мы часто называем это выполнением во время компиляции. Когда вы выполняете код, можно узнать, передано ли конкретное выполнение foo указатель на фактический int (игнорируйте тот факт, что reinterpret_cast запрещен в constexpr коде). Таким образом, во время оценки вы можете знать, происходит ли UB.

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

Это не то, что может случиться. Предполагая, что спецификация завершена и без дырок, то, будет ли выполнение программы демонстрировать четко определенное поведение или нет, является просто вопросом следования спецификации.

Проблема, с которой вы столкнулисьGCC и Clang не связаны с тем, можно ли определить UB или нет.

Более фундаментально, почему в стандарте запрашивается отсутствие UB? Есть ли техническая причина?

Гипотетически, мы могли бы вырвать все неопределенное поведение из C ++ или даже C. Мы могли бы иметь все априори четко определенным и удалить все из языка, оценка которого не моглабыть определенным из первых принципов.

Стандарт не делает этого, потому что это было бы плохо. Это помешало бы нам делать все виды полезных вещей низкого уровня. Это предотвратит полезную оптимизацию компилятора. И т. Д.

Ни одна из этих причин не применима к выполнению кода во время компиляции. Особенно эта целая часть "полезных вещей низкого уровня". Для скомпилированного кода существует реальная машина, на которой выполняется сгенерированный код. Так что иметь заднюю дверь для общения с реальной машиной имеет смысл. Однако во время компиляции не существует реальной машины для общения;есть только C ++ - определенная абстрактная машина. Так какой смысл разрешать UB?

Компилятор не генерирует машинный язык и не выполняет его;постоянная оценка - это в основном выполнение скриптового языка внутри компилятора. И, как и большинство языков сценариев, вы хотите, чтобы он оценивался безопасно и правильно. Вы хотите, чтобы ошибки (а UB - это ошибка ) были быстро обнаружены и предоставили чистое сообщение об ошибке в точке сбоя, а не погибли произвольно позже в процессе.

2 голосов
/ 31 октября 2019

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

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

A constexpr -функция должна иметь только один вход, где она является постоянным выражением, диагностика не требуется. Все остальное может и не быть.

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

0 голосов
/ 31 октября 2019

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

Так что же делают компиляторы, когда они не могут решить, является ли программа UB или нет? Они все еще принимают программу и продолжают компилировать со скрещенными пальцами? Или они более консервативны и отвергают программу, даже если она потенциально верна?

Если они не улавливают UB в программе, они просто компилируют ее. Это связано с тем, что при неопределенном поведении не нужно заботиться о том, что происходит . Поэтому, если у него нет конкретного случая, подобного этому, где ему нужно искать неопределенное поведение, он может предположить, что неопределенного поведения не существует, и все равно его скомпилировать. И в случае, подобном этому, где он должен искать UB, но не ловит его, он, вероятно, все равно его компилирует. Почему бы и нет?

Более фундаментально, почему стандарт запрашивает без UB?

Во-первых, он не запрашивается;требуется, чтобы в вашей программе не было UB!

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

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

Я думаю, что это несовместимо с остальной частью C ++, которая никогда не мешает вам выстрелить себе в ногу. Я бы предпочел, чтобы GCC принимал мой код constexpr и вылетал, или генерировал мусор, если UB;вместо того, чтобы не компилировать, когда он не знает, является ли это UB.

Полагаю, удержание вас от причинения себе вреда - это не то, что семья C делает много, но мы должны быть благодарны за это.

Поскольку GCC принимает ваш constexpr и выходит из строя позже, вы уверены, что это то, что вы хотите? Для одного, который побеждает цель constexpr, а для другого, предположим, что плохой constexpr превращен в рабочий код. Тогда это будет сбой для пользователей / клиентов. Теоретически, не лучше ли поймать это во время компиляции?

Наконец:

Для меня это имеет практическое значение

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

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