Pull to refresh
4
0

Пользователь

Send message

То, что реализация компилятора не позволила реализовать на нем memcpy не говорит о том, что gcc - это неправильный компилятор языка Си. Это поведение абсолютно валидно с точки зрения стандарта - никто вам такой функционал предоставлять не обязан.

Еще раз. Критерием "баг это или не баг" я считаю статус в багзилле GCC. В багзилле GCC это подтвердили как баг - значит это баг GCC. Баги это по-вашему только те случаи, когда некое поведение компилятора не соответствует стандарту?

Что вы имеете в виду под частично совместимым? C89 - это не частично совместимый с Си язык, а его единственно верный, как уверяет комитет, вариант

То и имею в виду, что допустим вместо C89 выпустили бы "Extended-C", а Си оставили б в состоянии на момет выхода K&R. Это было бы существено лучше, это бы что-то радикально изменило?

Не нравится официальный стандарт - изобретите свой стандарт и сделайте там так, как вам нравится. Есть достаточно много ответвлений от языка Си.

Тут же случилось так, что все написанные к 89-ому году работающие программы на Си неожиданно в одну секунду оказались, согласно стандарту, неработающими.

Совсем-совсем все, т.е. 100% программ вдруг стали неработающими? Неработающими с точки зрения стандарта C89? А с точки зрения K&R они точно все были работающими? В K&R например написано, что бит в байте может быть 8, а может быть больше чем 8 - если какая-то программа полагается на то, что байт 8-битный, она может не заработать на архитектуре с 9-битным байтом. Или наоборот, если предполагается, что байт 9-битный, программа с 8-битным байтом может не работать корректно.

Паскаль всегда был и остается языком высокого уровня, поэтому размер типа Integer там не указан намеренно.

А какое это имеет отношение к "уровневости"? В Си кстати тоже размер int не указан, получается что он в вашей классификации язык высокого уровня? Вот в Java размер типа int как раз указан, выходит что Java это язык низкого уровня? https://docs.oracle.com/javase/specs/jls/se17/html/jls-4.html#jls-4.2 : The integral types are byte, short, int, and long, whose values are 8-bit, 16-bit, 32-bit and 64-bit signed two's-complement integers, respectively, and char, whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1).

Причем сам Error стандарт Паскаля определяет гораздо мягче, чем Undefined Behavior у Си.

Это не отменяет того факта, что программы на языке Паскаль могут перестать работать из-за того, что тип Integer вдруг стал другим, и не вмещает в себя нужный диапазон. Так что же получается, "Паскаль должен умереть"?

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

Где в стандарте языка Си написано, что Си это язык низкого уровня?

Где в стандартах Си или Паскаля указано, что они компилируется в машинные коды?

Нигде. Вполне можно интерпретировать Си, впрочем как и Паскаль. Впрочем, как и ассемблер x86. Я о том, что Си и Паскаль можно компилировать в достаточно эффективный машинный код для фон-неймановских архитектур.

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

А для языка низкого уровня не это безразлично? Писать интерпретаторы ассемблера незаконно? Что значит "для языка безразлично", у него какое-то свое мнение есть? Что вообще такое язык высокого и язык низкого уровня? Я язык Си не считаю языком низкого уровня, язык низкого уровня для меня это ассемблер, машинный код.

Как багом может являться документированное поведение компилятора?

Багом это является по причине того, что в багзилле GCC признали, что это баг.

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

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

Т.е. надо было ничего в Си не менять, а создать новый язык, и было бы всем счастье? Ну допустим не меняли бы они ничего, выпустили бы вместо C89 новый частично совместимый с Си язык, назвали бы его как "Extended-C" например, напихали бы туда этих оптимизаций через неопределеное поведение при стрикт-алиазинге и прочее, на что вы тут жалуетесь, старые программы портировали бы на этот "Extended-C" а сам исконно-посконный Си забросили бы, как забросили языки B и BCPL, и что бы от этого принципиально поменялось?

Си, созданный Ритчи, был языком для конкретной машины. Как только он вылез за рамки PDP-11, начались проблемы.

А в каких случаях проблем бы не было? Вот например в паскале у Integer какой размер в байтах? https://archive.org/download/iso-iec-7185-1990-Pascal/iso-iec-7185-1990-Pascal.pdf - попробуйте там что-нибудь найти про это. На 55 странице этого PDF файла есть упоминание про maxint, который (сюрприз!) implementation-defined. Если посмотреть сюда https://wiki.freepascal.org/Integer - тут сказано Typical sizes of integer generally are 16 bit (2 byte), 32 bit (4 byte) or 64 bit (8 byte) - отлично, т.е. если кто-то в своей программе на паскале предполагает, что Integer 32-битный, на паскале с 16-битным Integer его код корректно не заработает. А если бит не 8-битный? Какие компилируемые в машинный код языки программирования, сопоставимые по уровню абстракции с тем же Си или паскалем, могут работать на (т.е. компилироваться под) архитектурах с не 8-битным байтом, чтобы код при этом не нужно было переделывать? Может надо делать отдельный язык для архитектур с 8-битным байтом, отдельный для 9-битного байта, отдельный для 16-битного байта, отдельный язык для two's complement, отдельный для one's complement знаковых чисел, и так под каждую архитектуру делать язык с особым уникальным именем?

Сейчас уже сами архитектуры подстраивают под языки, например в ARM есть инструкции, добавленные специально для джаваскрипта https://stackoverflow.com/questions/50966676/why-do-arm-chips-have-an-instruction-with-javascript-in-the-name-fjcvtzs

И специально для Си тоже добавляли инструкции чтобы код нормально работал http://c-faq.com/null/machexamp.html

The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to [footnote] all the extant poorly-written C code which made incorrect assumptions.

Это не баг, -nostdlib работает так, как написано в документации gcc:

Ну ок, значит не -nostdlib а какой-то другой флаг не сработал. Я просто привел цитату из багзиллы GCC.

Багом GCC является то, что использование некоторых флагов не позволяет эту оптимизацию отключить. То, что по стандарту (т.е. не используя специальных опций компилятора) нельзя написать свою реализацию memcpy, не является серьезной проблемой языка Си, т.к. во-первых такие функции не надо каждый день писать и во-вторых, это решается специальными флагами компилятора. Серьезных проблем в Си хватает, но это не одна из них

Ну так и сделали бы новый, прекрасный, переносимый, легко оптимизируемый язык

Под который нужно было бы переписывать весь старый код? А кто сказал, что такой язык никто не сделал? Вот D например есть. Или Rust.

Почему не сделали совместимый с Си язык, назвав его по-другому? Тоже сделали, называется он "C++", есть еще "Obj-C". Хотя насчет прекрасности я б тут поспорил.

Зачем было брать прибитый ржавыми гвоздями к архитектуре PDP-11 Си и вводить в заблуждение кучу людей?

Потому что под Си написано много кода, и его хотелось бы не переписывать, а дорабатывать. Введение в заблуждение в чем заключается? Си времен K&R был хорошим, а потом стал плохим? Вот например вы ругаете правила сравнения указателей:

Вот небольшой фрагмент кода, демонстрирующий некорректное с точки зрения стандарта сравнение:

int *p = malloc(64 * sizeof(int));
int *q = malloc(64 * sizeof(int));
if(p < q) /* Undefined behaviour! */
    do_something();

Только вот это было еще в K&R, даже можно найти архивы ньюзгрупп, где обсуждалось еще в 1988 году https://compilers.iecc.com/comparch/article/88-07-002

>K&R 1, page 98:
>"But all bets are off if you do arithmetic or comparisons with pointers
>pointing to different arrays. If you're lucky, you'll get obvious
>nonsense on all machines. If you're unlucky, your code will work on one
>machine but collapse mysteriously on another."

Т.е. реализация memcpy должна быть настолько сложной, чтобы компилятор не смог оптимизировать ее исходный код - прекрасно!

Скорее так: если нужно реализовывать memcpy и прочие функции из стандартной библиотеки Си, нужно использовать специальные флаги компилятора или прагмы, не дающие компилятору соптимизировать это в рекурсию. Программисты редко когда свой memcpy реализовывают, так что это не то чтобы большая проблема.

Почему вы считаете, что это баг компилятора? Компилятор ведет себя в соответствии со стандартом (4.1.2 Standard headers):

Потому что см. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56888#c12 :

Now, if this replacement still happens when you compile with -nostdlib, that would be a bug since it becomes legal code in that case.

Т.е. флаг -nostdlib по-идее должен это разрешать, а он не разрешает. Там советуют использовать опцию -fno-tree-loop-distribute-patterns

Можно использовать для таких функций attribute ((optimize ("-fno-tree-loop-distribute-patterns")))и тогда должно нормально компилироваться без рекурсий.

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

Для переносимости и для более агрессивной оптимизации.

Мы упомянули о том, что пароль (или его часть) может остаться в регистре. На самом деле ответ прост - в рамках самого языка Си эта проблема неразрешима. В тексте стандарта даже, очевидно, слово "stack" не упоминается - об очищении чего тогда вообще может идти речь?

А где она разрешима? Кроме ассемблера, я не слышал о других языках, которые бы давали такой уровень контроля. Как такой язык должен выглядеть, если это будет не ассемблер?

Но вообще можно придумать некую специальную прагму, которая заставит всё тот же компилятор Си генерировать код зануления стекфрейма и регистров по выходу из функции.

Возьмём следующий фрагмент кода на языке Си:

int x = 1;
x = x << sizeof(int) * 8;

Попробуем предположить, какой результат у нас получится.

Если размер байта больше 8 бит (такое вполне бывает и разрешено стандартом), ничего страшного не произойдет. Если хочется получить UB, лучше использовать макрос CHAR_BIT

int x = 1;
x = x << sizeof(int) * CHAR_BIT;

Кстати, тут можно и unsigned int взять. Если unsigned int 32-битный, сдвиг единицы на 32 тоже будет UB.

Для своей операционной системы разработчик решил использовать собственную реализацию функции memset. Но он не учёл, что в процессе трансляции компилятор gcc обнаружит в этом коде весьма заманчивую возможность для оптимизации.

Да, есть такой баг. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56888

Только вы определитесь, вы конкретный компилятор Си ругаете, ли сам Си?

Ваша программа работает с базой данных пользователей, хранящей их имена и пароли, и вы описали примерно такую функцию:

int check_password(const char *pwd)
{
    char real_pwd[32];
    get_password(real_pwd);
    return !strcmp(pwd, real_pwd);
}

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

А еще существует вероятность, что какие-то куски пароля останутся в каких-нибудь регистрах, как это проконтролировать? Может тогда на ассемблере писать, ну что уж точно ничего случайно не просочилось? И кстати пароли в открыдом виде не хранят, а хранят их хэши.

Кстати, по поводу хешей. Взять например SHA256Transform за авторством Aaron Gifford которое присутствует в OpenBSD например http://fxr.watson.org/fxr/source/crypto/sha2.c?v=OPENBSD#L309

Там есть такая замечательная строчка, как a = b = c = d = e = f = g = h = T1 = 0; , где все эти a b c d ... являются локальными переменными в этой функции. Что вообще автор хотел этим сказать? Видимо он хотел избежать утечки каких-то данных через стек, и даже возможно через регистры. Компилятор это зануление локалок конечно имеет право выкинуть, но ДОПУСТИМ он не выкинет их, и действительно занулит соответствующие регистры и стек. Только в стеке и в регистрах могут быть записаны какие-нибудь промежуточные результаты вычислений, а их как вообще занулить? Писать специализированный компилятор, который все использованные функцией адреса в стеке и все использованные регистры (кроме возвращаемого значения) забивает нулями?

Не отвечает на основные вопросы философии: "кто я?", "зачем я живу?", точнее отвечает, но ответ логически бессмысленен.

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

ну вы же выше цитату привели, там написано.

Мне из цитаты неясно, что такое "суть". Нужно определение слова "суть" в данном контексте.

Доказать не-сущестсование нельзя. Это научный метод. Чайник Рассела погуглите

Доказать не-существование чайника Рассела в определенном конкретном месте и в определенное время очень даже можно. Например, какой-нибудь телескоп доказывает через свои камеры, что в некоторой конечной области пространства сейчас никаких чайников нет. И космонавты в открытом космосе тоже доказывают, что мимо них чайники сейчас не пролетают. А доказать "вообще нигде во Вселенной нет ни одного чайника" будет уже затруднительно.

Кстати, а как бы вы доказывали, что между Землёй и Марсом вокруг Солнца вращается по крайней мере один чайник Рассела? Допустим, кто-то шутки ради действительно отправил на орбиту такой чайник, и его удалось увидеть на снимках с телескопа, но где гарантии, что снимки с телескопа не были специально подделаны какими-нибудь шутниками? А где гарантии, что всё это не ваш сон? Что будет считаться доказательством?

Если есть только ничто - значит у нас есть 0 единиц «чего-то»? По идее да, но ведь есть само - ничто. Значит 1 единича чего-то? Значит уже ничто - это не «никакого чего-то», а значит логический тупик.

По каким причинам мы само наличие "ничто" принимаем за "1 единицу чего-то"? Оно же на то и "ничто", что его нет, так что никакой "1 единицы ничего" нет, и дальнейшие рассуждения смысла не имеют.

Иррациональное мышление живет в идеалистическом мире, который живет в хаосе, не подчиняясь никаким законам.

Наличие каких-либо законов физики нельзя строго доказать. Можно поставить счетное и конечное множество экспериментов, и на основе полученных данных можно построить некую физическую теорию, по которой можно было бы с какой-то точностью предсказывать "что будет, если ...".

Сначала было ничто. То есть не было ничего. Иначе - ничто это отсутствие всего.

Где (в чем) оно было? Или оно было "само в себе"? Что вообще означает "быть"? Откуда мы можем знать, что это "ничто" реально когда-то было? А что если "быть" вообще неприменимо к "ничто"?

В ничто не было ни пространства, ни материи, ни отношений, ни движения, ни чего-либо иного кроме самого ничто.

ОК, допустим. И зачем понадобилось предполагать, что когда-то в самом начале было некое "ничто"?

Когда было одно лишь ничто, и ничего другого - существовало ли оно? Существовать - значит иметь в себе суть, то, что в полной мере показывает его отличие от чего-либо другого.

Что значит "иметь в себе суть"? Что такое "суть"?

Однако если у ничто была суть, то есть отличие - значит ничто было не одно, ведь невозможно описать отличие ничто от ничто - ведь это одно и то же.

Пусть будет одно единственное "ничто", и никаких других "ничто" не будет. Почему должно быть проблемой то, что кто-то в рамках некоторой формальной системы что-то не может описать?

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

А если логика неправильная, и на самом деле существование "ничто" ни к какому существованию "нечто" не приводит? Что такое "нечто"? И кстати, какая логика, классическая или интуиционистская(нет закона исключённого третьего)? Или может какая-то своя? Есть еще многозначные логики, модальные логики и проч.

Откуда взялась уверенность, что используемая вами логика адекватно описывает предметную область, и дает верные выводы о сущности вещей, что что-то там существует если вот то существует? См. https://youtu.be/u1AyoXrXYe4?t=1263 (почему можно поставить под сомнение тезис, что 2*2=4)

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

Я не вижу никакой проблемы в том, чтобы было только "ничего" (что равнозначно "ничего не было бы вообще"). Никакое "нечто" вообще не требуется. Просто ничего нет, и всё тут. В чем проблема вообще?

Именно поэтому невозможно доказать не-существование чего-либо.

Что значит "доказать"? Не-существование чего? Не-существования чего-то где-то иногда очень даже можно доказать. Например, можно посмотреть на пустой стакан, и это будет доказательством не-существования воды в этом стакане. Или это доказательством не является? Например, можно допустить, что у меня галлюцинации, и на самом-то деле вода там есть. Но тогда я и существование воды в стакане доказать не могу.

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

Сейчас весь скрам превратился в полный buzzword. Например, extreme programming пропагандирует техническое совершенство — но где это совершенство?.. А скрам теперь просто buzzword, оно больше ничего не стоит. И даже Кен Швабер, изначальный создатель скрама, ушел из Scrum Alliance, потому что это все превратилось в армию консультантов: они прошли двухдневный курс и говорят, что знают, как наладить процессы в организации. Это стало религией, которую никто не знает, как практиковать.

Но мы можем делать лучше. Через какое-то время появился новый манифест. Манифест получился еще круче, он назывался «Manifesto for Software Craftsmanship».

Слово "скрам" превратилось в баззворд? Не проблема, мы придумаем вам новое слово "крафтсмен", которое тоже превратится в баззворд, и тогда мы придумаем еще какое-то слово...

Основные компоненты Флиппера написаны на C, C++ и Rust, поэтому для разработки пригодится знание этих языков.

А зачем смесь сразу из трех языков использовать? Если используется Rust, зачем тогда C++, почему не оставить C, Rust?
Ну так это давно известно, такие аномалии выявлялись и ранее. Лекция поводу математики выборов и статистических аномалий некоторых голосованиях, проводившихся в РФ youtu.be/Q-RgoV_w8Vw которая была прочитана еще 22 декабря 2017 года. Там говорят про такие же аномалии с зубчиками на 80%, 85%, 90% и 95% — на этом моменте youtu.be/Q-RgoV_w8Vw?t=2529
Нет, не подходит godbolt.org/z/cc9479

Можно перебирать разные UB, может какое-то еще подойдет
Нельзя, даже когда нужно: по стандарту Си, это UB независимо от платформы.

Нельзя, но если очень хочется, то можно.
Посмотрим, что по этому поводу написано в документации GCC
gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
-fdelete-null-pointer-checks

Assume that programs cannot safely dereference null pointers, and that no code or data element resides at address zero. This option enables simple constant folding optimizations at all optimization levels. In addition, other optimization passes in GCC use this flag to control global dataflow analyses that eliminate useless checks for null pointers; these assume that a memory access to address zero always results in a trap, so that if a pointer is checked after it has already been dereferenced, it cannot be null.

Note however that in some environments this assumption is not true. Use -fno-delete-null-pointer-checks to disable this optimization for programs that depend on that behavior.

This option is enabled by default on most targets. On Nios II ELF, it defaults to off. On AVR, CR16, and MSP430, this option is completely disabled.

Passes that use the dataflow information are enabled independently at different optimization levels.


Могу раскрыть еще одну «страшную тайну» — использование dlsym() для получения указателей на функции это UB
pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html:
Note that conversion from a void * pointer to a function pointer as in:

fptr = (int (*)(int))dlsym(handle, "my_function");


is not defined by the ISO C standard. This standard requires this conversion to work correctly on conforming implementations.

Стандарт C99 явно запрещает преобразование void * в указатель на функцию www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf#%5B%7B%22num%22%3A116%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C-27%2C816%2Cnull%5D
8 A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

stackoverflow.com/a/13697654
Теперь живите с этим :)
Там спрашивали, как указать компилятору диапазон значений переменной при помощи стандартных средств Си.

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

Пример интересный, но для меня это не ново. Я бы это не называл стат. анализом, это скорее результат оптимизации на основе UB от разыменования нулевого указателя. И это зависит от опции -fdelete-null-pointer-checks
Я б лучше бы взял __builtin_unreachable():
godbolt.org/z/EEaP4j
#define ALWAYS_FALSE(a) if(a) __builtin_unreachable()
#define ALWAYS_TRUE(a) ALWAYS_FALSE(!(a))

