From 07665a69e88334ced1648b5e4bcc2dbe2b90e366 Mon Sep 17 00:00:00 2001
From: Douglas Gregor
DeclarationName
instances for the four kinds of
C++ special function names.
+
+Every declaration in a program exists within some declaration
+ context, such as a translation unit, namespace, class, or
+ function. Declaration contexts in Clang are represented by
+ the DeclContext
class, from which the various
+ declaration-context AST nodes
+ (TranslationUnitDecl
, NamespaceDecl
, RecordDecl
, FunctionDecl
,
+ etc.) will derive. The DeclContext
class provides
+ several facilities common to each declaration context:
DeclContext
provides two views of the declarations
+ stored within a declaration context. The source-centric view
+ accurately represents the program source code as written, including
+ multiple declarations of entities where present (see the
+ section Redeclarations and
+ Overloads), while the semantics-centric view represents the
+ program semantics. The two views are kept synchronized by semantic
+ analysis while the ASTs are being constructed.RecordDecl
) contains various member functions,
+ fields, nested types, and so on. All of these declarations will be
+ stored within the DeclContext
, and one can iterate
+ over the declarations via
+ [DeclContext::decls_begin()
,
+ DeclContext::decls_end()
). This mechanism provides
+ the source-centric view of declarations in the context.DeclContext
structure provides efficient name
+ lookup for names within that declaration context. For example,
+ if N
is a namespace we can look for the
+ name N::f
+ using DeclContext::lookup
. The lookup itself is
+ based on a lazily-constructed array (for declaration contexts
+ with a small number of declarations) or hash table (for
+ declaration contexts with more declarations). The lookup
+ operation provides the semantics-centric view of the declarations
+ in the context.DeclContext
owns all of the declarations that
+ were declared within its declaration context, and is responsible
+ for the management of their memory as well as their
+ (de-)serialization.The declarations stored within each declaration context are
+ called scoped declarations and the AST nodes for each of
+ these declarations are
+ derived from the ScopedDecl
class, which provides
+ information about the context in which that declaration lives. One
+ can retrieve the DeclContext
that contains a
+ particular ScopedDecl
+ using ScopedDecl::getDeclContext
. However, see the
+ section Lexical and Semantic
+ Contexts for more information about how to interpret this
+ context information.
Within a translation unit, it is common for an entity to be +declared several times. For example, we might declare a function "f" + and then later re-declare it as part of an inlined definition:
+ ++void f(int x, int y, int z = 1); + +inline void f(int x, int y, int z) { /* ... */ } ++ +
The representation of "f" differs in the source-centric and + semantics-centric views of a declaration context. In the + source-centric view, all redeclarations will be present, in the + order they occurred in the source code, making + this view suitable for clients that wish to see the structure of + the source code. In the semantics-centric view, only the most recent "f" + will be found by the lookup, since it effectively replaces the first + declaration of "f".
+ +In the semantics-centric view, overloading of functions is + represented explicitly. For example, given two declarations of a + function "g" that are overloaded, e.g.,
++void g(); +void g(int); ++
the DeclContext::lookup
operation will return
+ an OverloadedFunctionDecl
that contains both
+ declarations of "g". Clients that perform semantic analysis on a
+ program that is not concerned with the actual source code will
+ primarily use this semantics-centric view.
Each scoped declaration (whose AST node derived
+ from ScopedDecl
) has two potentially different
+ declaration contexts: a lexical context, which corresponds to
+ the source-centric view of the declaration context, and
+ a semantic context, which corresponds to the
+ semantics-centric view. The lexical context is accessible
+ via ScopedDecl::getLexicalDeclContext
while the
+ semantic context is accessible
+ via ScopedDecl::getDeclContext
, both of which return
+ DeclContext
pointers. For most declarations, the two
+ contexts are identical. For example:
+class X { +public: + void f(int x); +}; ++ +
Here, the semantic and lexical contexts of X::f
are
+ the DeclContext
associated with the
+ class X
(itself stored as a RecordDecl
AST
+ node). However, we can now define X::f
out-of-line:
+void X::f(int x = 17) { /* ... */ } ++ +
This definition of has different lexical and semantic
+ contexts. The lexical context corresponds to the declaration
+ context in which the actual declaration occurred in the source
+ code, e.g., the translation unit containing X
. Thus,
+ this declaration of X::f
can be found by traversing
+ the declarations provided by
+ [decls_begin()
, decls_end()
) in the
+ translation unit.
The semantic context of X::f
corresponds to the
+ class X
, since this member function is (semantically) a
+ member of X
. Lookup of the name f
into
+ the DeclContext
associated with X
will
+ then return the definition of X::f
(including
+ information about the default argument).
In C and C++, there are several contexts in which names that are + logically declared inside another declaration will actually "leak" + out into the enclosing scope from the perspective of name + lookup. The most obvious instance of this behavior is in + enumeration types, e.g.,
++enum Color { + Red, + Green, + Blue +}; ++ +
Here, Color
is an enumeration, which is a declaration
+ context that contains the
+ enumerators Red
, Green
,
+ and Blue
. Thus, traversing the list of declarations
+ contained in the enumeration Color
will
+ yield Red
, Green
,
+ and Blue
. However, outside of the scope
+ of Color
one can name the enumerator Red
+ without qualifying the name, e.g.,
+Color c = Red; ++ +
There are other entities in C++ that provide similar behavior. For + example, linkage specifications that use curly braces:
+ ++extern "C" { + void f(int); + void g(int); +} +// f and g are visible here ++ +
For source-level accuracy, we treat the linkage specification and + enumeration type as a + declaration context in which its enclosed declarations ("Red", + "Green", and "Blue"; "f" and "g") + are declared. However, these declarations are visible outside of the + scope of the declaration context.
+ +These language features (and several others, described below) have
+ roughly the same set of
+ requirements: declarations are declared within a particular lexical
+ context, but the declarations are also found via name lookup in
+ scopes enclosing the declaration itself. This feature is implemented
+ via transparent declaration contexts
+ (see DeclContext::isTransparentContext()
), whose
+ declarations are visible in the nearest enclosing non-transparent
+ declaration context. This means that the lexical context of the
+ declaration (e.g., an enumerator) will be the
+ transparent DeclContext
itself, as will the semantic
+ context, but the declaration will be visible in every outer context
+ up to and including the first non-transparent declaration context (since
+ transparent declaration contexts can be nested).
The transparent DeclContexts
are:
+enum Color { + Red, + Green, + Blue +}; +// Red, Green, and Blue are in scope +
+extern "C" { + void f(int); + void g(int); +} +// f and g are in scope +
+struct LookupTable { + bool IsVector; + union { + std::vector<Item> *Vector; + std::set<Item> *Set; + }; +}; + +LookupTable LT; +LT.Vector = 0; // Okay: finds Vector inside the unnamed union ++
+namespace mylib { + inline namespace debug { + class X; + } +} +mylib::X *xp; // okay: mylib::X refers to mylib::debug::X ++
C++ namespaces have the interesting--and, so far, unique--property that +the namespace can be defined multiple times, and the declarations +provided by each namespace definition are effectively merged (from +the semantic point of view). For example, the following two code +snippets are semantically indistinguishable:
++// Snippet #1: +namespace N { + void f(); +} +namespace N { + void f(int); +} + +// Snippet #2: +namespace N { + void f(); + void f(int); +} ++ +
In Clang's representation, the source-centric view of declaration
+ contexts will actually have two separate NamespaceDecl
+ nodes in Snippet #1, each of which is a declaration context that
+ contains a single declaration of "f". However, the semantics-centric
+ view provided by name lookup into the namespace N
for
+ "f" will return an OverloadedFunctionDecl
that contains
+ both declarations of "f".
DeclContext
manages multiply-defined declaration
+ contexts internally. The
+ function DeclContext::getPrimaryContext
retrieves the
+ "primary" context for a given DeclContext
instance,
+ which is the DeclContext
responsible for maintaining
+ the lookup table used for the semantics-centric view. Given the
+ primary context, one can follow the chain
+ of DeclContext
nodes that define additional
+ declarations via DeclContext::getNextContext
. Note that
+ these functions are used internally within the lookup and insertion
+ methods of the DeclContext
, so the vast majority of
+ clients can ignore them.