13

clang, gcc and VS2013 all complain about redefinition of w in main(), but I couldn't find in the Standard anything disallowing this.

namespace N {
    extern int j;
    int j;
}

int main()
{
    extern int w;
    int w;
}

These paragraphs say something about the use of an extern declaration in block scope, but they don't seem to justify the error message:

§3.3.1/4

Given a set of declarations in a single declarative region, ...

[ Note: These restrictions apply to the declarative region into which a name is introduced, which is not necessarily the same as the region in which the declaration occurs. In particular, elaborated-type-specifiers (7.1.6.3) and friend declarations (11.3) may introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to that region. Local extern declarations (3.5) may introduce a name into the declarative region where the declaration appears and also introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to both regions. —end note ]

§3.3.2/10

[ Note: Friend declarations refer to functions or classes that are members of the nearest enclosing namespace, but they do not introduce new names into that namespace (7.3.1.2). Function declarations at block scope and variable declarations with the extern specifier at block scope refer to declarations that are members of an enclosing namespace, but they do not introduce new names into that scope. —end note ]

Wake up Brazil
  • 3,421
  • 12
  • 19
  • I'd guess they introduce a new name into the *local scope*. I.e., the `extern int w;` refers to some global variable, and `int w;` defines a new object in the local scope. So there's a conflict between the name `w` referring to a global scope variable and the name `w` referring to a local variable. – dyp Apr 29 '14 at 14:55
  • Doesn't §3.3.2/10 say something opposed to what you've just written? – Wake up Brazil Apr 29 '14 at 15:02
  • I interpret 3.3.2/10 either as "they don't introduce new names into the enclosing namespace" or as "they reintroduce a name into the local scope, but don't introduce a *new* name". – dyp Apr 29 '14 at 15:06
  • I think §3.5/6 is also relevant "The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. [...]" – dyp Apr 29 '14 at 15:12
  • 3.5/7 says "However such a declaration does not introduce the member name in its namespace scope." So I think 3.3.2/10 indeed refers to the enclosing namespace. – dyp Apr 29 '14 at 15:13
  • 1
    I'm liking 3.3.1/4 for this: "Given a set of declarations in a single declarative region, each of which specifies the same unqualified name, they shall all refer to the same entity, or all refer to functions and function templates; or [other stuff that doesn't apply here]" – Casey Apr 29 '14 at 15:33
  • @Casey 3.3.1/4 == [basic.scope.declarative]/4, so we seem to agree here :) – dyp Apr 29 '14 at 15:38

2 Answers2

8

I believe this is mostly covered by §3.5/6.

In particular:

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage.

So, the extern int w; declares a w that has linkage (external linkage, in this case, since no matching entity is visible at that point).

Then you attempt to define a local w which has no linkage (by §3.5/8).

That gives two declarations of the same name at the same scope, but with different linkages. That's prohibited by §3.3.1/4:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,

  • they shall all refer to the same entity, or all refer to functions and function templates; or
  • exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden (3.3.10).

Neither refers to a function, function template, class name, or enumeration name, so none of these "escape clauses" applies. The two declarations must refer to the same entity, which must have both external linkage and no linkage. Since that's impossible, the code is ill-formed.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • After re-reading, I think @dyp was right, and 3.3.1/4 is really the part that applies. I've edited accordingly. – Jerry Coffin Apr 29 '14 at 15:45
  • @JerryCoffin Sorry for the delay. I was out of my desk for a couple of hours. I'm trying to understand the example in §3.5/6 before I analyse your answer. Why does the object in line #3 have static storage duration and external linkage? I can't relate this to the paragraph above. Thanks. – Wake up Brazil Apr 29 '14 at 19:32
  • 2
    There is a defect report about §3.5/6 ( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#426 ) – Wake up Brazil Apr 30 '14 at 15:11
  • Sorry for the delay in accepting your answer, but because of another question of mine in SO, I was finally convinced that you were right (+1). Thanks. – Wake up Brazil Sep 29 '14 at 17:56
0

Here is my interpretation: In §3.3.1/3 the standard says:

The names declared by a declaration are introduced into the scope in which the declaration occurs, except that the presence of a friend specifier (11.3), certain uses of the elaborated-type-specifier (7.1.6.3), and using-directives (7.3.4) alter this general behavior.

As extern declarations are not listed as exception, the name is introduced in the block scope, which is why you get the error when you try to redeclare it.

The paragraph you quoted says

but they do not introduce new names into that scope.

which is a bit ambiguous, as both block scope and namespace scope are mentioned. The standard would contradict itself if it referred to block scope, so I assume that namespace scope is meant.

jblume
  • 390
  • 1
  • 6
  • The exception is in [basic.link]/6-7 – dyp Apr 29 '14 at 15:17
  • Redeclaring a name is not necessarily an error. `extern int w; extern int w; extern int w;` declares `w` three times, but it's acceptable since all declarations refer to the same entity. – Casey Apr 29 '14 at 15:56