Search code examples
c++gcclanguage-lawyergcc11

GCC 11.x vexing-parse + inconsistent error (redeclaration as different symbol type), is it a GCC bug?


The following code compiles with no problems from GCC 4.7.1 up to but not including GCC 11.1:

constexpr int SomeValue = 0;

void test () {
    void (SomeValue) ();
}

On GCC 11.x it fails with:

<source>:4:23: error: 'void SomeValue()' redeclared as different kind of entity
    4 |     void (SomeValue) ();
      |                       ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
    1 | constexpr int SomeValue = 0;
      |               ^~~~~~~~~

But the error "redeclared as different kind of entity" seems strange to me: Ambiguous parsing possibilities aside, the scope is different. Also, these tests all compile on all versions of GCC since 4.7.1 (including 11.x), even though AFAIK each one is redeclaring SomeValue as "a different type of entity":

constexpr int SomeValue = 0;

void test1 () { typedef void (SomeValue) (); }
void test2 () { double SomeValue;            }
void test3 () { using SomeValue = char *;    }
void test4 () { void (* SomeValue) ();       }
void test5 () { struct SomeValue { };        }
void test6 () { enum class SomeValue { };    }

As a relatively less nonsensical example, this code also fails from 11.x on in a similar fashion:

constexpr int SomeValue = 0;

struct SomeClass {
    explicit SomeClass (int) { }
    void operator () () { }
};

void test () {
    SomeClass(SomeValue)();
}

Although in this case it's preceded by a vexing-parse warning that also isn't present before 11.x (the fact that the warning is here but not in the above makes sense, the fact that the warning doesn't appear pre-11.x is the interesting bit):

<source>: In function 'void test()':
<source>:9:25: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
    9 |     SomeClass(SomeValue)();
      |                         ^~
<source>: At global scope:
<source>:9:26: error: 'SomeClass SomeValue()' redeclared as different kind of entity
    9 |     SomeClass(SomeValue)();
      |                          ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
    1 | constexpr int SomeValue = 0;
      |               ^~~~~~~~~
Compiler returned: 1

But wait! There's more!

This code -- which I would have expected to fail on 11.x due to the same parsing ambiguities as above -- compiles just fine on all those versions of GCC (including 11.x):

constexpr int SomeValue = 0;

auto closure = [] (int) {
    return [] () { };
};

void test () { 
    closure(SomeValue)(); // <-- doesn't cause any problems
}

No warnings or anything there.

So... What's going on here? Why is it only a problem for SomeValue to be "redeclared as a different kind of entity" in those specific cases, and only since GCC 11.1, and why doesn't closure(SomeValue)() suffer the same problem as SomeClass(SomeValue)()?

Also what changed? Is GCC correct here? Is it a new bug introduced in GCC 11.x? Or perhaps an old bug that was finally fixed in 11.x? Or not a bug at all and something else changed?

I'm struggling to come up with a consistent explanation.


Solution

  • The difference is that your first snippet declares a function that exists globally; all your other declarations are of local entities.
    (Note that even if the declaration were valid, you couldn't call that function, since it can't exist.)

    In the last snippet, closure is not a type, so it can't be a declaration.