int test_array(unsigned char a[10])
{
  for (int i = 1; i < 10; i++)
  {
    ALWAYS_TRUE(a[i-1] <= a[i]);
  }
  return a[0] <= a[2];
}


Тот цикл for() с ALWAYS_TRUE гарантирует компилятору, что массив сортирован, и данная функция соптимизируется в GCC (но не Clang) до return 1;
Кстати если сделать return a[0] <= a[3] в этом примере, оно уже так хорошо не соптимизирует, видимо есть какие-то ограничения на глубину такого анализа.
Чем вас не устраивает, что два этих варианта совмещены в одном бинарнике, и опции компиляции включают/отключают конкретные анализы?

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

Да, проводит. Я говорил о гипотетическом сценарии, если сделать два варианта Clang, один из них будет заточен чисто для стат. анализа, чтобы именно переводить в какое-то удобоваримое для стат. анализатора представление, другой чисто под компиляцию. Можно пытаться совмещать это, тут вопрос в целесообразности.

Вот например такой код, на который сейчас и GCC и Clang жалуются
warning: this 'for' clause does not guard… [-Wmisleading-indentation]
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  for (int i = 0; i < 10; ++i)
    printf("%d\n", i);
    printf("blablabla\n");
  return EXIT_SUCCESS;
}

Если мы хотим максимально быстро всё скомпилировать, нам совершенно не нужно эти отступы в начале строки учитывать, они никакой погоды не делают. Учет количества пробелов на второй строчке после оператора for без фигурных скобок совершенно не нужен для компиляции, и с т.з. компиляции это лишние телодвижения, хотя и не очень затратные.
«Clang со встроенным Coverity» заведомо отработает быстрее, чем связка «Clang отдельно и Coverity отдельно», хотя бы потому что парситься исходник будет только один раз.

Да, отработает быстрее, при условии что какие-то начальные стадии (парсинг исходного кода и приведение его к какому-то представлению) будут общими. Но они могут не быть общими, и для стат анализатора может требоваться сильно другой подход. Например для стат анализа можно сделать специальным образом пропатченный Clang, который бы сохранял какую-то дополнительную информацию и переводил бы его в какое-то специализированное абстрактное представление, которое нужно именно для задач статического анализа, а обычный Clang-для-компиляции никакого стат. анализа вообще не проводит, и максимально быстро транслирует в то представление, которое требуется именно для компиляции. Статический анализ без компиляции и компиляция без статического анализа тоже может требоваться.

С другой стороны, можно применить подходы инкрементального статического анализа, когда где-то в IDE при изменении и сохранении конкретного файла с исходным кодом, «переанализирование» производилось бы только этого файла и тех файлов, которые его инклудят, «переанализированный» кусок абстрактного представления, соответствующий измененному файлу замещал бы то, что там было до этого, и производился бы стат. анализ без полного переанализирования, но при этом никакой перекомпиляции (даже инкрементальной) и запуск кучи юнит-тестов на каждую правку мы не хотим делать.

Полагаю, Andrey2008 может ответить по теме более подробно. Я не эксперт в стат. анализаторах.
А зачем добавлять предварительный запуск специального статического анализатора, если можно всё нужное встроить в компилятор?

Тут сначала надо понять, что же является нужным. Если встраивать функционал Coverity или PVS-Studio, это приведет к очень долгой компиляции. Статические анализаторы смотрят на исходный код совершенно не так, как на него смотрят компиляторы, у компиляторов задача — перегнать побыстрее человекочитаемый исходный код в некое абстрактное представление, и дальше с ним делать какие-то манипуляции. У стат. анализатора задачи совсем другие, и это можно даже понять по тем ошибкам, которые та же PVS-Studio умеет диагностировать, более подробно о подходах можно почитать например там www.viva64.com/ru/b/0592, хотя наверняка есть и более серьезные научные статьи на эту тему. Для компиляции это всё лишнее, сильно ее замедлит, и эффективный стат. анализ может с компиляцией вообще никак не пересекаться, т.е. оперировать другим абстрактными представлениями, которые для целей компиляции не нужны.

