ATSTNG's blog

By ATSTNG, history, 4 hours ago, In English

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?
  • Vote: I like it
  • +53
  • Vote: I do not like it