59

Consider the following program:

extern int x;
auto x = 42;
int main() { }

Clang 3.5 accepts it (live demo), GCC 4.9 and VS2013 do not (live demo for the former). Who is right, and where is the correct behavior specified in the C++ Standard?

M.M
  • 138,810
  • 21
  • 208
  • 365
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • As of today, GCC development branch is 8.X and this bug is still here: https://godbolt.org/g/Kpjg11 – YSC Jun 18 '18 at 11:17
  • For more answers see https://stackoverflow.com/questions/52304410/ – M.M Sep 13 '18 at 00:32
  • 1
    I got a [reply from Richard Smith](https://stackoverflow.com/a/52304618/1708801) about this and clang is correct, see my answer there for details. I requested a question merge since they are identical. – Shafik Yaghmour Sep 15 '18 at 14:15
  • @ShafikYaghmour clang is correct in the opinion of a clang developer... great :] IMHO it should still be treated as underspecified until something actually gets into a standard draft – M.M Sep 17 '18 at 05:55
  • @M.M the logic is sound, if this was disallowed w/o `[dcl.spec.auto]p11` then there would be no need for `[dcl.spec.auto]p11` to ban it for deduced return types or if we have to explicitly disallow it for deduced types than it is not already disallowed since the catch-all would do the job. – Shafik Yaghmour Sep 17 '18 at 05:59
  • @M.M if you not convinced then you should file a defect ... I can explain how. – Shafik Yaghmour Sep 17 '18 at 06:07
  • 1
    @M.M: This is CWG2389 (not publicly published yet); yes, this is allowed. – Davis Herring Nov 07 '19 at 01:50

3 Answers3

26

There's surprisingly little in the standard about this. About all we hear about redeclaration is:

[C++11: 3.1/1]: A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. [..]

and the only relevant part of auto's semantics:

[C++11: 7.1.6.4/3]: Otherwise, the type of the variable is deduced from its initializer. [..]

(reminding us that the type of x is int).

We know that a variable must be given the same type by all declarations:

[C++11: 3.5/10]: After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.

and the "after all adjustments of types" ought to take care of any questions regarding auto's participation in all of this; my interpretation, then, is that this is inherently a valid redeclaration (and definition) of the x at global scope with type int, and that Clang is correct. Even if we propose that auto does not count as "adjustment of type", since no diagnostic is required, at worst all listed implementations are compliant in their own way.

I believe GCC and Visual Studio are taking the following as inspiration:

[C++11: 7.1.6.4/5]: A program that uses auto in a context not explicitly allowed in this section is ill-formed.

…but I think that this is short-sighted. It seems unlikely that the standard language is intended to prohibit the usual redeclaration rules, just because they are not repeated or explicitly referenced from within 7.1.6.4.

C++14 adds wording that relates to declarations of functions with deduced types:

[C++14: 7.1.6.4/13]: Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [..]

By symmetry one might suggest that, in your int case, it is intended that GCC and VS be correct in rejecting the program. However, this is a different feature (since deduction cannot be applied to mere declarations) and thus a different scenario.

