Is Type(::x); valid?
Asked Answered
S

2

23

While discussing the Type(identifier); syntax and how it's a declaration, I came across Type(::x); not working with Clang. I would expect that given a global variable x, it would treat ::x as an expression (::x + 2 works) and cast ::x to Type. However, it gives a compiler error.

Here is a short example:

int x;

int main() {
    int(::x); //does not compile
    int(::x + 2); //compiles
}

The compiler error given by Clang 3.5 is:

error: definition or redeclaration of 'x' cannot name the global scope

GCC 4.9.0, however, compiles this just fine. Is this code valid or not?

Snap answered 8/7, 2014 at 3:7 Comment(0)
V
19

As far as I can tell this is covered by draft C++ standard section 8.3 Meaning of declarators paragraph 6 which says (emphasis mine going forward):

In a declaration T D where D has the form

( D1 )

the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration

T D1

Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.

so:

int(::x);

is equivalent to:

int ::x ;

which is clearly not valid, and this produces the same error as well. So gcc 4.9 is not correct here but since this looks fixed in the gcc 4.8.3 which was released later I would expect this to be fixed in later releases of 4.9 as well. Although I don't see any obvious matches for this issue in the gcc 4.8.3 bugs fixed list but they don't claim it is a complete list.

The second case is a functional explicit type conversion which is covered in section 5.2.3 Explicit type conversion (functional notation) which says:

A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).[...]

This is unambiguous since ::x + 2 is an expression.

The section that covers when a statement is considered a declaration or a expression is 6.8 Ambiguity resolution which says:

There is an ambiguity in the grammar involving expression-statements and declarations: An expressionstatement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration. [ Note: To disambiguate, the whole statement might have to be examined to determine if it is an expression-statement or a declaration. This disambiguates many examples.

and provides the following examples:

T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a,5)<<c; // expression-statement
T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration

Note: without the () then T ::D is a qualified-id in the case of T being a class, that is a covered in the grammar of 5.1 Primary expressions.

Update

Filed a gcc bug report.

gcc's response is that:

Current G++ and EDG both treat it as the valid expression (int)::x

Since this response implies clang is incorrect(I don't agree though), I filed a clang bug report and older bug report looks similar and seems to disagree with the gcc response.

Update 2

In response to the clang bug report Richard Smith agrees this should be treated as a declaration and says:

That does not imply clang is incorrect; in fact, Clang is correct here, as far as I can see. (I've also sent a bug report to EDG.)

That said, we should give a proper 'you hit a vexing parse, here's how to disambiguate' error in this case.

Update 3

gcc confirms it is a bug.

Veal answered 8/7, 2014 at 3:41 Comment(18)
Do you know of the part that says this must be treated as a declaration rather than an expression regardless of the declaration being invalid? P.S., the error does change if struct Foo {}; is used as a type :)Snap
"In a declaration T D" (emphasis mine). We have to establish that int (::x); is a declaration before this clause can be applined.Spirit
Foo::x; and Foo (::x); give different errors, where Foo is a class.Spirit
@MattMcNabb that is because in the Foo ::x case the space is removed and it is treated as a qualified id.Veal
But your post says T (D1) is equivalent to T D1. Set T as Foo and D1 as ::x. This seems to reinforce my contention that your first quote only applies to something which has already been established to be a declaration.Spirit
I followed the standard in what exactly a declaration was and indeed int(::x); counts as a declaration.Snap
@Snap can you post an answer detailing that?Spirit
This whole line of reasoning seems fishy; if we have template<typename T> void func() { T (::x); } it should not make a difference to the parsing whether T is int or FooSpirit
@MattMcNabb, It's a long chain, but not answer-worthy with these ones here tbh. I'll put it in a comment when I finish.Snap
@Snap it seems to me that the crux of the matter is whether int (::x); is syntactically valid as a declaration or not. The other answers don't address this other than to assert its truthfulnessSpirit
@MattMcNabb, Of course it also sucks when you forget exactly which path led you there.Snap
@MattMcNabb, Here's the sequence I found: pastebin.com/sh5fuuX5. That's for int(::x);, but can be branched a bit for others I guess.Snap
@Snap I made a mistake, it is a qualified-id without the () when T is a class.Veal
@MattMcNabb, Feel free to use that how you wish as well. It's really too late for me to start creating anything.Snap
With the reassurance from that Pastebin link that Type(::x); is indeed a valid declaration-statement, I feel this answer best explains why the declaration is chosen and what's going on. While I'm sure more could be said about T(::x); vs T ::x;, that really wasn't the question. Thanks.Snap
@MattMcNabb fyi, gcc confirmed this is a bugVeal
"Since this response implies clang is incorrect" If I had meant to imply that I would have closed the PR rather than leave it unconfirmed :-) What I meant is that (int)::x is a valid expression, and GCC and EDG (rightly or wrongly) treat your example as equivalent to that. Richard convinced me that is wrong.Abecedarian
@JonathanWakely apologies, I misinterpreted. It makes sense now that you explain it that way ;-)Veal
O
11

This looks like the most vexing parse to me. If it can be parsed as a declaration it will be. The first could be parsed as a declaration of an int variable (int ::x;), but the :: is illegal in that context. The second has to be an expression and so the compiler does the math, casts it to int, and throws away the result.

Was this a pedantic question, or is there another problem as well? If you have a specific problem you're trying to solve, additional information would allow a workaround for your use case.

Ovovitellin answered 8/7, 2014 at 3:23 Comment(4)
@Matt McNabb That's just the way the C++ language works. It's the same sort of reason we need the typename keyword for templates.Ovovitellin
I'm unclear on what int::x; is supposed to be. Foo::x; would look up x in the scope of Foo, and this is not a declaration. It seems that int::x; is different to int(::x);Spirit
Interesting. I didn't realize an invalid declaration still applied with a valid expression.Snap
@MarkB We need typename to indicate whether an identifier identifies a type or a variable. I don't see how that is similar to the case int (::x); . The rule is that if it is syntactically valid as a declaration then it is a declaration (even if it's also ill-formed as a declaration). However I don't see how int (::x); (at block scope) is valid syntax for a declaration.Spirit

© 2022 - 2024 — McMap. All rights reserved.