diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Sema/ExternalSemaSource.h | 6 | ||||
-rw-r--r-- | include/clang/Sema/MultiplexExternalSemaSource.h | 4 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 8 | ||||
-rw-r--r-- | include/clang/Serialization/ASTBitCodes.h | 6 | ||||
-rw-r--r-- | include/clang/Serialization/ASTReader.h | 7 | ||||
-rw-r--r-- | lib/Sema/MultiplexExternalSemaSource.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 54 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 32 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 12 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 21 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 18 | ||||
-rw-r--r-- | test/Analysis/engine/replay-without-inlining.c | 2 | ||||
-rw-r--r-- | test/SemaCXX/undefined-inline.cpp | 57 |
14 files changed, 169 insertions, 66 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index fe47c3a9c8..b1f4fb62dc 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3284,10 +3284,12 @@ def note_deleted_assign_field : Note< "%select{copy|move}0 assignment operator of %1 is implicitly deleted " "because field %2 is of %select{reference|const-qualified}4 type %3">; -// This should eventually be an error. +// These should be errors. def warn_undefined_internal : Warning< "%select{function|variable}0 %q1 has internal linkage but is not defined">, InGroup<DiagGroup<"undefined-internal">>; +def warn_undefined_inline : Warning<"inline function %q0 is not defined">, + InGroup<DiagGroup<"undefined-inline">>; def note_used_here : Note<"used here">; def warn_internal_in_extern_inline : ExtWarn< diff --git a/include/clang/Sema/ExternalSemaSource.h b/include/clang/Sema/ExternalSemaSource.h index 6693dc8683..cbce757ea7 100644 --- a/include/clang/Sema/ExternalSemaSource.h +++ b/include/clang/Sema/ExternalSemaSource.h @@ -67,9 +67,11 @@ public: virtual void ReadKnownNamespaces( SmallVectorImpl<NamespaceDecl *> &Namespaces); - virtual void ReadUndefinedInternals( + /// \brief Load the set of used but not defined functions or variables with + /// internal linkage, or used but not defined internal functions. + virtual void ReadUndefinedButUsed( llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined); - + /// \brief Do last resort, unqualified lookup on a LookupResult that /// Sema cannot find. /// diff --git a/include/clang/Sema/MultiplexExternalSemaSource.h b/include/clang/Sema/MultiplexExternalSemaSource.h index 494ba656bb..32b7276c83 100644 --- a/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/include/clang/Sema/MultiplexExternalSemaSource.h @@ -253,7 +253,9 @@ public: /// which will be used during typo correction. virtual void ReadKnownNamespaces(SmallVectorImpl<NamespaceDecl*> &Namespaces); - virtual void ReadUndefinedInternals( + /// \brief Load the set of used but not defined functions or variables with + /// internal linkage, or used but not defined inline functions. + virtual void ReadUndefinedButUsed( llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined); /// \brief Do last resort, unqualified lookup on a LookupResult that diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d0d3ff937d..da80f0c8df 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -738,12 +738,12 @@ public: // argument locations. llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs; - /// UndefinedInternals - all the used, undefined objects with - /// internal linkage in this translation unit. - llvm::DenseMap<NamedDecl *, SourceLocation> UndefinedInternals; + /// UndefinedInternals - all the used, undefined objects which require a + /// definition in this translation unit. + llvm::DenseMap<NamedDecl *, SourceLocation> UndefinedButUsed; /// Obtain a sorted list of functions that are undefined but ODR-used. - void getUndefinedInternals( + void getUndefinedButUsed( llvm::SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined); typedef std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 166434201c..6f76367342 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -526,9 +526,9 @@ namespace clang { /// being deserialized. MACRO_UPDATES = 48, - /// \brief Record code for undefined but used internal functions and - /// variables. - UNDEFINED_INTERNALS = 49 + /// \brief Record code for undefined but used functions and variables that + /// need a definition in this TU. + UNDEFINED_BUT_USED = 49 }; /// \brief Record types used within a source manager block. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index aa3633b4b3..d3f3466c33 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -677,8 +677,9 @@ private: /// \brief A list of the namespaces we've seen. SmallVector<uint64_t, 4> KnownNamespaces; - /// \brief A list of undefined decls with internal linkage. - SmallVector<uint64_t, 8> UndefinedInternals; + /// \brief A list of undefined decls with internal linkage followed by the + /// SourceLocation of a matching ODR-use. + SmallVector<uint64_t, 8> UndefinedButUsed; /// \brief A list of modules that were imported by precompiled headers or /// any other non-module AST file. @@ -1520,7 +1521,7 @@ public: virtual void ReadKnownNamespaces( SmallVectorImpl<NamespaceDecl *> &Namespaces); - virtual void ReadUndefinedInternals( + virtual void ReadUndefinedButUsed( llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined); virtual void ReadTentativeDefinitions( diff --git a/lib/Sema/MultiplexExternalSemaSource.cpp b/lib/Sema/MultiplexExternalSemaSource.cpp index 609324d32d..ecce6d8e7b 100644 --- a/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/lib/Sema/MultiplexExternalSemaSource.cpp @@ -201,10 +201,10 @@ void MultiplexExternalSemaSource::ReadKnownNamespaces( Sources[i]->ReadKnownNamespaces(Namespaces); } -void MultiplexExternalSemaSource::ReadUndefinedInternals( +void MultiplexExternalSemaSource::ReadUndefinedButUsed( llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined){ for(size_t i = 0; i < Sources.size(); ++i) - Sources[i]->ReadUndefinedInternals(Undefined); + Sources[i]->ReadUndefinedButUsed(Undefined); } bool MultiplexExternalSemaSource::LookupUnqualified(LookupResult &R, Scope *S){ diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 76ea996ec5..5e5011b7f6 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -366,12 +366,16 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) { } namespace { - struct SortUndefinedInternal { + struct SortUndefinedButUsed { const SourceManager &SM; - explicit SortUndefinedInternal(SourceManager &SM) : SM(SM) {} + explicit SortUndefinedButUsed(SourceManager &SM) : SM(SM) {} bool operator()(const std::pair<NamedDecl *, SourceLocation> &l, const std::pair<NamedDecl *, SourceLocation> &r) const { + if (l.second.isValid() && !r.second.isValid()) + return true; + if (!l.second.isValid() && r.second.isValid()) + return false; if (l.second != r.second) return SM.isBeforeInTranslationUnit(l.second, r.second); return SM.isBeforeInTranslationUnit(l.first->getLocation(), @@ -381,55 +385,65 @@ namespace { } /// Obtains a sorted list of functions that are undefined but ODR-used. -void Sema::getUndefinedInternals( +void Sema::getUndefinedButUsed( SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined) { for (llvm::DenseMap<NamedDecl *, SourceLocation>::iterator - I = UndefinedInternals.begin(), E = UndefinedInternals.end(); + I = UndefinedButUsed.begin(), E = UndefinedButUsed.end(); I != E; ++I) { NamedDecl *ND = I->first; // Ignore attributes that have become invalid. if (ND->isInvalidDecl()) continue; - // If we found out that the decl is external, don't warn. - if (ND->getLinkage() == ExternalLinkage) continue; - // __attribute__((weakref)) is basically a definition. if (ND->hasAttr<WeakRefAttr>()) continue; if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { if (FD->isDefined()) continue; + if (FD->getLinkage() == ExternalLinkage && + !FD->getMostRecentDecl()->isInlined()) + continue; } else { if (cast<VarDecl>(ND)->hasDefinition() != VarDecl::DeclarationOnly) continue; + if (ND->getLinkage() == ExternalLinkage) + continue; } Undefined.push_back(std::make_pair(ND, I->second)); } - // Sort (in order of use site) so that we're not (as) dependent on - // the iteration order through an llvm::DenseMap. + // Sort (in order of use site) so that we're not dependent on the iteration + // order through an llvm::DenseMap. std::sort(Undefined.begin(), Undefined.end(), - SortUndefinedInternal(Context.getSourceManager())); + SortUndefinedButUsed(Context.getSourceManager())); } -/// checkUndefinedInternals - Check for undefined objects with internal linkage. -static void checkUndefinedInternals(Sema &S) { - if (S.UndefinedInternals.empty()) return; +/// checkUndefinedButUsed - Check for undefined objects with internal linkage +/// or that are inline. +static void checkUndefinedButUsed(Sema &S) { + if (S.UndefinedButUsed.empty()) return; // Collect all the still-undefined entities with internal linkage. SmallVector<std::pair<NamedDecl *, SourceLocation>, 16> Undefined; - S.getUndefinedInternals(Undefined); + S.getUndefinedButUsed(Undefined); if (Undefined.empty()) return; for (SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> >::iterator I = Undefined.begin(), E = Undefined.end(); I != E; ++I) { NamedDecl *ND = I->first; - S.Diag(ND->getLocation(), diag::warn_undefined_internal) - << isa<VarDecl>(ND) << ND; - S.Diag(I->second, diag::note_used_here); + if (ND->getLinkage() != ExternalLinkage) { + S.Diag(ND->getLocation(), diag::warn_undefined_internal) + << isa<VarDecl>(ND) << ND; + } else { + assert(cast<FunctionDecl>(ND)->getMostRecentDecl()->isInlined() && + "used object requires definition but isn't inline or internal?"); + S.Diag(ND->getLocation(), diag::warn_undefined_inline) << ND; + } + if (I->second.isValid()) + S.Diag(I->second, diag::note_used_here); } } @@ -743,8 +757,8 @@ void Sema::ActOnEndOfTranslationUnit() { } if (ExternalSource) - ExternalSource->ReadUndefinedInternals(UndefinedInternals); - checkUndefinedInternals(*this); + ExternalSource->ReadUndefinedButUsed(UndefinedButUsed); + checkUndefinedButUsed(*this); } if (Diags.getDiagnosticLevel(diag::warn_unused_private_field, @@ -1088,7 +1102,7 @@ void ExternalSemaSource::ReadKnownNamespaces( SmallVectorImpl<NamespaceDecl *> &Namespaces) { } -void ExternalSemaSource::ReadUndefinedInternals( +void ExternalSemaSource::ReadUndefinedButUsed( llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) { } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 4404c6f9a0..0d549fcc18 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2215,6 +2215,23 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { New->setType(QualType(NewType, 0)); NewQType = Context.getCanonicalType(New->getType()); } + + // If this redeclaration makes the function inline, we may need to add it to + // UndefinedButUsed. + if (!Old->isInlined() && New->isInlined() && + !New->hasAttr<GNUInlineAttr>() && + (getLangOpts().CPlusPlus || !getLangOpts().GNUInline) && + Old->isUsed(false) && + !Old->isDefined() && !New->isThisDeclarationADefinition()) + UndefinedButUsed.insert(std::make_pair(Old->getCanonicalDecl(), + SourceLocation())); + + // If this redeclaration makes it newly gnu_inline, we don't want to warn + // about it. + if (New->hasAttr<GNUInlineAttr>() && + Old->isInlined() && !Old->hasAttr<GNUInlineAttr>()) { + UndefinedButUsed.erase(Old->getCanonicalDecl()); + } if (getLangOpts().CPlusPlus) { // (C++98 13.1p2): @@ -8431,12 +8448,17 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD) { FD->setBody(Body); - // The only way to be included in UndefinedInternals is if there is an - // ODR-use before the definition. Avoid the expensive map lookup if this + // The only way to be included in UndefinedButUsed is if there is an + // ODR use before the definition. Avoid the expensive map lookup if this // is the first declaration. - if (FD->getPreviousDecl() != 0 && FD->getPreviousDecl()->isUsed() && - FD->getLinkage() != ExternalLinkage) - UndefinedInternals.erase(FD); + if (FD->getPreviousDecl() != 0 && FD->getPreviousDecl()->isUsed()) { + if (FD->getLinkage() != ExternalLinkage) + UndefinedButUsed.erase(FD); + else if (FD->isInlined() && + (LangOpts.CPlusPlus || !LangOpts.GNUInline) && + (!FD->getPreviousDecl()->hasAttr<GNUInlineAttr>())) + UndefinedButUsed.erase(FD); + } // If the function implicitly returns zero (like 'main') or is naked, // don't complain about missing return statements. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 87d75f630f..5dd75ced97 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -10506,9 +10506,13 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { } // Keep track of used but undefined functions. - if (!Func->isDefined() && Func->getLinkage() != ExternalLinkage) { - SourceLocation &old = UndefinedInternals[Func->getCanonicalDecl()]; - if (old.isInvalid()) old = Loc; + if (!Func->isDefined()) { + if (Func->getLinkage() != ExternalLinkage) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + else if (Func->getMostRecentDecl()->isInlined() && + (LangOpts.CPlusPlus || !LangOpts.GNUInline) && + !Func->getMostRecentDecl()->hasAttr<GNUInlineAttr>()) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); } // Normally the must current decl is marked used while processing the use and @@ -11021,7 +11025,7 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var, if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && Var->getLinkage() != ExternalLinkage && !(Var->isStaticDataMember() && Var->hasInit())) { - SourceLocation &old = SemaRef.UndefinedInternals[Var->getCanonicalDecl()]; + SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; if (old.isInvalid()) old = Loc; } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index d38b5308de..1524da9bd6 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2462,19 +2462,19 @@ bool ASTReader::ReadASTBlock(ModuleFile &F) { KnownNamespaces.push_back(getGlobalDeclID(F, Record[I])); break; - case UNDEFINED_INTERNALS: - if (UndefinedInternals.size() % 2 != 0) { - Error("Invalid existing UndefinedInternals"); + case UNDEFINED_BUT_USED: + if (UndefinedButUsed.size() % 2 != 0) { + Error("Invalid existing UndefinedButUsed"); return true; } if (Record.size() % 2 != 0) { - Error("invalid undefined internals record"); + Error("invalid undefined-but-used record"); return true; } for (unsigned I = 0, N = Record.size(); I != N; /* in loop */) { - UndefinedInternals.push_back(getGlobalDeclID(F, Record[I++])); - UndefinedInternals.push_back( + UndefinedButUsed.push_back(getGlobalDeclID(F, Record[I++])); + UndefinedButUsed.push_back( ReadSourceLocation(F, Record, I).getRawEncoding()); } break; @@ -5962,16 +5962,15 @@ void ASTReader::ReadKnownNamespaces( } } -void ASTReader::ReadUndefinedInternals( +void ASTReader::ReadUndefinedButUsed( llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined) { - for (unsigned Idx = 0, N = UndefinedInternals.size(); Idx != N;) { - NamedDecl *D = cast<NamedDecl>(GetDecl(UndefinedInternals[Idx++])); + for (unsigned Idx = 0, N = UndefinedButUsed.size(); Idx != N;) { + NamedDecl *D = cast<NamedDecl>(GetDecl(UndefinedButUsed[Idx++])); SourceLocation Loc = - SourceLocation::getFromRawEncoding(UndefinedInternals[Idx++]); + SourceLocation::getFromRawEncoding(UndefinedButUsed[Idx++]); Undefined.insert(std::make_pair(D, Loc)); } } - void ASTReader::ReadTentativeDefinitions( SmallVectorImpl<VarDecl *> &TentativeDefs) { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 8120799c11..2d975466b5 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -824,7 +824,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(OPENCL_EXTENSIONS); RECORD(DELEGATING_CTORS); RECORD(KNOWN_NAMESPACES); - RECORD(UNDEFINED_INTERNALS); + RECORD(UNDEFINED_BUT_USED); RECORD(MODULE_OFFSET_MAP); RECORD(SOURCE_MANAGER_LINE_TABLE); RECORD(OBJC_CATEGORIES_MAP); @@ -3588,15 +3588,15 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, AddDeclRef(I->first, KnownNamespaces); } - // Build a record of all used, undefined objects with internal linkage. - RecordData UndefinedInternals; + // Build a record of all used, undefined objects that require definitions. + RecordData UndefinedButUsed; SmallVector<std::pair<NamedDecl *, SourceLocation>, 16> Undefined; - SemaRef.getUndefinedInternals(Undefined); + SemaRef.getUndefinedButUsed(Undefined); for (SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> >::iterator I = Undefined.begin(), E = Undefined.end(); I != E; ++I) { - AddDeclRef(I->first, UndefinedInternals); - AddSourceLocation(I->second, UndefinedInternals); + AddDeclRef(I->first, UndefinedButUsed); + AddSourceLocation(I->second, UndefinedButUsed); } // Write the control block @@ -3814,9 +3814,9 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, if (!KnownNamespaces.empty()) Stream.EmitRecord(KNOWN_NAMESPACES, KnownNamespaces); - // Write the undefined internal functions and variables. - if (!UndefinedInternals.empty()) - Stream.EmitRecord(UNDEFINED_INTERNALS, UndefinedInternals); + // Write the undefined internal functions and variables, and inline functions. + if (!UndefinedButUsed.empty()) + Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed); // Write the visible updates to DeclContexts. for (llvm::SmallPtrSet<const DeclContext *, 16>::iterator diff --git a/test/Analysis/engine/replay-without-inlining.c b/test/Analysis/engine/replay-without-inlining.c index 0602973169..14b2b819b9 100644 --- a/test/Analysis/engine/replay-without-inlining.c +++ b/test/Analysis/engine/replay-without-inlining.c @@ -16,7 +16,7 @@ typedef struct { int cur; int end; } IB; -inline unsigned long gl(IB *input); +unsigned long gl(IB *input); inline void gbs(IB *input, unsigned char *buf, int count); void getB(IB *st, Hdr2 *usedtobeundef); inline unsigned char gb(IB *input) { diff --git a/test/SemaCXX/undefined-inline.cpp b/test/SemaCXX/undefined-inline.cpp new file mode 100644 index 0000000000..ad719ae03a --- /dev/null +++ b/test/SemaCXX/undefined-inline.cpp @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// PR14993 + +namespace test1 { + inline void f(); // expected-warning{{inline function 'test1::f' is not defined}} + void test() { f(); } // expected-note{{used here}} +} + +namespace test2 { + inline int f(); + void test() { (void)sizeof(f()); } +} + +namespace test3 { + void f(); // expected-warning{{inline function 'test3::f' is not defined}} + inline void f(); + void test() { f(); } // expected-note{{used here}} +} + +namespace test4 { + inline void error_on_zero(int); // expected-warning{{inline function 'test4::error_on_zero' is not defined}} + inline void error_on_zero(char*) {} + void test() { error_on_zero(0); } // expected-note{{used here}} +} + +namespace test5 { + struct X { void f(); }; + void test(X &x) { x.f(); } +} + +namespace test6 { + struct X { inline void f(); }; // expected-warning{{inline function 'test6::X::f' is not defined}} + void test(X &x) { x.f(); } // expected-note{{used here}} +} + +namespace test7 { + void f(); // expected-warning{{inline function 'test7::f' is not defined}} + void test() { f(); } // no used-here note. + inline void f(); +} + +namespace test8 { + inline void foo() __attribute__((gnu_inline)); + void test() { foo(); } +} + +namespace test9 { + void foo(); + void test() { foo(); } + inline void foo() __attribute__((gnu_inline)); +} + +namespace test10 { + inline void foo(); + void test() { foo(); } + inline void foo() __attribute__((gnu_inline)); +} |