Блог пользователя ATSTNG

Автор ATSTNG, история, 14 месяцев назад, По-английски

Apparently $$$24$$$ hours without sleep and months of coding in scripting languages had the effect on me... During the contest I submitted this 310073998

Quick challenge: pause reading and find the bug in my solution

Link to original art

When I found out that I failed a div3D on not properly using int64_t I was quite surprised, because I double checked that I did it right. I felt very stupid at first, but then I got surprised even more when I figured how exactly this happened. Take a look at the line $$$25$$$

        const& r = cr[i];

It appears that GCC allows to declare variable references omitting type declaration completely when cv-qualifier is present. Yes, without invoking type deduction using auto. To be precise expression in form

<cv-qualifier> &<variable-name> = <value>;

appears to be well-formed and compilable.

But what type you will end up using? You might expect GCC to be smart enough to put auto for you, but you are wrong. In this special case type will always default to int. In many cases this will immediately make code ill-formed due to <value> not being convertable to int. But in my case int64_t is actually implicitly convertable to int. Which is exactly what happened in my code: r gets int type and r*r gets truncated to $$$32$$$ bits.

Funny enough, not even #define int long long will save you in that case

Isn't that amazing?

Yes, -Wconversion compiler option will correctly produce the warning on creating defaulted-to-int reference to int64_t variable.

But why the hell this is even compilable in the first place!?

It appears that this default-to-int behaviour is a part of K&R C standard of C-language aging back to year $$$1978$$$. Almost the same age as my parents are.

Trying to reproduce and explore this issue I ended up with this code

#include <cstdio>

int main() {
    long long int v1 = 1'000'000'000'001;
    const &r1 = v1;

    volatile int v2 = 42;
    volatile &r2 = v2;

    volatile int v3 = 42;
    const volatile &r3 = v3;

    printf("GCC %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
    printf("r1 = %d\n", r1);
    printf("r2 = %d\n", r2);
    printf("r3 = %d\n", r3);
}

It appears to be compilable on all $$$3$$$ available G++ compilers on CF and on my local GCC 11.2.0 producing expected result of

GCC 11.2.0
r1 = -727379967
r2 = 42
r3 = 42

Exploring it in GodBolt I learned that this is a part of -fpermissive behaviour of GCC. But still, all versions of GCC that I checked in GodBolt will produce compile errors without -fpermissive option and produce warnings with -fpermissive. Other compilers also produce compile errors.

But I did not enable -fpermissive locally, neither did CodeForces (as long as Codeforces Command Lines (2023-10-06) are up to date). So, there are two questions to this:

  1. How do I configure GCC locally to produce compile errors on such code?
  2. How do I make CodeForces give compile errors to such code?
  • Проголосовать: нравится
  • +116
  • Проголосовать: не нравится

»
14 месяцев назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

Is this a windows thing? locally I am seeing the error with codeforces compile options: g++-14 -std=c++23 -Wall -Wextra -Wconversion -static -DONLINE_JUDGE -Wl,--stack=268435456 -O2 submit.cpp -o bin/submit

submit.cpp:5:12: error: ISO C++ forbids declaration of 'r1' with no type [-fpermissive]
    5 |     const &r1 = v1;
My GCC
»
14 месяцев назад, скрыть # |
Rev. 2  
Проголосовать: нравится +8 Проголосовать: не нравится

Turns out it is actually a Windows issue, more precisely a MinGW issue. Thank you, TianyiChen, for pointing in that direction. I did not expect default behaviour of GCC to be platform-dependent.

Among many other extensions GCC has ms-extensions, that aim to make language closer to MSVC dialect allowing a set of non-standard C/C++ features from it.

StackOverflow: What does the -fms-extensions flag do exactly with GCC?

For some reason ms-extensions are enabled in MinGW by default.

The problem is that ms-extensions were not properly updated for like $$$20$$$ years while GCC, MSVC and C++ were making a large progress. So ms-extensions still assume that implicit-int should be allowed for MSVC compatibility, while neither of aformentioned $$$3$$$ independently assume it.

Big thank you for Andrew Pinski, who clarified this https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119400

Answering the questions from the blog:

(1) ms-extensions are controlled with antagonistic compilation flags -fms-extensions / -fno-ms-extensions. Using the second one prevents this undesired behaviour. The victory!

(2) I believe that, unlike optimization flags with pragma GCC, -fno-ms-extensions unfortunately cannot be set from inside source code.

Here a proposition to add -fno-ms-extensions to CodeForces GCC compile options can be made, but I'm not sure. On the one hand, why someone who might have nothing to do with Windows and MSVC at all must have their code compiled with questionable extensions, that are here just by default and not by someone's conscious decision? On the other hand, this is very radical solution to seemingly non-problem: I'm likely the only one who got affected by this in any noticeable way on CF ever. And still it happened just because I was careless and made the mistake by myself in the first place.

MikeMirzayanov Vladosiya may be you will have more concrete opinion in this

»
14 месяцев назад, скрыть # |
 
Проголосовать: нравится -6 Проголосовать: не нравится

I would just write the i64. Its 3 characters and makes the code easier to both read and debug