The DeclContext of block-scope function declarations.

Consider the following program:

$ cat block.cc
namespace N {

void f() {
   extern int var; // #1
   extern void g(int); // #2
   g(var);
}

class S {
   friend void h(); // #3
};

} // namespace N

Function N::f() declares extern function `g(int)' and int object `var'.
In the AST generated by clang, these two block-scope declarations have function N::f() as their (semantic and lexical) DeclContext.

However, C++11 3.5p7 says:

   "When a block scope declaration of an entity with linkage is not
    found to refer to some other declaration, then that entity is a
    member of the innermost enclosing namespace. However such a
    declaration does not introduce the member name in its
    namespace scope."

This seems to suggest that both `g(int)' and `var' should have namespace N as their semantic DeclContext (but be "hidden" there), while still having f() as their lexical DeclContext. That is, things should be (more or less) similar to the case of friend function h() in class S.

As an example where this DeclContext issue matters, when calling method

    NamedDecl::getQualifiedNameAsString()

on the three declarations we obtain:

    #1 var // wrong, should be N::var
    #2 g // wrong, should be N::g
    #3 N::h // correct

We would appreciate if someone could confirm the issue and diagnosis above, as well as hinting at the places in Sema that need to be modified to obtain the correct behavior.

Cheers,
Enea.

Ping.

Ping^2.

This is a longstanding [*] problem in Clang's representation. The lexical DeclContext should be the function for #1 and #2, while the semantic DeclContext should be the nearest enclosing namespace. Ideally, name lookup for redeclarations (but not uses) would then be able to find these functions, so that in

namespace N {
  void f() {
    extern int var; #1
  }

  extern int var; // #2

  void g() {
    extern int var; // #3
  }
}

#1, #2, and #3 would all end up in the same redeclaration chain. That doesn't happen now.

  - Doug

[*] Block-scope function declarations and extern variables predate the introduction of DeclContexts!

>
>> Consider the following program:
>>
>> $ cat block.cc
>> namespace N {
>>
>> void f() {
>> extern int var; // #1
>> extern void g(int); // #2
>> g(var);
>> }
>>
>> class S {
>> friend void h(); // #3
>> };
>>
>> } // namespace N
>>

[...]

> This is a longstanding [*] problem in Clang's representation. The
lexical DeclContext should be the function for #1 and #2, while the
semantic DeclContext should be the nearest enclosing namespace. Ideally,
name lookup for redeclarations (but not uses) would then be able to find
these functions, so that in
>
> namespace N {
> void f() {
> extern int var; #1
> }
>
> extern int var; // #2
>
> void g() {
> extern int var; // #3
> }
> }
>
> #1, #2, and #3 would all end up in the same redeclaration chain. That doesn't happen now.
>
> - Doug
>
> [*] Block-scope function declarations and extern variables predate the introduction of DeclContexts!

Summarizing, the declaration of the local extern entity should be:

(a) visible when looked up for redeclaration purposes
     in its lexical context;

(b) visible when looked up for redeclaration purposes
     in its enclosing namespace (i.e., semantic context);

(c) visible when looked up for uses in its lexical context;

(d) *not* visible when looked up for uses in its enclosing
     namespace (i.e., semantic context).

Currently, we have (a) and (c), but not (b) nor (d).

By changing the semantic DeclContext to be the nearest enclosing namespace, we will get properties (a) and (b), but we won't obtain properties (c) and (d).

As for (c), maybe
   DeclContext::makeDeclVisibleinContext(NamedDecl*)
is a viable solution?

And then what about (d)?

Should we use another flag in the Decl::IdentifierNamespace enumeration (e.g., IDNS_LocalExtern) mimicking what was done for IDNS_OrdinaryFriend, but with its own specific visibility rules?
Are there simpler approaches?

Enea.

>
>
>> Consider the following program:
>>
>> $ cat block.cc
>> namespace N {
>>
>> void f() {
>> extern int var; // #1
>> extern void g(int); // #2
>> g(var);
>> }
>>
>> class S {
>> friend void h(); // #3
>> };
>>
>> } // namespace N
>>

[...]

> This is a longstanding [*] problem in Clang's representation. The
lexical DeclContext should be the function for #1 and #2, while the
semantic DeclContext should be the nearest enclosing namespace. Ideally,
name lookup for redeclarations (but not uses) would then be able to find
these functions, so that in
>
> namespace N {
> void f() {
> extern int var; #1
> }
>
> extern int var; // #2
>
> void g() {
> extern int var; // #3
> }
> }
>
> #1, #2, and #3 would all end up in the same redeclaration chain. That doesn't happen now.
>
>
>
> - Doug
>
> [*] Block-scope function declarations and extern variables predate the introduction of DeclContexts!

Summarizing, the declaration of the local extern entity should be:

(a) visible when looked up for redeclaration purposes
   in its lexical context;

(b) visible when looked up for redeclaration purposes
   in its enclosing namespace (i.e., semantic context);

(c) visible when looked up for uses in its lexical context;

(d) *not* visible when looked up for uses in its enclosing
   namespace (i.e., semantic context).

Currently, we have (a) and (c), but not (b) nor (d).

By changing the semantic DeclContext to be the nearest enclosing namespace, we will get properties (a) and (b), but we won't obtain properties (c) and (d).

As for (c), maybe
DeclContext::makeDeclVisibleinContext(NamedDecl*)
is a viable solution?

Yes.

And then what about (d)?

Should we use another flag in the Decl::IdentifierNamespace enumeration (e.g., IDNS_LocalExtern) mimicking what was done for IDNS_OrdinaryFriend, but with its own specific visibility rules?
Are there simpler approaches?

I *think* the best approach is to generalize the idea behind IDNS_OrdinaryFriend, because friends have the same visibility properties as local externs. The new IDNS_OrdinaryHidden (or whatever) would be found be redeclaration lookup for ordinary names, but not by normal name lookup for ordinary names.

  - Doug