Если встраивать что-то меньшее — ну ок, встраивать можно. Мне лично больше нравится юникс вей: Пишите программы, которые делают что-то одно и делают это хорошо.
Для Google всегда были важны надёжность и безопасность программ — например, одно из правил компании требует обязательного добавления статической проверки в компилятор для каждой новой ошибки, обнаруженной в продакшене. Добавить подобного рода проверку в Clang гораздо проще.

А зачем это встраивать в компилятор? Ведь можно в сборочный скрипт добавить предварительный запуск специального статического анализатора(или встроить это в систему непрерывной интеграции, чтобы проверка стат. анализатором запускалась на каждом коммите), сам же компилятор можно вызывать с вообще полностью выключенными варнингами, это не его основная функция.
Ещё один аргумент в пользу Clang — поддержка Windows. GCC органически не подходит для Windows, и хотя есть версия GCC и для этой операционной системы, серьёзную разработку с её помощью вести нельзя. Некоторые вещи, например поддержку отладочной информации в формате PDB, в GCC в принципе невозможно добавить — всё из-за тех же лицензионных ограничений.

Вот с этого места поподробней. Каким образом отсутствие поддержки PDB в компиляторе GCC мешает серьезной разработке? У GCC есть поддержка DWARF, чем он не устраивает? Или под полноценной разработкой понимается совместимость с MSVC? Тогда и Clang для серьезной разработки не подходит, ведь там неполная поддержка SEH
clang.llvm.org/docs/MSVCCompatibility.html:
Asynchronous Exceptions (SEH): Partial. Structured exceptions (__try / __except / __finally) mostly work on x86 and x64. LLVM does not model asynchronous exceptions, so it is currently impossible to catch an asynchronous exception generated in the same frame as the catching __try.
Очень много фактических ошибок в статье.
То же самое касается поддержки языка C++. Ричард Смит, инженер компании Google, выступающий секретарём комитета ISO по стандартизации C++ — иными словами, тот человек, кто собственно пишет текст всех дополнений стандарта своими руками — является маинтейнером фронт-энда Clang. Многие другие заметные участники комитета также являются активными разработчиками Clang / LLVM. Сложите эти два факта, и вывод очевиден: поддержка новых дополнений в языке C++ раньше всего появляется именно в Clang'е.

Нет, из этого такой вывод нельзя сделать. Более того, на самом-то деле это не так, сейчас поддержка стандарта C++20 в GCC лучше, чем в Clang. См. gcc.gnu.org/projects/cxx-status.html#cxx2a и clang.llvm.org/cxx_status.html#cxx20 — можно даже чисто визуально по количеству красного понять, что в GCC поддержка лучше. Концепты в GCC уже почти реализованы, а в Clang до этого еще далеко.

Ещё одно важное преимущество Clang — и как мы знаем, главная причина перехода команды Android на LLVM — развитая поддержка статической верификации.

Вы видимо путаете статический анализ со статической верификацией. Верификатора в Clang нет. Есть проекты, использующие Clang и LLVM, которые такой функционал предоставляют. Clang Static Analyzer ( clang-analyzer.llvm.org ) -это не верификатор. Он ничего не доказывает. Clang и LLVM не являются верификаторами, но могут быть использованы как фронт-энд для верификаторов — см. seahorn.github.io
Clang разрабатывался как полностью совместимая замена GCC, так что в стандартном проекте для перехода достаточно просто поменять имя компилятора.

Нет, не полностью. Например, не поддерживаются nested functions из GCC — lists.llvm.org/pipermail/cfe-dev/2015-September/045035.html
Если с демонами не повезёт, вы точно заметите быструю скорость компиляции и линковки, улучшения в производительности, оптимальное использование новых инструкций ARM

Нет, это не точно. Может быть для ARM архитектур это действительно так (не проверял), но вот тесты phoronix для x86-64 показывают, что на некоторых тестах Clang быстрее, на некоторых — GCC, и явного перевеса ни у кого из них нет.

Information

Rating
Does not participate
Registered
Activity