Either way, improved standard wording would help here. I consider it a [reasonably minor] editorial defect.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I think that is what it intends to prohibit, to rule out constructs like `extern int i; auto i = decltype(i)();`, which implementations might have a lot of trouble with. Or worse, `extern int i; auto j = decltype(i)(); auto i = decltype(j)();`. Does that violate the restrictions on a variable declared as `auto` appearing in its own initialiser? –  Oct 15 '14 at 15:41
  • @hvd: I don't see a big problem with either of those. `i` is already in scope and its type is known, in both cases. – Lightness Races in Orbit Oct 15 '14 at 15:42
  • Yet clang rejects the first (`i` lexically appears in its own initialiser, and that is how it implements the rule) and accepts the second. –  Oct 15 '14 at 15:43
  • @hvd: That's only a problem with the `auto`. Declaring and defining as `int`, `i` is in scope within its own initialiser (the prior declaration notwithstanding). Evaluating it would be UB due to not being initialised yet, but inside `decltype` is an unevaluated context. Now, with `auto`.... I dunno. I think that still falls under the umbrella of what the OP is asking. – Lightness Races in Orbit Oct 15 '14 at 15:44
  • @hvd That may indeed be a clue to intent. I'd still prefer a Note or something cos if this _is_ the intent then it's kinda silly. – Lightness Races in Orbit Oct 15 '14 at 15:46
  • 2
    I'm getting a bit confused, but one thing I can definitely agree on: improved wording would help greatly. –  Oct 15 '14 at 15:47
  • I can't find in the standard requirement that two declarations of a single object need to have same type/declaration/?. Shouldn't it be somewhere? I think if such a rule exists its wording should resolve the issue. – zch Oct 15 '14 at 15:56
  • @zch: It's the first quote in my answer, combined with the definition of _name_ in chapter 3 (in short, a _name_ refers to an object, and we all know that two expressions consisting solely of names referring to the same object cannot have different types). – Lightness Races in Orbit Oct 15 '14 at 16:16
  • 1
    I think 3.5p10 applies. It allows for "After all adjustments of types". I think if there is any doubt about whether the placeholder type might be considered a different type from the "int", then the "after adjustments of types" will allow for it. So I think that the code is valid because at the end both types agree. But even if it would be invalid, the paragraph also says that a violation doesn't require a diagnostic. But I am not at all sure about my analysis. – Johannes Schaub - litb Oct 18 '14 at 11:25
  • @zch see my comment. The rule is at 3.5p10 – Johannes Schaub - litb Oct 18 '14 at 11:27
  • @JohannesSchaub-litb: I concur. – Lightness Races in Orbit Oct 18 '14 at 17:28
  • 1
    You've phrased this answer as if it's the case that, when 7.1.6.4/5 says, "this is all the contexts in which `auto` is allowed", what it really means is, "oh yeah, apart from another one which we forgot to mention". But the way I read it `auto` *is* being used in one of the listed contexts. Specifically, "This use of auto is allowed when declaring variables in a block (6.3), in namespace scope (3.3.6)...". It's a variable declaration in the global namespace scope. All that's left to determine is whether resolving `auto` really is an "adjustment to types": if so then gcc plain failed to do it. – Steve Jessop May 24 '16 at 09:43
  • C++11 7.1.6.4/5 does not apply because the context in the question is in the list of explicitly allowed contexts (declaration of variable at namespace scope) – M.M May 24 '16 at 09:48
  • suggest updating the answer to use non-numeric section specifier, and use C++14 references – M.M May 24 '16 at 09:50
  • Actually as I've discussed this with M.M. under http://stackoverflow.com/questions/37407547/ I'm coming to the conclusion that resolving `auto` probably isn't even an "adjustment to types". Rather, it's an alternative way of the programmer writing "the type specified by" the second declaration. Either way, gcc should have done it. – Steve Jessop May 24 '16 at 10:02
3

Note

I answered a question that was closed a duplicate of this one. I asked for merge and was told instead to provide an answer here. See below for my original answer.

Update clang is correct

I asked this question on twitter and the response I received from Richard Smith was as follows:

Not a defect, it's intentional that this restriction applies only to deduced return types and not to variable types. For variables, it's just a convenience shorthand, but return type deduction affects something more fundamental about functions (and especially function templates).

So the logic is that this is allowed by [dcl.spec.auto] and to restrict this for deduced return types paragraph [dcl.spec.auto]p11 was added to the section. Otherwise there is no restriction and therefore this is not restricted for the variables case.

Original

Currently [dcl.spec.auto] does not seem to cover this case explictly but it does say in [dcl.spec.auto]p5:

A program that uses auto or decltype(auto) in a context not explicitly allowed in this subclause is ill-formed.

and we can see it makes a similar case for functions ill-formed in [dcl.spec.auto]p11:

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. Similarly, redeclarations or specializations of a function or function template with a declared return type that does not use a placeholder type shall not use a placeholder. [ Example:

auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()

....

So although this could use clarification as currently worded it feels like gcc is correct and this is ill-formed.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
0

I'd imagine the restriction in [dcl.spec.auto]p11 exists because otherwise, that would allow:

int f();
auto f(); // What's the return type here?

The thing is, you can have an undeduced type type has the return type of a function. There are no deduction rules based on previous declarations, which is why such mixing is disallowed for functions, even though the following would be perfectly fine:

int f();
auto f() { return 1; }

This problem does not exist for variables:

extern int v;
extern auto v; // ill-formed

Any declaration-only variables has to use a non-placeholder type. What this means is that if you use a placeholder type for the definition of v, it can get deduced without any problems and then of course has to match the non-placeholder type used in the first declaration.

extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.
Rakete1111
  • 47,013
  • 16
  • 123
  • 162