forked from OSchip/llvm-project
Add VarDecl::isThisDeclarationADefinition(), which properly encapsulates the logic for when a variable declaration is a (possibly tentativ) definition. Add a few functions building on this, and shift C tentative definition handling over to this new functionality. This shift also kills the Sema::TentativeDefinitions map and instead simply stores all declarations in the renamed list. The correct handling for multiple tentative definitions is instead shifted to the final walk of the list.
llvm-svn: 94968
This commit is contained in:
parent
a682427e42
commit
35351a9554
|
@ -538,6 +538,7 @@ public:
|
|||
/// };
|
||||
/// \endcode
|
||||
bool isStaticDataMember() const {
|
||||
// If it wasn't static, it would be a FieldDecl.
|
||||
return getDeclContext()->isRecord();
|
||||
}
|
||||
|
||||
|
@ -546,6 +547,26 @@ public:
|
|||
return const_cast<VarDecl*>(this)->getCanonicalDecl();
|
||||
}
|
||||
|
||||
enum DefinitionKind {
|
||||
DeclarationOnly, ///< This declaration is only a declaration.
|
||||
TentativeDefinition, ///< This declaration is a tentative definition.
|
||||
Definition ///< This declaration is definitely a definition.
|
||||
};
|
||||
|
||||
/// \brief Check whether this declaration is a definition. If this could be
|
||||
/// a tentative definition (in C), don't check whether there's an overriding
|
||||
/// definition.
|
||||
DefinitionKind isThisDeclarationADefinition() const;
|
||||
|
||||
/// \brief Get the tentative definition that acts as the real definition in
|
||||
/// a TU. Returns null if there is a proper definition available.
|
||||
const VarDecl *getActingDefinition() const;
|
||||
VarDecl *getActingDefinition();
|
||||
|
||||
/// \brief Determine whether this is a tentative definition of a
|
||||
/// variable in C.
|
||||
bool isTentativeDefinitionNow() const;
|
||||
|
||||
/// \brief Retrieve the definition of this variable, which may come
|
||||
/// from a previous declaration. Def will be set to the VarDecl that
|
||||
/// contains the initializer, and the result will be that
|
||||
|
@ -579,10 +600,9 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this is a tentative definition of a
|
||||
/// variable in C.
|
||||
bool isTentativeDefinition(ASTContext &Context) const;
|
||||
|
||||
bool hasInit() const {
|
||||
return !Init.isNull();
|
||||
}
|
||||
const Expr *getInit() const {
|
||||
if (Init.isNull())
|
||||
return 0;
|
||||
|
|
|
@ -480,6 +480,82 @@ VarDecl *VarDecl::getCanonicalDecl() {
|
|||
return getFirstDeclaration();
|
||||
}
|
||||
|
||||
VarDecl::DefinitionKind VarDecl::isThisDeclarationADefinition() const {
|
||||
// C++ [basic.def]p2:
|
||||
// A declaration is a definition unless [...] it contains the 'extern'
|
||||
// specifier or a linkage-specification and neither an initializer [...],
|
||||
// it declares a static data member in a class declaration [...].
|
||||
// C++ [temp.expl.spec]p15:
|
||||
// An explicit specialization of a static data member of a template is a
|
||||
// definition if the declaration includes an initializer; otherwise, it is
|
||||
// a declaration.
|
||||
if (isStaticDataMember()) {
|
||||
if (isOutOfLine() && (hasInit() ||
|
||||
getTemplateSpecializationKind() != TSK_ExplicitSpecialization))
|
||||
return Definition;
|
||||
else
|
||||
return DeclarationOnly;
|
||||
}
|
||||
// C99 6.7p5:
|
||||
// A definition of an identifier is a declaration for that identifier that
|
||||
// [...] causes storage to be reserved for that object.
|
||||
// Note: that applies for all non-file-scope objects.
|
||||
// C99 6.9.2p1:
|
||||
// If the declaration of an identifier for an object has file scope and an
|
||||
// initializer, the declaration is an external definition for the identifier
|
||||
if (hasInit())
|
||||
return Definition;
|
||||
// AST for 'extern "C" int foo;' is annotated with 'extern'.
|
||||
if (hasExternalStorage())
|
||||
return DeclarationOnly;
|
||||
|
||||
// C99 6.9.2p2:
|
||||
// A declaration of an object that has file scope without an initializer,
|
||||
// and without a storage class specifier or the scs 'static', constitutes
|
||||
// a tentative definition.
|
||||
// No such thing in C++.
|
||||
if (!getASTContext().getLangOptions().CPlusPlus && isFileVarDecl())
|
||||
return TentativeDefinition;
|
||||
|
||||
// What's left is (in C, block-scope) declarations without initializers or
|
||||
// external storage. These are definitions.
|
||||
return Definition;
|
||||
}
|
||||
|
||||
const VarDecl *VarDecl::getActingDefinition() const {
|
||||
return const_cast<VarDecl*>(this)->getActingDefinition();
|
||||
}
|
||||
|
||||
VarDecl *VarDecl::getActingDefinition() {
|
||||
DefinitionKind Kind = isThisDeclarationADefinition();
|
||||
if (Kind != TentativeDefinition)
|
||||
return 0;
|
||||
|
||||
VarDecl *LastTentative = false;
|
||||
VarDecl *First = getFirstDeclaration();
|
||||
for (redecl_iterator I = First->redecls_begin(), E = First->redecls_end();
|
||||
I != E; ++I) {
|
||||
Kind = (*I)->isThisDeclarationADefinition();
|
||||
if (Kind == Definition)
|
||||
return 0;
|
||||
else if (Kind == TentativeDefinition)
|
||||
LastTentative = *I;
|
||||
}
|
||||
return LastTentative;
|
||||
}
|
||||
|
||||
bool VarDecl::isTentativeDefinitionNow() const {
|
||||
DefinitionKind Kind = isThisDeclarationADefinition();
|
||||
if (Kind != TentativeDefinition)
|
||||
return false;
|
||||
|
||||
for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) {
|
||||
if ((*I)->isThisDeclarationADefinition() == Definition)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const Expr *VarDecl::getDefinition(const VarDecl *&Def) const {
|
||||
redecl_iterator I = redecls_begin(), E = redecls_end();
|
||||
while (I != E && !I->getInit())
|
||||
|
@ -521,15 +597,6 @@ VarDecl *VarDecl::getOutOfLineDefinition() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
|
||||
if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
|
||||
return false;
|
||||
|
||||
const VarDecl *Def = 0;
|
||||
return (!getDefinition(Def) &&
|
||||
(getStorageClass() == None || getStorageClass() == Static));
|
||||
}
|
||||
|
||||
void VarDecl::setInit(ASTContext &C, Expr *I) {
|
||||
if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>()) {
|
||||
Eval->~EvaluatedStmt();
|
||||
|
@ -547,8 +614,7 @@ VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const {
|
|||
}
|
||||
|
||||
TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() const {
|
||||
if (MemberSpecializationInfo *MSI
|
||||
= getASTContext().getInstantiatedFromStaticDataMember(this))
|
||||
if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo())
|
||||
return MSI->getTemplateSpecializationKind();
|
||||
|
||||
return TSK_Undeclared;
|
||||
|
|
|
@ -2464,11 +2464,10 @@ void PCHReader::InitializeSema(Sema &S) {
|
|||
PreloadedDecls.clear();
|
||||
|
||||
// If there were any tentative definitions, deserialize them and add
|
||||
// them to Sema's table of tentative definitions.
|
||||
// them to Sema's list of tentative definitions.
|
||||
for (unsigned I = 0, N = TentativeDefinitions.size(); I != N; ++I) {
|
||||
VarDecl *Var = cast<VarDecl>(GetDecl(TentativeDefinitions[I]));
|
||||
SemaObj->TentativeDefinitions[Var->getDeclName()] = Var;
|
||||
SemaObj->TentativeDefinitionList.push_back(Var->getDeclName());
|
||||
SemaObj->TentativeDefinitions.push_back(Var);
|
||||
}
|
||||
|
||||
// If there were any locally-scoped external declarations,
|
||||
|
|
|
@ -1960,13 +1960,11 @@ void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
|||
}
|
||||
|
||||
// Build a record containing all of the tentative definitions in this file, in
|
||||
// TentativeDefinitionList order. Generally, this record will be empty for
|
||||
// TentativeDefinitions order. Generally, this record will be empty for
|
||||
// headers.
|
||||
RecordData TentativeDefinitions;
|
||||
for (unsigned i = 0, e = SemaRef.TentativeDefinitionList.size(); i != e; ++i){
|
||||
VarDecl *VD =
|
||||
SemaRef.TentativeDefinitions.lookup(SemaRef.TentativeDefinitionList[i]);
|
||||
if (VD) AddDeclRef(VD, TentativeDefinitions);
|
||||
for (unsigned i = 0, e = SemaRef.TentativeDefinitions.size(); i != e; ++i) {
|
||||
AddDeclRef(SemaRef.TentativeDefinitions[i], TentativeDefinitions);
|
||||
}
|
||||
|
||||
// Build a record containing all of the locally-scoped external
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Sema.h"
|
||||
#include "TargetAttributesSema.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
@ -472,12 +473,14 @@ void Sema::ActOnEndOfTranslationUnit() {
|
|||
// translation unit contains a file scope declaration of that
|
||||
// identifier, with the composite type as of the end of the
|
||||
// translation unit, with an initializer equal to 0.
|
||||
for (unsigned i = 0, e = TentativeDefinitionList.size(); i != e; ++i) {
|
||||
VarDecl *VD = TentativeDefinitions.lookup(TentativeDefinitionList[i]);
|
||||
llvm::SmallSet<VarDecl *, 32> Seen;
|
||||
for (unsigned i = 0, e = TentativeDefinitions.size(); i != e; ++i) {
|
||||
VarDecl *VD = TentativeDefinitions[i]->getActingDefinition();
|
||||
|
||||
// If the tentative definition was completed, it will be in the list, but
|
||||
// not the map.
|
||||
if (VD == 0 || VD->isInvalidDecl() || !VD->isTentativeDefinition(Context))
|
||||
// If the tentative definition was completed, getActingDefinition() returns
|
||||
// null. If we've already seen this variable before, insert()'s second
|
||||
// return value is false.
|
||||
if (VD == 0 || VD->isInvalidDecl() || !Seen.insert(VD))
|
||||
continue;
|
||||
|
||||
if (const IncompleteArrayType *ArrayT
|
||||
|
|
|
@ -272,14 +272,8 @@ public:
|
|||
/// not visible.
|
||||
llvm::DenseMap<DeclarationName, NamedDecl *> LocallyScopedExternalDecls;
|
||||
|
||||
/// \brief The set of tentative declarations seen so far in this
|
||||
/// translation unit for which no definition has been seen.
|
||||
///
|
||||
/// The tentative declarations are indexed by the name of the
|
||||
/// declaration, and only the most recent tentative declaration for
|
||||
/// a given variable will be recorded here.
|
||||
llvm::DenseMap<DeclarationName, VarDecl *> TentativeDefinitions;
|
||||
std::vector<DeclarationName> TentativeDefinitionList;
|
||||
/// \brief All the tentative definitions encountered in the TU.
|
||||
std::vector<VarDecl *> TentativeDefinitions;
|
||||
|
||||
struct DelayedDiagnostic {
|
||||
enum DDKind { Deprecation, Access };
|
||||
|
|
|
@ -3570,14 +3570,6 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) {
|
|||
// Attach the initializer to the decl.
|
||||
VDecl->setInit(Context, Init);
|
||||
|
||||
// If the previous declaration of VDecl was a tentative definition,
|
||||
// remove it from the set of tentative definitions.
|
||||
if (VDecl->getPreviousDeclaration() &&
|
||||
VDecl->getPreviousDeclaration()->isTentativeDefinition(Context)) {
|
||||
bool Deleted = TentativeDefinitions.erase(VDecl->getDeclName());
|
||||
assert(Deleted && "Unrecorded tentative definition?"); Deleted=Deleted;
|
||||
}
|
||||
|
||||
if (getLangOptions().CPlusPlus) {
|
||||
// Make sure we mark the destructor as used if necessary.
|
||||
QualType InitType = VDecl->getType();
|
||||
|
@ -3602,20 +3594,8 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
|
|||
QualType Type = Var->getType();
|
||||
|
||||
// Record tentative definitions.
|
||||
if (Var->isTentativeDefinition(Context)) {
|
||||
std::pair<llvm::DenseMap<DeclarationName, VarDecl *>::iterator, bool>
|
||||
InsertPair =
|
||||
TentativeDefinitions.insert(std::make_pair(Var->getDeclName(), Var));
|
||||
|
||||
// Keep the latest definition in the map. If we see 'int i; int i;' we
|
||||
// want the second one in the map.
|
||||
InsertPair.first->second = Var;
|
||||
|
||||
// However, for the list, we don't care about the order, just make sure
|
||||
// that there are no dupes for a given declaration name.
|
||||
if (InsertPair.second)
|
||||
TentativeDefinitionList.push_back(Var->getDeclName());
|
||||
}
|
||||
if (Var->isTentativeDefinitionNow())
|
||||
TentativeDefinitions.push_back(Var);
|
||||
|
||||
// C++ [dcl.init.ref]p3:
|
||||
// The initializer can be omitted for a reference only in a
|
||||
|
@ -3794,7 +3774,8 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
|
|||
// storage-class specifier or with the storage-class specifier "static",
|
||||
// constitutes a tentative definition. Note: A tentative definition with
|
||||
// external linkage is valid (C99 6.2.2p5).
|
||||
if (IDecl->isTentativeDefinition(Context) && !IDecl->isInvalidDecl()) {
|
||||
if (IDecl->isThisDeclarationADefinition() == VarDecl::TentativeDefinition &&
|
||||
!IDecl->isInvalidDecl()) {
|
||||
if (const IncompleteArrayType *ArrayT
|
||||
= Context.getAsIncompleteArrayType(T)) {
|
||||
if (RequireCompleteType(IDecl->getLocation(),
|
||||
|
|
Loading…
Reference in New Issue