diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index fd79bf59ea91..6dc7e13d8f70 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/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/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 8bda32398fc5..0326b34960fc 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/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/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 0526e7845ccf..9ae93599df16 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/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/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index e7924fade534..21ef8da863c7 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/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/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index 896464ed5a8e..9b85df61da05 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/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/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index ed5c431da82f..19155b6cc345 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/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/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 1f6cd0440c4c..f3c337c148ed 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 716219c034a8..abde26ae533b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/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/clang/test/PCH/external-defs.h b/clang/test/PCH/external-defs.h index 4d233e2a4049..bb51a9d64b9f 100644 --- a/clang/test/PCH/external-defs.h +++ b/clang/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/clang/test/Sema/init.c b/clang/test/Sema/init.c index cbf75e1bb1c6..7938ec5568dc 100644 --- a/clang/test/Sema/init.c +++ b/clang/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/clang/test/Sema/tentative-decls.c b/clang/test/Sema/tentative-decls.c index 3a830bf4283d..85b5ed76bd6b 100644 --- a/clang/test/Sema/tentative-decls.c +++ b/clang/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/clang/tools/clang-cc/Backend.cpp b/clang/tools/clang-cc/Backend.cpp index 58ecedb7f2c1..2d8ca82e2f1c 100644 --- a/clang/tools/clang-cc/Backend.cpp +++ b/clang/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); + } }; }