An insect has been sitting in your compiler for 13 years and is not going to get out.

Let's imagine that you have a perfect project. Tasks are being cut, the compiler is compiling, static analyzers are analyzing, releases are being released. At some point, you make a strong-willed decision to open an ancient file that no one has looked at for many years, and you see that it is in Windows-1251 encoding. Despite the fact that the entire project has long since switched to UTF-8. “This is not right!” you think, and with a slight movement of your hand, you change the encoding. The next day, a local apocalypse occurs on your test server. Do you think this can't happen? Then I suggest we discuss it.

History of failure

“It just happened that way historically” is a legendary phrase. A universal stopper for juniors and a harbinger of trouble for experienced programmers. In our project, this is the reason why a significant portion of source code files are in Windows-1251 encoding.

At some point, we got tired of problems with broken symbols, Doxygen, and in general, like, it's 2024. There was a need to convert files to UTF-8. And to make it easier for various utilities to automatically detect the encoding, we decided to add a BOM header when converting. We created a task, and progress began: the task was executed, the files were converted. It would seem like an anti-buhurt. But everything is not so simple.

At the same time, another programmer was working on another task. The task was simple: it was necessary to slightly edit the code in one header file. The programmer decided to help his colleagues by slightly reducing their scope of work. To do this, he converted the affected header file to UTF-8 encoding with BOM and committed it. The consequences of the “small” fix overtook the company the next day: the build and all night tests were out of order, and the team leader was furious. They began to sort it out.

When reviewing the compiler output, multiple violation messages were found. one definition rule (ODR). It seemed like someone forgot to add it to the header file. #pragma once. We started looking for the starting point in the commits where the problems started, and we didn't find it. All the edits were fairly simple and didn't add any new header files. However, after some time, suspicion fell on the commit with the change in the encoding of the header file. The suspicion turned into certainty when we did minimal reproducible example.

This example is a very simple project with just a couple of files. File functions.hprecompiled header filecontaining the declaration of the class and function. To protect against double inclusion, it contains #pragma once. Files functions.cpp And main.cpp — these are translation units that include a precompiled header file.

It would seem to be the simplest code. There are no errors. But the project does not compile. If you try to build the project using GCC (tested on versions 12.2.0 and 13.2.0), you will see messages about ODR violation.

But all you have to do is change the file encoding functions.h on UTF-8 without BOM header, all compilation errors will disappear. That's how GCC showed you the trick with disappearance. It's just a pity that it disappeared #pragma once from your project.

After searching for similar reports in the bug tracker, I found this such. It even has a fresh message with a patch that might fix the bug. But so far, all is quiet. There have been no updates for four years, and the ticket status is still unconfirmed. What's remarkable is that the ticket was created in 2013, and the bug hasn't been fixed since then.

What is this if not a buhurt?

Attentive users will tell me: “Wait, 11 years have passed since 2013, why did you write about 13 years in the title then?” The thing is that the ticket has duplicateand it is two years older than the original.

GCC users may start defending their compiler, saying, we have include guardswhy do we need yours pragma? You see, you started a discussion here. Well, if you compile only under GCC and that's enough for you, then there are no questions for you. Unless you write macros and #ifdef'y are less convenient than pragma. But if your product is cross-platform, compiled under different compilers, then you have trouble and sorrow: you will have to either use exclusively guards, or write cumbersome #ifdef's specifically for GCC.

Let's add third-party libraries to the list of problems, which may include #pragma once. Moreover, everything may be fine with the header file from the third-party library. It may not even have a BOM header and be in the encoding you need. And it may not even be precompiled. Your project will still crash, as soon as this header with pragma get into another one, which will be precompiled. For example, in stdafx.h.

And it would be good if you could figure out which file is causing your build to crash. Because very often the precompiled header contains headers that include headers that include other headers, etc. And the problematic file may be at the very bottom of this hierarchy. Even if the very presence pragma won't dump your assembly, then due to the many inclusions of the header in the translation units it can be very slow down.

For GCC pragma — this is a “sore subject”. In addition to the above, you may start having problems with templatesand God knows what else. A huge number of bugs associated with pragmaand the lack of fixes for them gave rise to a culture of “cancellation” pragma” GCC compiler supporters often extrapolate their negative experiences to other compilers and urge not to use #pragma once in general, they say, an unpredictable thing. Although, it would seem, a convenient tool supported by many compilers.

Conclusion

It's been at least 13 years since the bug appeared in the compiler, and four years since a possible fix for it was published. GCC, not it's time or?

Supporting users and fixing their problems in a timely manner is very important for business survival. At PVS-Studio, we devote a lot of time to support and try to promptly issue fixes to users so that they can use the full functionality of the tool. For example, you can read my other article, co-authored with a colleague, about how we sorted through 278 gigabytes of logs.

If you want to learn about different types of bugs and how they might look in code and in practice, I invite you to read the articles of my colleagues:

  1. Smile through the bugs

  2. Not fixed, but worked through the adoption: how some bugs in games became features

  3. Bugs that made a lot of noise

  4. 30 Years of DOOM: New Code, New Bugs

  5. Checking the qdEngine Game Engine, Part One: Top 10 PVS-Studio Warnings

  6. Let's get going! Let's look at the War Thunder engine and talk to its creators

If you would like to share this article with an English-speaking audience, please use the translation link: Grigory Semenchev. An insect is sitting in your compiler and doesn't want to leave for 13 years.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *