diff options
author | Sebastian Redl <sebastian.redl@getdesigned.at> | 2010-01-31 22:27:38 +0000 |
---|---|---|
committer | Sebastian Redl <sebastian.redl@getdesigned.at> | 2010-01-31 22:27:38 +0000 |
commit | e9d12b6c50c1e9b05443db099e21026c5991a93b (patch) | |
tree | a750984a3b4a4624cc6dd6a27fcaf030ea010db0 | |
parent | 6997aae42800d95a1189a6186af438feb19ecc54 (diff) |
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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94968 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Decl.h | 28 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 88 | ||||
-rw-r--r-- | lib/Frontend/PCHReader.cpp | 5 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 15 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 10 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 27 |
7 files changed, 121 insertions, 60 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d7d7937773..17ee3e54da 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -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; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 46f7117f2c..d75d355fdc 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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; diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 259355145a..bd93cf6b08 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -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, diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index 9909c95847..caf1ce47a1 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -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 diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 171101bb96..c0e7572cdd 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -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]); - - // If the tentative definition was completed, it will be in the list, but - // not the map. - if (VD == 0 || VD->isInvalidDecl() || !VD->isTentativeDefinition(Context)) + 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, 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 diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 7f4806d305..54d36357be 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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 }; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 398c156f9b..a493a29a5d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(), |