From b6c8c8bd8d362c8a6cdb767415b0d21e62b77eb2 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 21 Apr 2009 17:11:58 +0000 Subject: [PATCH] Explictly track tentative definitions within Sema, then hand those tentative definitions off to the ASTConsumer at the end of the translation unit. Eliminate CodeGen's internal tracking of tentative definitions, and instead hook into ASTConsumer::CompleteTentativeDefinition. Also, tweak the definition-deferal logic for C++, where there are no tentative definitions. Fixes , and will make it much easier for precompiled headers to cope with tentative definitions in the future. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69681 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTConsumer.h | 12 ++++++ lib/AST/Decl.cpp | 3 +- lib/CodeGen/CodeGenModule.cpp | 27 ++++++-------- lib/CodeGen/CodeGenModule.h | 11 +----- lib/CodeGen/ModuleBuilder.cpp | 7 ++++ lib/Sema/Sema.cpp | 65 ++++++++++++++++----------------- lib/Sema/Sema.h | 8 ++++ lib/Sema/SemaDecl.cpp | 17 +++++++++ test/PCH/external-defs.h | 3 +- test/Sema/init.c | 3 +- test/Sema/tentative-decls.c | 6 +++ tools/clang-cc/Backend.cpp | 4 ++ 12 files changed, 103 insertions(+), 63 deletions(-) diff --git a/include/clang/AST/ASTConsumer.h b/include/clang/AST/ASTConsumer.h index fd79bf59ea..6dc7e13d8f 100644 --- a/include/clang/AST/ASTConsumer.h +++ b/include/clang/AST/ASTConsumer.h @@ -20,6 +20,7 @@ namespace clang { class TagDecl; class HandleTagDeclDefinition; class SemaConsumer; // layering violation required for safe SemaConsumer + class VarDecl; /// ASTConsumer - This is an abstract interface that should be implemented by /// clients that read ASTs. This abstraction layer allows the client to be @@ -56,6 +57,17 @@ public: /// can be defined in declspecs). virtual void HandleTagDeclDefinition(TagDecl *D) {} + /// \brief Callback invoked at the end of a translation unit to + /// notify the consumer that the given tentative definition should + /// be completed. + /// + /// The variable declaration itself will be a tentative + /// definition. If it had an incomplete array type, its type will + /// have already been changed to an array of size 1. However, the + /// declaration remains a tentative definition and has not been + /// modified by the introduction of an implicit zero initializer. + virtual void CompleteTentativeDefinition(VarDecl *D) {} + /// PrintStats - If desired, print any statistics. virtual void PrintStats() { } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8bda32398f..0326b34960 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -291,7 +291,8 @@ bool VarDecl::isTentativeDefinition(ASTContext &Context) const { if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus) return false; - return (!getInit() && + const VarDecl *Def = 0; + return (!getDefinition(Def) && (getStorageClass() == None || getStorageClass() == Static)); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 0526e7845c..9ae93599df 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -424,13 +424,6 @@ void CodeGenModule::EmitDeferred() { // Otherwise, emit the definition and move on to the next one. EmitGlobalDefinition(D); } - - // Emit any tentative definitions, in reverse order so the most - // important (merged) decl will be seen and emitted first. - for (std::vector::reverse_iterator - it = TentativeDefinitions.rbegin(), ie = TentativeDefinitions.rend(); - it != ie; ++it) - EmitTentativeDefinition(*it); } /// EmitAnnotateAttr - Generate the llvm::ConstantStruct which contains the @@ -502,6 +495,7 @@ bool CodeGenModule::MayDeferGeneration(const ValueDecl *Global) { const VarDecl *VD = cast(Global); assert(VD->isFileVarDecl() && "Invalid decl"); + return VD->getStorageClass() == VarDecl::Static; } @@ -520,16 +514,14 @@ void CodeGenModule::EmitGlobal(const ValueDecl *Global) { const VarDecl *VD = cast(Global); assert(VD->isFileVarDecl() && "Cannot emit local var decl as global."); - // If this isn't a definition, defer code generation. - if (!VD->getInit()) { - // If this is a tentative definition, remember it so that we can - // emit the common definition if needed. It is important to - // defer tentative definitions, since they may have incomplete - // type. - if (!VD->hasExternalStorage()) - TentativeDefinitions.push_back(VD); + // In C++, if this is marked "extern", defer code generation. + if (getLangOptions().CPlusPlus && + VD->getStorageClass() == VarDecl::Extern && !VD->getInit()) + return; + + // In C, if this isn't a definition, defer code generation. + if (!getLangOptions().CPlusPlus && !VD->getInit()) return; - } } // Defer code generation when possible if this is a static definition, inline @@ -727,6 +719,9 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { // See if we have already defined this (as a variable), if so we do // not need to do anything. llvm::GlobalValue *GV = GlobalDeclMap[getMangledName(D)]; + if (!GV && MayDeferGeneration(D)) // this variable was never referenced + return; + if (llvm::GlobalVariable *Var = dyn_cast_or_null(GV)) if (Var->hasInitializer()) return; diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index e7924fade5..21ef8da863 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -119,14 +119,6 @@ class CodeGenModule : public BlockModule { /// is done. std::vector DeferredDeclsToEmit; - /// TentativeDefinitions - A list of declarations which are - /// tentative definitions. Code generation for these must be - /// deferred because they are allowed to have incomplete type when - /// they are seen. This also allows us to avoid generating an extra - /// common definiton in situations where the tentative definition is - /// followed by an actual definition. - std::vector TentativeDefinitions; - /// LLVMUsed - List of global values which are required to be /// present in the object file; bitcast to i8*. This is used for /// forcing visibility of symbols which may otherwise be optimized @@ -339,6 +331,8 @@ public: CXXCtorType Type); const char *getMangledCXXDtorName(const CXXDestructorDecl *D, CXXDtorType Type); + + void EmitTentativeDefinition(const VarDecl *D); enum GVALinkage { GVA_Internal, @@ -382,7 +376,6 @@ private: void EmitGlobalDefinition(const ValueDecl *D); void EmitGlobalFunctionDefinition(const FunctionDecl *D); - void EmitTentativeDefinition(const VarDecl *D); void EmitGlobalVarDefinition(const VarDecl *D); void EmitAliasDefinition(const ValueDecl *D); void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D); diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index 896464ed5a..9b85df61da 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -83,6 +83,13 @@ namespace { if (Builder) Builder->Release(); }; + + virtual void CompleteTentativeDefinition(VarDecl *D) { + if (Diags.hasErrorOccurred()) + return; + + Builder->EmitTentativeDefinition(D); + } }; } diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index ed5c431da8..19155b6cc3 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -232,41 +233,39 @@ 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. - if (!getLangOptions().CPlusPlus) { - // Note: we traverse the scope's list of declarations rather than - // the DeclContext's list, because we only want to see the most - // recent declaration of each identifier. - for (Scope::decl_iterator I = TUScope->decl_begin(), - IEnd = TUScope->decl_end(); - I != IEnd; ++I) { - Decl *D = (*I).getAs(); - if (D->isInvalidDecl()) - continue; + for (llvm::DenseMap::iterator + D = TentativeDefinitions.begin(), + DEnd = TentativeDefinitions.end(); + D != DEnd; ++D) { + VarDecl *VD = D->second; - if (VarDecl *VD = dyn_cast(D)) { - if (VD->isTentativeDefinition(Context)) { - if (const IncompleteArrayType *ArrayT - = Context.getAsIncompleteArrayType(VD->getType())) { - if (RequireCompleteType(VD->getLocation(), - ArrayT->getElementType(), - diag::err_tentative_def_incomplete_type_arr)) - VD->setInvalidDecl(); - else { - // Set the length of the array to 1 (C99 6.9.2p5). - Diag(VD->getLocation(), diag::warn_tentative_incomplete_array); - llvm::APInt One(Context.getTypeSize(Context.getSizeType()), - true); - QualType T - = Context.getConstantArrayType(ArrayT->getElementType(), - One, ArrayType::Normal, 0); - VD->setType(T); - } - } else if (RequireCompleteType(VD->getLocation(), VD->getType(), - diag::err_tentative_def_incomplete_type)) - VD->setInvalidDecl(); - } + if (VD->isInvalidDecl() || !VD->isTentativeDefinition(Context)) + continue; + + if (const IncompleteArrayType *ArrayT + = Context.getAsIncompleteArrayType(VD->getType())) { + if (RequireCompleteType(VD->getLocation(), + ArrayT->getElementType(), + diag::err_tentative_def_incomplete_type_arr)) + VD->setInvalidDecl(); + else { + // Set the length of the array to 1 (C99 6.9.2p5). + Diag(VD->getLocation(), diag::warn_tentative_incomplete_array); + llvm::APInt One(Context.getTypeSize(Context.getSizeType()), + true); + QualType T + = Context.getConstantArrayType(ArrayT->getElementType(), + One, ArrayType::Normal, 0); + VD->setType(T); } - } + } else if (RequireCompleteType(VD->getLocation(), VD->getType(), + diag::err_tentative_def_incomplete_type)) + VD->setInvalidDecl(); + + // Notify the consumer that we've completed a tentative definition. + if (!VD->isInvalidDecl()) + Consumer.CompleteTentativeDefinition(VD); + } } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 1f6cd0440c..f3c337c148 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -233,6 +233,14 @@ public: /// not visible. llvm::DenseMap 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 TentativeDefinitions; + IdentifierResolver IdResolver; // Enum values used by KnownFunctionIDs (see below). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 716219c034..abde26ae53 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2545,6 +2545,18 @@ void Sema::AddInitializerToDecl(DeclPtrTy dcl, ExprArg init, bool DirectInit) { // Attach the initializer to the decl. VDecl->setInit(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)) { + llvm::DenseMap::iterator Pos + = TentativeDefinitions.find(VDecl->getDeclName()); + assert(Pos != TentativeDefinitions.end() && + "Unrecorded tentative definition?"); + TentativeDefinitions.erase(Pos); + } + return; } @@ -2557,6 +2569,11 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl) { if (VarDecl *Var = dyn_cast(RealDecl)) { QualType Type = Var->getType(); + + // Record tentative definitions. + if (Var->isTentativeDefinition(Context)) + TentativeDefinitions[Var->getDeclName()] = Var; + // C++ [dcl.init.ref]p3: // The initializer can be omitted for a reference only in a // parameter declaration (8.3.5), in the declaration of a diff --git a/test/PCH/external-defs.h b/test/PCH/external-defs.h index 4d233e2a40..bb51a9d64b 100644 --- a/test/PCH/external-defs.h +++ b/test/PCH/external-defs.h @@ -14,5 +14,4 @@ static int z; int incomplete_array[]; int incomplete_array2[]; -// FIXME: CodeGen problems prevents this from working () -// struct S s; +struct S s; diff --git a/test/Sema/init.c b/test/Sema/init.c index cbf75e1bb1..7938ec5568 100644 --- a/test/Sema/init.c +++ b/test/Sema/init.c @@ -74,8 +74,7 @@ int sym_fw1a_scr[] = { }; // PR3001 -struct s1 s2 = { // expected-error{{tentative definition has type 'struct s1' that is never completed}} \ - // expected-note{{forward declaration of 'struct s1'}} +struct s1 s2 = { .a = sizeof(struct s3), // expected-error {{invalid application of 'sizeof'}} \ // expected-note{{forward declaration of 'struct s3'}} .b = bogus // expected-error {{use of undeclared identifier 'bogus'}} diff --git a/test/Sema/tentative-decls.c b/test/Sema/tentative-decls.c index 3a830bf428..85b5ed76bd 100644 --- a/test/Sema/tentative-decls.c +++ b/test/Sema/tentative-decls.c @@ -57,3 +57,9 @@ void func2(void) extern double *p; } +// +static int a0[]; +static int b0; + +static int a0[] = { 4 }; +static int b0 = 5; diff --git a/tools/clang-cc/Backend.cpp b/tools/clang-cc/Backend.cpp index 58ecedb7f2..2d8ca82e2f 100644 --- a/tools/clang-cc/Backend.cpp +++ b/tools/clang-cc/Backend.cpp @@ -158,6 +158,10 @@ namespace { "LLVM IR generation of declaration"); Gen->HandleTagDeclDefinition(D); } + + virtual void CompleteTentativeDefinition(VarDecl *D) { + Gen->CompleteTentativeDefinition(D); + } }; } -- 2.40.0