Hi,
Attached patch reimplements the "is a variable declaration a definition"
code. This currently adds some stuff and replaces isTentativeDefinition.
Follow-ups will then move more code over and eventually fix the C++
redefinition issues. But I'd like this part of the code reviewed before
I commit it. All test cases pass.
Sebastian
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h (revision 94752)
+++ include/clang/AST/Decl.h (working copy)
@@ -537,6 +537,7 @@
/// };
/// \endcode
bool isStaticDataMember() const {
+ // If it wasn't static, it would be a FieldDecl.
return getDeclContext()->isRecord();
}
@@ -545,6 +546,26 @@
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 Determine whether this declaration acts as a definition. This is
+ /// the case if it either is a definition, or the last tentative definition
+ /// in the TU if there is no real definition.
+ bool actsAsDefinition() const;
+
+ /// \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
@@ -578,10 +599,9 @@
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;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp (revision 94752)
+++ lib/Sema/SemaDecl.cpp (working copy)
@@ -3570,14 +3570,6 @@
// 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,7 +3594,7 @@
QualType Type = Var->getType();
// Record tentative definitions.
- if (Var->isTentativeDefinition(Context)) {
+ if (Var->isTentativeDefinitionNow()) {
std::pair<llvm::DenseMap<DeclarationName, VarDecl *>::iterator, bool>
InsertPair =
TentativeDefinitions.insert(std::make_pair(Var->getDeclName(), Var));
@@ -3794,7 +3786,8 @@
// 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(),
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp (revision 94752)
+++ lib/Sema/Sema.cpp (working copy)
@@ -477,7 +477,7 @@
// 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 (VD == 0 || VD->isInvalidDecl() || !VD->actsAsDefinition())
continue;
if (const IncompleteArrayType *ArrayT
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp (revision 94752)
+++ lib/AST/Decl.cpp (working copy)
@@ -480,6 +480,81 @@
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;
+}
+
+bool VarDecl::actsAsDefinition() const {
+ DefinitionKind Kind = isThisDeclarationADefinition();
+ switch (Kind) {
+ case DeclarationOnly: return false;
+ case Definition: return true;
+ case TentativeDefinition: break;
+ }
+
+ bool LastIsThis = false;
+ const VarDecl *First = getFirstDeclaration();
+ for (redecl_iterator I = First->redecls_begin(), E = First->redecls_end();
+ I != E; ++I) {
+ Kind = (*I)->isThisDeclarationADefinition();
+ if (Kind == Definition)
+ return false;
+ else if (Kind == TentativeDefinition)
+ LastIsThis = (*I == this);
+ }
+ return LastIsThis;
+}
+
+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 +596,6 @@
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 +613,7 @@
}
TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() const {
- if (MemberSpecializationInfo *MSI
- = getASTContext().getInstantiatedFromStaticDataMember(this))
+ if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo())
return MSI->getTemplateSpecializationKind();
return TSK_Undeclared;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits