diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
commit | 56d91b49b13fe55c918afbda19f6165b5fbff87a (patch) | |
tree | 9abb1a658a297776086f4e0dfa6ca533de02104e /lib | |
parent | 41e20f564abdb05101d6b2b29c59459a966c22cc (diff) | |
download | src-56d91b49b13fe55c918afbda19f6165b5fbff87a.tar.gz src-56d91b49b13fe55c918afbda19f6165b5fbff87a.zip |
Vendor import of clang trunk r161861:vendor/clang/clang-trunk-r161861
Notes
Notes:
svn path=/vendor/clang/dist/; revision=239313
svn path=/vendor/clang/clang-trunk-r161861/; revision=239314; tag=vendor/clang/clang-trunk-r161861
Diffstat (limited to 'lib')
353 files changed, 38404 insertions, 18662 deletions
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 9354dc38b8c6..f291dec21fda 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -91,11 +91,40 @@ namespace { class CaptureDiagnosticConsumer : public DiagnosticConsumer { DiagnosticsEngine &Diags; + DiagnosticConsumer &DiagClient; CapturedDiagList &CapturedDiags; + bool HasBegunSourceFile; public: CaptureDiagnosticConsumer(DiagnosticsEngine &diags, - CapturedDiagList &capturedDiags) - : Diags(diags), CapturedDiags(capturedDiags) { } + DiagnosticConsumer &client, + CapturedDiagList &capturedDiags) + : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags), + HasBegunSourceFile(false) { } + + virtual void BeginSourceFile(const LangOptions &Opts, + const Preprocessor *PP) { + // Pass BeginSourceFile message onto DiagClient on first call. + // The corresponding EndSourceFile call will be made from an + // explicit call to FinishCapture. + if (!HasBegunSourceFile) { + DiagClient.BeginSourceFile(Opts, PP); + HasBegunSourceFile = true; + } + } + + void FinishCapture() { + // Call EndSourceFile on DiagClient on completion of capture to + // enable VerifyDiagnosticConsumer to check diagnostics *after* + // it has received the diagnostic list. + if (HasBegunSourceFile) { + DiagClient.EndSourceFile(); + HasBegunSourceFile = false; + } + } + + virtual ~CaptureDiagnosticConsumer() { + assert(!HasBegunSourceFile && "FinishCapture not called!"); + } virtual void HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic &Info) { @@ -195,8 +224,19 @@ createInvocationForMigration(CompilerInvocation &origCI) { CInvok->getLangOpts()->ObjCAutoRefCount = true; CInvok->getLangOpts()->setGC(LangOptions::NonGC); CInvok->getDiagnosticOpts().ErrorLimit = 0; - CInvok->getDiagnosticOpts().Warnings.push_back( - "error=arc-unsafe-retained-assign"); + CInvok->getDiagnosticOpts().PedanticErrors = 0; + + // Ignore -Werror flags when migrating. + std::vector<std::string> WarnOpts; + for (std::vector<std::string>::iterator + I = CInvok->getDiagnosticOpts().Warnings.begin(), + E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) { + if (!StringRef(*I).startswith("error")) + WarnOpts.push_back(*I); + } + WarnOpts.push_back("error=arc-unsafe-retained-assign"); + CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts); + CInvok->getLangOpts()->ObjCRuntimeHasWeak = HasARCRuntime(origCI); return CInvok.take(); @@ -249,13 +289,15 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); // Filter of all diagnostics. - CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); + CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); Diags->setClient(&errRec, /*ShouldOwnClient=*/false); OwningPtr<ASTUnit> Unit( ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags)); - if (!Unit) + if (!Unit) { + errRec.FinishCapture(); return true; + } // Don't filter diagnostics anymore. Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); @@ -267,6 +309,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); capturedDiags.reportDiagnostics(*Diags); DiagClient->EndSourceFile(); + errRec.FinishCapture(); return true; } @@ -304,6 +347,7 @@ bool arcmt::checkForManualIssues(CompilerInvocation &origCI, capturedDiags.reportDiagnostics(*Diags); DiagClient->EndSourceFile(); + errRec.FinishCapture(); // If we are migrating code that gets the '-fobjc-arc' flag, make sure // to remove it so that we don't get errors from normal compilation. @@ -480,13 +524,12 @@ public: class RewritesApplicator : public TransformActions::RewriteReceiver { Rewriter &rewriter; - ASTContext &Ctx; MigrationProcess::RewriteListener *Listener; public: RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, MigrationProcess::RewriteListener *listener) - : rewriter(rewriter), Ctx(ctx), Listener(listener) { + : rewriter(rewriter), Listener(listener) { if (Listener) Listener->start(ctx); } @@ -553,7 +596,7 @@ bool MigrationProcess::applyTransform(TransformFn trans, new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false)); // Filter of all diagnostics. - CaptureDiagnosticConsumer errRec(*Diags, capturedDiags); + CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); Diags->setClient(&errRec, /*ShouldOwnClient=*/false); OwningPtr<ARCMTMacroTrackerAction> ASTAction; @@ -562,8 +605,10 @@ bool MigrationProcess::applyTransform(TransformFn trans, OwningPtr<ASTUnit> Unit( ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags, ASTAction.get())); - if (!Unit) + if (!Unit) { + errRec.FinishCapture(); return true; + } Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. // Don't filter diagnostics anymore. @@ -576,6 +621,7 @@ bool MigrationProcess::applyTransform(TransformFn trans, DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); capturedDiags.reportDiagnostics(*Diags); DiagClient->EndSourceFile(); + errRec.FinishCapture(); return true; } @@ -599,6 +645,7 @@ bool MigrationProcess::applyTransform(TransformFn trans, } DiagClient->EndSourceFile(); + errRec.FinishCapture(); if (DiagClient->getNumErrors()) return true; diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt index fcb7f72ee21d..f602fc8e5f88 100644 --- a/lib/ARCMigrate/CMakeLists.txt +++ b/lib/ARCMigrate/CMakeLists.txt @@ -1,5 +1,3 @@ -set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend clangRewrite) - add_clang_library(clangARCMigrate ARCMT.cpp ARCMTActions.cpp @@ -25,5 +23,19 @@ add_clang_library(clangARCMigrate add_dependencies(clangARCMigrate ClangAttrClasses ClangAttrList + ClangAttrParsedAttrList + ClangCommentNodes ClangDeclNodes - ClangStmtNodes) + ClangDiagnosticCommon + ClangDiagnosticGroups + ClangDiagnosticSema + ClangStmtNodes + ) + +target_link_libraries(clangARCMigrate + clangBasic + clangAST + clangParse + clangFrontend + clangRewrite + ) diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp index 474ce7dcbaf8..e9b49b3039ba 100644 --- a/lib/ARCMigrate/FileRemapper.cpp +++ b/lib/ARCMigrate/FileRemapper.cpp @@ -77,7 +77,9 @@ bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, for (unsigned idx = 0; idx+3 <= lines.size(); idx += 3) { StringRef fromFilename = lines[idx]; unsigned long long timeModified; - lines[idx+1].getAsInteger(10, timeModified); + if (lines[idx+1].getAsInteger(10, timeModified)) + return report("Invalid file data: '" + lines[idx+1] + "' not a number", + Diag); StringRef toFilename = lines[idx+2]; const FileEntry *origFE = FileMgr->getFile(fromFilename); diff --git a/lib/ARCMigrate/Internals.h b/lib/ARCMigrate/Internals.h index 59177c483ea0..935fc9b52535 100644 --- a/lib/ARCMigrate/Internals.h +++ b/lib/ARCMigrate/Internals.h @@ -12,6 +12,7 @@ #include "clang/ARCMigrate/ARCMT.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" namespace clang { class Sema; @@ -144,6 +145,7 @@ public: Sema &SemaRef; TransformActions &TA; std::vector<SourceLocation> &ARCMTMacroLocs; + llvm::Optional<bool> EnableCFBridgeFns; MigrationPass(ASTContext &Ctx, LangOptions::GCMode OrigGCMode, Sema &sema, TransformActions &TA, @@ -157,6 +159,8 @@ public: void setNSAllocReallocError(bool val) { MigOptions.NoNSAllocReallocError = val; } bool noFinalizeRemoval() const { return MigOptions.NoFinalizeRemoval; } void setNoFinalizeRemoval(bool val) {MigOptions.NoFinalizeRemoval = val; } + + bool CFBridgingFunctionsDefined(); }; static inline StringRef getARCMTMacroName() { diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index e63527447633..0098f973e63d 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -10,6 +10,7 @@ #include "clang/ARCMigrate/ARCMTActions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/MultiplexConsumer.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/NSAPI.h" #include "clang/AST/ASTConsumer.h" @@ -209,6 +210,7 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { } bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { + CI.getDiagnostics().setIgnoreAllWarnings(true); CI.getPreprocessorOpts().DetailedRecord = true; CI.getPreprocessorOpts().DetailedRecordConditionalDirectives = true; return true; diff --git a/lib/ARCMigrate/TransAPIUses.cpp b/lib/ARCMigrate/TransAPIUses.cpp index aaa82d8dfb7a..5336f859052f 100644 --- a/lib/ARCMigrate/TransAPIUses.cpp +++ b/lib/ARCMigrate/TransAPIUses.cpp @@ -19,6 +19,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Sema/SemaDiagnostic.h" using namespace clang; diff --git a/lib/ARCMigrate/TransARCAssign.cpp b/lib/ARCMigrate/TransARCAssign.cpp index cfa6da1f99f9..b83f85a1fac2 100644 --- a/lib/ARCMigrate/TransARCAssign.cpp +++ b/lib/ARCMigrate/TransARCAssign.cpp @@ -23,6 +23,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Sema/SemaDiagnostic.h" using namespace clang; diff --git a/lib/ARCMigrate/TransAutoreleasePool.cpp b/lib/ARCMigrate/TransAutoreleasePool.cpp index 87877242a124..5205ce4a70a9 100644 --- a/lib/ARCMigrate/TransAutoreleasePool.cpp +++ b/lib/ARCMigrate/TransAutoreleasePool.cpp @@ -29,6 +29,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Basic/SourceManager.h" #include <map> @@ -75,7 +76,7 @@ public: &pass.Ctx.Idents.get("drain")); } - void transformBody(Stmt *body) { + void transformBody(Stmt *body, Decl *ParentD) { Body = body; TraverseStmt(body); } diff --git a/lib/ARCMigrate/TransBlockObjCVariable.cpp b/lib/ARCMigrate/TransBlockObjCVariable.cpp index 3be8132e3b7e..2a79c9aeff20 100644 --- a/lib/ARCMigrate/TransBlockObjCVariable.cpp +++ b/lib/ARCMigrate/TransBlockObjCVariable.cpp @@ -9,7 +9,7 @@ // // rewriteBlockObjCVariable: // -// Adding __block to an obj-c variable could be either because the the variable +// Adding __block to an obj-c variable could be either because the variable // is used for output storage or the user wanted to break a retain cycle. // This transformation checks whether a reference of the variable for the block // is actually needed (it is assigned to or its address is taken) or not. @@ -27,6 +27,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" using namespace clang; @@ -37,7 +38,6 @@ namespace { class RootBlockObjCVarRewriter : public RecursiveASTVisitor<RootBlockObjCVarRewriter> { - MigrationPass &Pass; llvm::DenseSet<VarDecl *> &VarsToChange; class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> { @@ -71,9 +71,8 @@ class RootBlockObjCVarRewriter : }; public: - RootBlockObjCVarRewriter(MigrationPass &pass, - llvm::DenseSet<VarDecl *> &VarsToChange) - : Pass(pass), VarsToChange(VarsToChange) { } + RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange) + : VarsToChange(VarsToChange) { } bool VisitBlockDecl(BlockDecl *block) { SmallVector<VarDecl *, 4> BlockVars; @@ -111,16 +110,14 @@ private: }; class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> { - MigrationPass &Pass; llvm::DenseSet<VarDecl *> &VarsToChange; public: - BlockObjCVarRewriter(MigrationPass &pass, - llvm::DenseSet<VarDecl *> &VarsToChange) - : Pass(pass), VarsToChange(VarsToChange) { } + BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange) + : VarsToChange(VarsToChange) { } bool TraverseBlockDecl(BlockDecl *block) { - RootBlockObjCVarRewriter(Pass, VarsToChange).TraverseDecl(block); + RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block); return true; } }; @@ -131,7 +128,7 @@ void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) { MigrationPass &Pass = BodyCtx.getMigrationContext().Pass; llvm::DenseSet<VarDecl *> VarsToChange; - BlockObjCVarRewriter trans(Pass, VarsToChange); + BlockObjCVarRewriter trans(VarsToChange); trans.TraverseStmt(BodyCtx.getTopStmt()); for (llvm::DenseSet<VarDecl *>::iterator diff --git a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp index 0fb7141544aa..552cb2fa631c 100644 --- a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp +++ b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp @@ -21,6 +21,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/SourceManager.h" @@ -44,7 +45,7 @@ static bool isEmptyARCMTMacroStatement(NullStmt *S, SourceManager &SM = Ctx.getSourceManager(); std::vector<SourceLocation>::iterator I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc, - SourceManager::LocBeforeThanCompare(SM)); + BeforeThanCompare<SourceLocation>(SM)); --I; SourceLocation AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); @@ -210,8 +211,8 @@ static void cleanupDeallocOrFinalize(MigrationPass &pass) { ObjCMethodDecl *DeallocM = 0; ObjCMethodDecl *FinalizeM = 0; for (ObjCImplementationDecl::instmeth_iterator - MI = (*I)->instmeth_begin(), - ME = (*I)->instmeth_end(); MI != ME; ++MI) { + MI = I->instmeth_begin(), + ME = I->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; if (!MD->hasBody()) continue; diff --git a/lib/ARCMigrate/TransGCAttrs.cpp b/lib/ARCMigrate/TransGCAttrs.cpp index 9f6066ef77da..eec7306ba74a 100644 --- a/lib/ARCMigrate/TransGCAttrs.cpp +++ b/lib/ARCMigrate/TransGCAttrs.cpp @@ -9,12 +9,13 @@ #include "Transforms.h" #include "Internals.h" -#include "clang/Lex/Lexer.h" +#include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" -#include "llvm/Support/SaveAndRestore.h" +#include "clang/Lex/Lexer.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/TinyPtrVector.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace arcmt; @@ -136,7 +137,7 @@ public: if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { for (CXXRecordDecl::method_iterator MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) { - if ((*MI)->isOutOfLine()) + if (MI->isOutOfLine()) return true; } return false; @@ -166,7 +167,7 @@ public: for (Decl::redecl_iterator I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I) - if (!isInMainFile((*I)->getLocation())) + if (!isInMainFile(I->getLocation())) return false; return true; diff --git a/lib/ARCMigrate/TransGCCalls.cpp b/lib/ARCMigrate/TransGCCalls.cpp index 1be902088c62..2ec480c0af64 100644 --- a/lib/ARCMigrate/TransGCCalls.cpp +++ b/lib/ARCMigrate/TransGCCalls.cpp @@ -9,6 +9,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Sema/SemaDiagnostic.h" using namespace clang; @@ -20,13 +21,12 @@ namespace { class GCCollectableCallsChecker : public RecursiveASTVisitor<GCCollectableCallsChecker> { MigrationContext &MigrateCtx; - ParentMap &PMap; IdentifierInfo *NSMakeCollectableII; IdentifierInfo *CFMakeCollectableII; public: - GCCollectableCallsChecker(MigrationContext &ctx, ParentMap &map) - : MigrateCtx(ctx), PMap(map) { + GCCollectableCallsChecker(MigrationContext &ctx) + : MigrateCtx(ctx) { IdentifierTable &Ids = MigrateCtx.Pass.Ctx.Idents; NSMakeCollectableII = &Ids.get("NSMakeCollectable"); CFMakeCollectableII = &Ids.get("CFMakeCollectable"); @@ -78,7 +78,6 @@ public: } // anonymous namespace void GCCollectableCallsTraverser::traverseBody(BodyContext &BodyCtx) { - GCCollectableCallsChecker(BodyCtx.getMigrationContext(), - BodyCtx.getParentMap()) + GCCollectableCallsChecker(BodyCtx.getMigrationContext()) .TraverseStmt(BodyCtx.getTopStmt()); } diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index cc85fe25cca9..fdd6e8863b59 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -309,17 +309,8 @@ private: if (RE->getDecl() != Ivar) return true; - if (ObjCMessageExpr * - ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) - if (ME->getMethodFamily() == OMF_retain) + if (isPlusOneAssign(E)) return false; - - ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); - while (implCE && implCE->getCastKind() == CK_BitCast) - implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); - - if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) - return false; } return true; diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 11a655334144..91d2b399e3f1 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -19,10 +19,11 @@ #include "Transforms.h" #include "Internals.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/ParentMap.h" -#include "clang/Lex/Lexer.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaDiagnostic.h" using namespace clang; using namespace arcmt; @@ -49,7 +50,7 @@ public: Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); } - void transformBody(Stmt *body) { + void transformBody(Stmt *body, Decl *ParentD) { Body = body; collectRemovables(body, Removables); StmtMap.reset(new ParentMap(body)); @@ -64,14 +65,16 @@ public: return true; case OMF_autorelease: if (isRemovable(E)) { - // An unused autorelease is badness. If we remove it the receiver - // will likely die immediately while previously it was kept alive - // by the autorelease pool. This is bad practice in general, leave it - // and emit an error to force the user to restructure his code. - Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " - "message; its receiver may be destroyed immediately", - E->getLocStart(), E->getSourceRange()); - return true; + if (!isCommonUnusedAutorelease(E)) { + // An unused autorelease is badness. If we remove it the receiver + // will likely die immediately while previously it was kept alive + // by the autorelease pool. This is bad practice in general, leave it + // and emit an error to force the user to restructure his code. + Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " + "message; its receiver may be destroyed immediately", + E->getLocStart(), E->getSourceRange()); + return true; + } } // Pass through. case OMF_retain: @@ -156,6 +159,80 @@ public: } private: + /// \brief Checks for idioms where an unused -autorelease is common. + /// + /// Currently only returns true for this idiom which is common in property + /// setters: + /// + /// [backingValue autorelease]; + /// backingValue = [newValue retain]; // in general a +1 assign + /// + bool isCommonUnusedAutorelease(ObjCMessageExpr *E) { + Expr *Rec = E->getInstanceReceiver(); + if (!Rec) + return false; + + Decl *RefD = getReferencedDecl(Rec); + if (!RefD) + return false; + + Stmt *OuterS = E, *InnerS; + do { + InnerS = OuterS; + OuterS = StmtMap->getParent(InnerS); + } + while (OuterS && (isa<ParenExpr>(OuterS) || + isa<CastExpr>(OuterS) || + isa<ExprWithCleanups>(OuterS))); + + if (!OuterS) + return false; + + // Find next statement after the -autorelease. + + Stmt::child_iterator currChildS = OuterS->child_begin(); + Stmt::child_iterator childE = OuterS->child_end(); + for (; currChildS != childE; ++currChildS) { + if (*currChildS == InnerS) + break; + } + if (currChildS == childE) + return false; + ++currChildS; + if (currChildS == childE) + return false; + + Stmt *nextStmt = *currChildS; + if (!nextStmt) + return false; + nextStmt = nextStmt->IgnoreImplicit(); + + // Check for "RefD = [+1 retained object];". + + if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) { + if (RefD != getReferencedDecl(Bop->getLHS())) + return false; + if (isPlusOneAssign(Bop)) + return true; + } + return false; + } + + Decl *getReferencedDecl(Expr *E) { + if (!E) + return 0; + + E = E->IgnoreParenCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getDecl(); + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E)) + return IRE->getDecl(); + + return 0; + } + /// \brief Check if the retain/release is due to a GCD/XPC macro that are /// defined as: /// diff --git a/lib/ARCMigrate/TransUnbridgedCasts.cpp b/lib/ARCMigrate/TransUnbridgedCasts.cpp index 48437c795fa9..ac18b5d6e739 100644 --- a/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -12,7 +12,7 @@ // A cast of non-objc pointer to an objc one is checked. If the non-objc pointer // is from a file-level variable, __bridge cast is used to convert it. // For the result of a function call that we know is +1/+0, -// __bridge/__bridge_transfer is used. +// __bridge/CFBridgingRelease is used. // // NSString *str = (NSString *)kUTTypePlainText; // str = b ? kUTTypeRTF : kUTTypePlainText; @@ -21,8 +21,8 @@ // ----> // NSString *str = (__bridge NSString *)kUTTypePlainText; // str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); -// NSString *_uuidString = (__bridge_transfer NSString *) -// CFUUIDCreateString(kCFAllocatorDefault, _uuid); +// NSString *_uuidString = (NSString *) +// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid)); // // For a C pointer to ObjC, for casting 'self', __bridge is used. // @@ -35,9 +35,11 @@ #include "Transforms.h" #include "Internals.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/SmallString.h" using namespace clang; @@ -50,13 +52,15 @@ class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ MigrationPass &Pass; IdentifierInfo *SelfII; OwningPtr<ParentMap> StmtMap; + Decl *ParentD; public: - UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) { + UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass), ParentD(0) { SelfII = &Pass.Ctx.Idents.get("self"); } - void transformBody(Stmt *body) { + void transformBody(Stmt *body, Decl *ParentD) { + this->ParentD = ParentD; StmtMap.reset(new ParentMap(body)); TraverseStmt(body); } @@ -155,6 +159,21 @@ private: } } } + + // If returning an ivar or a member of an ivar from a +0 method, use + // a __bridge cast. + Expr *base = inner->IgnoreParenImpCasts(); + while (isa<MemberExpr>(base)) + base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts(); + if (isa<ObjCIvarRefExpr>(base) && + isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) { + if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) { + if (!method->hasAttr<NSReturnsRetainedAttr>()) { + castToObjCObject(E, /*retained=*/false); + return; + } + } + } } void castToObjCObject(CastExpr *E, bool retained) { @@ -191,22 +210,48 @@ private: TA.clearDiagnostic(diag::err_arc_mismatched_cast, diag::err_arc_cast_requires_bridge, E->getLocStart()); - if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { - TA.insertAfterToken(CCE->getLParenLoc(), bridge); - } else { - SourceLocation insertLoc = E->getSubExpr()->getLocStart(); - SmallString<128> newCast; - newCast += '('; - newCast += bridge; - newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); - newCast += ')'; - - if (isa<ParenExpr>(E->getSubExpr())) { - TA.insert(insertLoc, newCast.str()); + if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) { + if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { + TA.insertAfterToken(CCE->getLParenLoc(), bridge); } else { + SourceLocation insertLoc = E->getSubExpr()->getLocStart(); + SmallString<128> newCast; newCast += '('; - TA.insert(insertLoc, newCast.str()); - TA.insertAfterToken(E->getLocEnd(), ")"); + newCast += bridge; + newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); + newCast += ')'; + + if (isa<ParenExpr>(E->getSubExpr())) { + TA.insert(insertLoc, newCast.str()); + } else { + newCast += '('; + TA.insert(insertLoc, newCast.str()); + TA.insertAfterToken(E->getLocEnd(), ")"); + } + } + } else { + assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained); + SmallString<32> BridgeCall; + + Expr *WrapE = E->getSubExpr(); + SourceLocation InsertLoc = WrapE->getLocStart(); + + SourceManager &SM = Pass.Ctx.getSourceManager(); + char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1)); + if (Lexer::isIdentifierBodyChar(PrevChar, Pass.Ctx.getLangOpts())) + BridgeCall += ' '; + + if (Kind == OBC_BridgeTransfer) + BridgeCall += "CFBridgingRelease"; + else + BridgeCall += "CFBridgingRetain"; + + if (isa<ParenExpr>(WrapE)) { + TA.insert(InsertLoc, BridgeCall); + } else { + BridgeCall += '('; + TA.insert(InsertLoc, BridgeCall); + TA.insertAfterToken(WrapE->getLocEnd(), ")"); } } } diff --git a/lib/ARCMigrate/TransUnusedInitDelegate.cpp b/lib/ARCMigrate/TransUnusedInitDelegate.cpp index 60ed32aef4ce..3057e391d0a8 100644 --- a/lib/ARCMigrate/TransUnusedInitDelegate.cpp +++ b/lib/ARCMigrate/TransUnusedInitDelegate.cpp @@ -22,6 +22,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/Sema/SemaDiagnostic.h" using namespace clang; @@ -40,7 +41,7 @@ public: UnusedInitRewriter(MigrationPass &pass) : Body(0), Pass(pass) { } - void transformBody(Stmt *body) { + void transformBody(Stmt *body, Decl *ParentD) { Body = body; collectRemovables(body, Removables); TraverseStmt(body); diff --git a/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp index d1f08aac28c2..a07596d0cb0c 100644 --- a/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp +++ b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp @@ -15,6 +15,7 @@ #include "Transforms.h" #include "Internals.h" +#include "clang/AST/ASTContext.h" using namespace clang; using namespace arcmt; diff --git a/lib/ARCMigrate/TransformActions.cpp b/lib/ARCMigrate/TransformActions.cpp index 0ecfeb54f85d..783db1c8f3aa 100644 --- a/lib/ARCMigrate/TransformActions.cpp +++ b/lib/ARCMigrate/TransformActions.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Internals.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/SourceManager.h" diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp index d342d1aa0477..1175c363163b 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -9,11 +9,14 @@ #include "Transforms.h" #include "Internals.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" -#include "clang/Lex/Lexer.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/DenseSet.h" #include <map> @@ -24,6 +27,13 @@ using namespace trans; ASTTraverser::~ASTTraverser() { } +bool MigrationPass::CFBridgingFunctionsDefined() { + if (!EnableCFBridgeFns.hasValue()) + EnableCFBridgeFns = SemaRef.isKnownName("CFBridgingRetain") && + SemaRef.isKnownName("CFBridgingRelease"); + return *EnableCFBridgeFns; +} + //===----------------------------------------------------------------------===// // Helpers. //===----------------------------------------------------------------------===// @@ -56,6 +66,47 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type, return true; } +bool trans::isPlusOneAssign(const BinaryOperator *E) { + if (E->getOpcode() != BO_Assign) + return false; + + if (const ObjCMessageExpr * + ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts())) + if (ME->getMethodFamily() == OMF_retain) + return true; + + if (const CallExpr * + callE = dyn_cast<CallExpr>(E->getRHS()->IgnoreParenCasts())) { + if (const FunctionDecl *FD = callE->getDirectCallee()) { + if (FD->getAttr<CFReturnsRetainedAttr>()) + return true; + + if (FD->isGlobal() && + FD->getIdentifier() && + FD->getParent()->isTranslationUnit() && + FD->getLinkage() == ExternalLinkage && + ento::cocoa::isRefType(callE->getType(), "CF", + FD->getIdentifier()->getName())) { + StringRef fname = FD->getIdentifier()->getName(); + if (fname.endswith("Retain") || + fname.find("Create") != StringRef::npos || + fname.find("Copy") != StringRef::npos) { + return true; + } + } + } + } + + const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS()); + while (implCE && implCE->getCastKind() == CK_BitCast) + implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr()); + + if (implCE && implCE->getCastKind() == CK_ARCConsumeObject) + return true; + + return false; +} + /// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned @@ -472,8 +523,8 @@ static void GCRewriteFinalize(MigrationPass &pass) { for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { for (ObjCImplementationDecl::instmeth_iterator - MI = (*I)->instmeth_begin(), - ME = (*I)->instmeth_end(); MI != ME; ++MI) { + MI = I->instmeth_begin(), + ME = I->instmeth_end(); MI != ME; ++MI) { ObjCMethodDecl *MD = *MI; if (!MD->hasBody()) continue; diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h index 445c3e599d65..5d4ac9446045 100644 --- a/lib/ARCMigrate/Transforms.h +++ b/lib/ARCMigrate/Transforms.h @@ -13,6 +13,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/SaveAndRestore.h" namespace clang { class Decl; @@ -154,6 +155,8 @@ public: bool canApplyWeak(ASTContext &Ctx, QualType type, bool AllowOnUnknownClass = false); +bool isPlusOneAssign(const BinaryOperator *E); + /// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned @@ -174,15 +177,22 @@ StringRef getNilString(ASTContext &Ctx); template <typename BODY_TRANS> class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > { MigrationPass &Pass; + Decl *ParentD; + typedef RecursiveASTVisitor<BodyTransform<BODY_TRANS> > base; public: - BodyTransform(MigrationPass &pass) : Pass(pass) { } + BodyTransform(MigrationPass &pass) : Pass(pass), ParentD(0) { } bool TraverseStmt(Stmt *rootS) { if (rootS) - BODY_TRANS(Pass).transformBody(rootS); + BODY_TRANS(Pass).transformBody(rootS, ParentD); return true; } + + bool TraverseObjCMethodDecl(ObjCMethodDecl *D) { + SaveAndRestore<Decl *> SetParent(ParentD, D); + return base::TraverseObjCMethodDecl(D); + } }; typedef llvm::DenseSet<Expr *> ExprSet; diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index a31b3c5bfb37..a74ef14a9e2b 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -467,9 +467,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ FI != RD->field_end(); ++FI) { if (!First) Out << ", "; - if ((*FI)->isUnnamedBitfield()) continue; - getStructField((*FI)->getFieldIndex()). - printPretty(Out, Ctx, (*FI)->getType()); + if (FI->isUnnamedBitfield()) continue; + getStructField(FI->getFieldIndex()). + printPretty(Out, Ctx, FI->getType()); First = false; } Out << '}'; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index cb4d336de157..ad48dffb4d4f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/CommentCommandTraits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -53,6 +54,218 @@ enum FloatingRank { HalfRank, FloatRank, DoubleRank, LongDoubleRank }; +RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { + if (!CommentsLoaded && ExternalSource) { + ExternalSource->ReadComments(); + CommentsLoaded = true; + } + + assert(D); + + // User can not attach documentation to implicit declarations. + if (D->isImplicit()) + return NULL; + + // TODO: handle comments for function parameters properly. + if (isa<ParmVarDecl>(D)) + return NULL; + + // TODO: we could look up template parameter documentation in the template + // documentation. + if (isa<TemplateTypeParmDecl>(D) || + isa<NonTypeTemplateParmDecl>(D) || + isa<TemplateTemplateParmDecl>(D)) + return NULL; + + ArrayRef<RawComment *> RawComments = Comments.getComments(); + + // If there are no comments anywhere, we won't find anything. + if (RawComments.empty()) + return NULL; + + // Find declaration location. + // For Objective-C declarations we generally don't expect to have multiple + // declarators, thus use declaration starting location as the "declaration + // location". + // For all other declarations multiple declarators are used quite frequently, + // so we use the location of the identifier as the "declaration location". + SourceLocation DeclLoc; + if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) || + isa<ObjCPropertyDecl>(D) || + isa<RedeclarableTemplateDecl>(D) || + isa<ClassTemplateSpecializationDecl>(D)) + DeclLoc = D->getLocStart(); + else + DeclLoc = D->getLocation(); + + // If the declaration doesn't map directly to a location in a file, we + // can't find the comment. + if (DeclLoc.isInvalid() || !DeclLoc.isFileID()) + return NULL; + + // Find the comment that occurs just after this declaration. + ArrayRef<RawComment *>::iterator Comment; + { + // When searching for comments during parsing, the comment we are looking + // for is usually among the last two comments we parsed -- check them + // first. + RawComment CommentAtDeclLoc(SourceMgr, SourceRange(DeclLoc)); + BeforeThanCompare<RawComment> Compare(SourceMgr); + ArrayRef<RawComment *>::iterator MaybeBeforeDecl = RawComments.end() - 1; + bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc); + if (!Found && RawComments.size() >= 2) { + MaybeBeforeDecl--; + Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc); + } + + if (Found) { + Comment = MaybeBeforeDecl + 1; + assert(Comment == std::lower_bound(RawComments.begin(), RawComments.end(), + &CommentAtDeclLoc, Compare)); + } else { + // Slow path. + Comment = std::lower_bound(RawComments.begin(), RawComments.end(), + &CommentAtDeclLoc, Compare); + } + } + + // Decompose the location for the declaration and find the beginning of the + // file buffer. + std::pair<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc); + + // First check whether we have a trailing comment. + if (Comment != RawComments.end() && + (*Comment)->isDocumentation() && (*Comment)->isTrailingComment() && + (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D))) { + std::pair<FileID, unsigned> CommentBeginDecomp + = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getBegin()); + // Check that Doxygen trailing comment comes after the declaration, starts + // on the same line and in the same file as the declaration. + if (DeclLocDecomp.first == CommentBeginDecomp.first && + SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second) + == SourceMgr.getLineNumber(CommentBeginDecomp.first, + CommentBeginDecomp.second)) { + (*Comment)->setDecl(D); + return *Comment; + } + } + + // The comment just after the declaration was not a trailing comment. + // Let's look at the previous comment. + if (Comment == RawComments.begin()) + return NULL; + --Comment; + + // Check that we actually have a non-member Doxygen comment. + if (!(*Comment)->isDocumentation() || (*Comment)->isTrailingComment()) + return NULL; + + // Decompose the end of the comment. + std::pair<FileID, unsigned> CommentEndDecomp + = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getEnd()); + + // If the comment and the declaration aren't in the same file, then they + // aren't related. + if (DeclLocDecomp.first != CommentEndDecomp.first) + return NULL; + + // Get the corresponding buffer. + bool Invalid = false; + const char *Buffer = SourceMgr.getBufferData(DeclLocDecomp.first, + &Invalid).data(); + if (Invalid) + return NULL; + + // Extract text between the comment and declaration. + StringRef Text(Buffer + CommentEndDecomp.second, + DeclLocDecomp.second - CommentEndDecomp.second); + + // There should be no other declarations or preprocessor directives between + // comment and declaration. + if (Text.find_first_of(",;{}#@") != StringRef::npos) + return NULL; + + (*Comment)->setDecl(D); + return *Comment; +} + +const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { + // If we have a 'templated' declaration for a template, adjust 'D' to + // refer to the actual template. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) + D = FTD; + } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { + if (const ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) + D = CTD; + } + // FIXME: Alias templates? + + // Check whether we have cached a comment for this declaration already. + { + llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos = + RedeclComments.find(D); + if (Pos != RedeclComments.end()) { + const RawCommentAndCacheFlags &Raw = Pos->second; + if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) + return Raw.getRaw(); + } + } + + // Search for comments attached to declarations in the redeclaration chain. + const RawComment *RC = NULL; + for (Decl::redecl_iterator I = D->redecls_begin(), + E = D->redecls_end(); + I != E; ++I) { + llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos = + RedeclComments.find(*I); + if (Pos != RedeclComments.end()) { + const RawCommentAndCacheFlags &Raw = Pos->second; + if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) { + RC = Raw.getRaw(); + break; + } + } else { + RC = getRawCommentForDeclNoCache(*I); + RawCommentAndCacheFlags Raw; + if (RC) { + Raw.setRaw(RC); + Raw.setKind(RawCommentAndCacheFlags::FromDecl); + } else + Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl); + RedeclComments[*I] = Raw; + if (RC) + break; + } + } + + // If we found a comment, it should be a documentation comment. + assert(!RC || RC->isDocumentation()); + + // Update cache for every declaration in the redeclaration chain. + RawCommentAndCacheFlags Raw; + Raw.setRaw(RC); + Raw.setKind(RawCommentAndCacheFlags::FromRedecl); + + for (Decl::redecl_iterator I = D->redecls_begin(), + E = D->redecls_end(); + I != E; ++I) { + RawCommentAndCacheFlags &R = RedeclComments[*I]; + if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl) + R = Raw; + } + + return RC; +} + +comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const { + const RawComment *RC = getRawCommentForAnyRedecl(D); + if (!RC) + return NULL; + + return RC->getParsed(*this); +} + void ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, TemplateTemplateParmDecl *Parm) { @@ -206,7 +419,10 @@ static const LangAS::Map *getAddressSpaceMap(const TargetInfo &T, static const unsigned FakeAddrSpaceMap[] = { 1, // opencl_global 2, // opencl_local - 3 // opencl_constant + 3, // opencl_constant + 4, // cuda_device + 5, // cuda_constant + 6 // cuda_shared }; return &FakeAddrSpaceMap; } else { @@ -226,6 +442,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM, SubstTemplateTemplateParmPacks(this_()), GlobalNestedNameSpecifier(0), Int128Decl(0), UInt128Decl(0), + BuiltinVaListDecl(0), ObjCIdDecl(0), ObjCSelDecl(0), ObjCClassDecl(0), ObjCProtocolClassDecl(0), CFConstantStringTypeDecl(0), ObjCInstanceTypeDecl(0), FILEDecl(0), @@ -240,6 +457,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM, BuiltinInfo(builtins), DeclarationNames(*this), ExternalSource(0), Listener(0), + Comments(SM), CommentsLoaded(false), LastSDM(0, 0), UniqueBlockByRefTypeID(0) { @@ -436,6 +654,8 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { } else // C99 WCharTy = getFromTargetType(Target.getWCharType()); + WIntTy = getFromTargetType(Target.getWIntType()); + if (LangOpts.CPlusPlus) // C++0x 3.9.1p5, extension for C++ InitBuiltinType(Char16Ty, BuiltinType::Char16); else // C99 @@ -473,8 +693,6 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { DoubleComplexTy = getComplexType(DoubleTy); LongDoubleComplexTy = getComplexType(LongDoubleTy); - BuiltinVaListType = QualType(); - // Builtin types for 'id', 'Class', and 'SEL'. InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId); InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass); @@ -494,6 +712,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { // half type (OpenCL 6.1.1.1) / ARM NEON __fp16 InitBuiltinType(HalfTy, BuiltinType::Half); + + // Builtin type used to help define __builtin_va_list. + VaListTagTy = QualType(); } DiagnosticsEngine &ASTContext::getDiagnostics() const { @@ -881,6 +1102,10 @@ ASTContext::getTypeInfoImpl(const Type *T) const { Align = llvm::NextPowerOf2(Align); Width = llvm::RoundUpToAlignment(Width, Align); } + // Adjust the alignment based on the target max. + uint64_t TargetVectorAlign = Target->getMaxVectorAlign(); + if (TargetVectorAlign && TargetVectorAlign < Align) + Align = TargetVectorAlign; break; } @@ -1337,14 +1562,6 @@ void ASTContext::setBlockVarCopyInits(VarDecl*VD, Expr* Init) { BlockVarCopyInits[VD] = Init; } -/// \brief Allocate an uninitialized TypeSourceInfo. -/// -/// The caller should initialize the memory held by TypeSourceInfo using -/// the TypeLoc wrappers. -/// -/// \param T the type that will be the basis for type source info. This type -/// should refer to how the declarator was written in source code, not to -/// what type semantic analysis resolved the declarator to. TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T, unsigned DataSize) const { if (!DataSize) @@ -2187,15 +2404,18 @@ ASTContext::getFunctionType(QualType ResultTy, // - exception types // - consumed-arguments flags // Instead of the exception types, there could be a noexcept - // expression. + // expression, or information used to resolve the exception + // specification. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); - if (EPI.ExceptionSpecType == EST_Dynamic) + if (EPI.ExceptionSpecType == EST_Dynamic) { Size += EPI.NumExceptions * sizeof(QualType); - else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { + } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { Size += sizeof(Expr*); } else if (EPI.ExceptionSpecType == EST_Uninstantiated) { Size += 2 * sizeof(FunctionDecl*); + } else if (EPI.ExceptionSpecType == EST_Unevaluated) { + Size += sizeof(FunctionDecl*); } if (EPI.ConsumedArguments) Size += NumArgs * sizeof(bool); @@ -2730,10 +2950,17 @@ QualType ASTContext::getPackExpansionType(QualType Pattern, QualType Canon; if (!Pattern.isCanonical()) { - Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions); - - // Find the insert position again. - PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos); + Canon = getCanonicalType(Pattern); + // The canonical type might not contain an unexpanded parameter pack, if it + // contains an alias template specialization which ignores one of its + // parameters. + if (Canon->containsUnexpandedParameterPack()) { + Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions); + + // Find the insert position again, in case we inserted an element into + // PackExpansionTypes and invalidated our insert position. + PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos); + } } T = new (*this) PackExpansionType(Pattern, Canon, NumExpansions); @@ -2950,7 +3177,7 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const { if (Canon) { // We already have a "canonical" version of an equivalent, dependent // decltype type. Use that as our canonical type. - dt = new (*this, TypeAlignment) DecltypeType(e, DependentTy, + dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType, QualType((DecltypeType*)Canon, 0)); } else { // Build a new, canonical typeof(expr) type. @@ -3331,8 +3558,7 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { Arg.getNumTemplateExpansions()); case TemplateArgument::Integral: - return TemplateArgument(*Arg.getAsIntegral(), - getCanonicalType(Arg.getIntegralType())); + return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType())); @@ -3471,7 +3697,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const { VAT->getBracketsRange())); } -QualType ASTContext::getAdjustedParameterType(QualType T) { +QualType ASTContext::getAdjustedParameterType(QualType T) const { // C99 6.7.5.3p7: // A declaration of a parameter as "array of type" shall be // adjusted to "qualified pointer to type", where the type @@ -3490,7 +3716,7 @@ QualType ASTContext::getAdjustedParameterType(QualType T) { return T; } -QualType ASTContext::getSignatureParameterType(QualType T) { +QualType ASTContext::getSignatureParameterType(QualType T) const { T = getVariableArrayDecayedType(T); T = getAdjustedParameterType(T); return T.getUnqualifiedType(); @@ -3809,7 +4035,7 @@ QualType ASTContext::getCFConstantStringType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); CFConstantStringTypeDecl->addDecl(Field); } @@ -3853,7 +4079,7 @@ QualType ASTContext::getBlockDescriptorType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3896,7 +4122,7 @@ QualType ASTContext::getBlockDescriptorExtendedType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3972,7 +4198,7 @@ ASTContext::BuildByRefType(StringRef DeclName, QualType Ty) const { &Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -4045,6 +4271,8 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { E = Decl->param_end(); PI != E; ++PI) { QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); + if (sz.isZero()) + continue; assert (sz.isPositive() && "BlockExpr - Incomplete param type"); ParmOffset += sz; } @@ -4086,8 +4314,8 @@ bool ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl, QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) - return true; - + continue; + assert (sz.isPositive() && "getObjCEncodingForFunctionDecl - Incomplete param type"); ParmOffset += sz; @@ -4155,8 +4383,8 @@ bool ASTContext::getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl, QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) - return true; - + continue; + assert (sz.isPositive() && "getObjCEncodingForMethodDecl - Incomplete param type"); ParmOffset += sz; @@ -4387,7 +4615,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, // information is not especially sensible, but we're stuck with it for // compatibility with GCC, although providing it breaks anything that // actually uses runtime introspection and wants to work on both runtimes... - if (!Ctx->getLangOpts().NeXTRuntime) { + if (Ctx->getLangOpts().ObjCRuntime.isGNUFamily()) { const RecordDecl *RD = FD->getParent(); const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD); S += llvm::utostr(RL.getFieldOffset(FD->getFieldIndex())); @@ -4563,7 +4791,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S, // Special case bit-fields. if (Field->isBitField()) { getObjCEncodingForTypeImpl(Field->getType(), S, false, true, - (*Field)); + *Field); } else { QualType qt = Field->getType(); getLegacyIntegralTypeEncoding(qt); @@ -4746,7 +4974,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); if (base->isEmpty()) continue; - uint64_t offs = layout.getBaseClassOffsetInBits(base); + uint64_t offs = toBits(layout.getBaseClassOffset(base)); FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs), std::make_pair(offs, base)); } @@ -4769,7 +4997,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); if (base->isEmpty()) continue; - uint64_t offs = layout.getVBaseClassOffsetInBits(base); + uint64_t offs = toBits(layout.getVBaseClassOffset(base)); if (FieldOrBaseOffsets.find(offs) == FieldOrBaseOffsets.end()) FieldOrBaseOffsets.insert(FieldOrBaseOffsets.end(), std::make_pair(offs, base)); @@ -4787,11 +5015,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, std::multimap<uint64_t, NamedDecl *>::iterator CurLayObj = FieldOrBaseOffsets.begin(); - if ((CurLayObj != FieldOrBaseOffsets.end() && CurLayObj->first != 0) || - (CurLayObj == FieldOrBaseOffsets.end() && - CXXRec && CXXRec->isDynamicClass())) { - assert(CXXRec && CXXRec->isDynamicClass() && - "Offset 0 was empty but no VTable ?"); + if (CXXRec && CXXRec->isDynamicClass() && + (CurLayObj == FieldOrBaseOffsets.end() || CurLayObj->first != 0)) { if (FD) { S += "\"_vptr$"; std::string recname = CXXRec->getNameAsString(); @@ -4877,12 +5102,6 @@ void ASTContext::getObjCEncodingForTypeQualifier(Decl::ObjCDeclQualifier QT, S += 'V'; } -void ASTContext::setBuiltinVaListType(QualType T) { - assert(BuiltinVaListType.isNull() && "__builtin_va_list type already set!"); - - BuiltinVaListType = T; -} - TypedefDecl *ASTContext::getObjCIdDecl() const { if (!ObjCIdDecl) { QualType T = getObjCObjectType(ObjCBuiltinIdTy, 0, 0); @@ -4936,6 +5155,241 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const { return ObjCProtocolClassDecl; } +//===----------------------------------------------------------------------===// +// __builtin_va_list Construction Functions +//===----------------------------------------------------------------------===// + +static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) { + // typedef char* __builtin_va_list; + QualType CharPtrType = Context->getPointerType(Context->CharTy); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(CharPtrType); + + TypedefDecl *VaListTypeDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + return VaListTypeDecl; +} + +static TypedefDecl *CreateVoidPtrBuiltinVaListDecl(const ASTContext *Context) { + // typedef void* __builtin_va_list; + QualType VoidPtrType = Context->getPointerType(Context->VoidTy); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VoidPtrType); + + TypedefDecl *VaListTypeDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + return VaListTypeDecl; +} + +static TypedefDecl *CreatePowerABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef struct __va_list_tag { + RecordDecl *VaListTagDecl; + + VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + &Context->Idents.get("__va_list_tag")); + VaListTagDecl->startDefinition(); + + const size_t NumFields = 5; + QualType FieldTypes[NumFields]; + const char *FieldNames[NumFields]; + + // unsigned char gpr; + FieldTypes[0] = Context->UnsignedCharTy; + FieldNames[0] = "gpr"; + + // unsigned char fpr; + FieldTypes[1] = Context->UnsignedCharTy; + FieldNames[1] = "fpr"; + + // unsigned short reserved; + FieldTypes[2] = Context->UnsignedShortTy; + FieldNames[2] = "reserved"; + + // void* overflow_arg_area; + FieldTypes[3] = Context->getPointerType(Context->VoidTy); + FieldNames[3] = "overflow_arg_area"; + + // void* reg_save_area; + FieldTypes[4] = Context->getPointerType(Context->VoidTy); + FieldNames[4] = "reg_save_area"; + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create(*Context, VaListTagDecl, + SourceLocation(), + SourceLocation(), + &Context->Idents.get(FieldNames[i]), + FieldTypes[i], /*TInfo=*/0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit); + Field->setAccess(AS_public); + VaListTagDecl->addDecl(Field); + } + VaListTagDecl->completeDefinition(); + QualType VaListTagType = Context->getRecordType(VaListTagDecl); + Context->VaListTagTy = VaListTagType; + + // } __va_list_tag; + TypedefDecl *VaListTagTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__va_list_tag"), + Context->getTrivialTypeSourceInfo(VaListTagType)); + QualType VaListTagTypedefType = + Context->getTypedefType(VaListTagTypedefDecl); + + // typedef __va_list_tag __builtin_va_list[1]; + llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); + QualType VaListTagArrayType + = Context->getConstantArrayType(VaListTagTypedefType, + Size, ArrayType::Normal, 0); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VaListTagArrayType); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + + return VaListTypedefDecl; +} + +static TypedefDecl * +CreateX86_64ABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef struct __va_list_tag { + RecordDecl *VaListTagDecl; + VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + &Context->Idents.get("__va_list_tag")); + VaListTagDecl->startDefinition(); + + const size_t NumFields = 4; + QualType FieldTypes[NumFields]; + const char *FieldNames[NumFields]; + + // unsigned gp_offset; + FieldTypes[0] = Context->UnsignedIntTy; + FieldNames[0] = "gp_offset"; + + // unsigned fp_offset; + FieldTypes[1] = Context->UnsignedIntTy; + FieldNames[1] = "fp_offset"; + + // void* overflow_arg_area; + FieldTypes[2] = Context->getPointerType(Context->VoidTy); + FieldNames[2] = "overflow_arg_area"; + + // void* reg_save_area; + FieldTypes[3] = Context->getPointerType(Context->VoidTy); + FieldNames[3] = "reg_save_area"; + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create(const_cast<ASTContext &>(*Context), + VaListTagDecl, + SourceLocation(), + SourceLocation(), + &Context->Idents.get(FieldNames[i]), + FieldTypes[i], /*TInfo=*/0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit); + Field->setAccess(AS_public); + VaListTagDecl->addDecl(Field); + } + VaListTagDecl->completeDefinition(); + QualType VaListTagType = Context->getRecordType(VaListTagDecl); + Context->VaListTagTy = VaListTagType; + + // } __va_list_tag; + TypedefDecl *VaListTagTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__va_list_tag"), + Context->getTrivialTypeSourceInfo(VaListTagType)); + QualType VaListTagTypedefType = + Context->getTypedefType(VaListTagTypedefDecl); + + // typedef __va_list_tag __builtin_va_list[1]; + llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); + QualType VaListTagArrayType + = Context->getConstantArrayType(VaListTagTypedefType, + Size, ArrayType::Normal,0); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VaListTagArrayType); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + + return VaListTypedefDecl; +} + +static TypedefDecl *CreatePNaClABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef int __builtin_va_list[4]; + llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 4); + QualType IntArrayType + = Context->getConstantArrayType(Context->IntTy, + Size, ArrayType::Normal, 0); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + Context->getTrivialTypeSourceInfo(IntArrayType)); + + return VaListTypedefDecl; +} + +static TypedefDecl *CreateVaListDecl(const ASTContext *Context, + TargetInfo::BuiltinVaListKind Kind) { + switch (Kind) { + case TargetInfo::CharPtrBuiltinVaList: + return CreateCharPtrBuiltinVaListDecl(Context); + case TargetInfo::VoidPtrBuiltinVaList: + return CreateVoidPtrBuiltinVaListDecl(Context); + case TargetInfo::PowerABIBuiltinVaList: + return CreatePowerABIBuiltinVaListDecl(Context); + case TargetInfo::X86_64ABIBuiltinVaList: + return CreateX86_64ABIBuiltinVaListDecl(Context); + case TargetInfo::PNaClABIBuiltinVaList: + return CreatePNaClABIBuiltinVaListDecl(Context); + } + + llvm_unreachable("Unhandled __builtin_va_list type kind"); +} + +TypedefDecl *ASTContext::getBuiltinVaListDecl() const { + if (!BuiltinVaListDecl) + BuiltinVaListDecl = CreateVaListDecl(this, Target->getBuiltinVaListKind()); + + return BuiltinVaListDecl; +} + +QualType ASTContext::getVaListTagType() const { + // Force the creation of VaListTagTy by building the __builtin_va_list + // declaration. + if (VaListTagTy.isNull()) + (void) getBuiltinVaListDecl(); + + return VaListTagTy; +} + void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) { assert(ObjCConstantStringType.isNull() && "'NSConstantString' type already set!"); @@ -6412,6 +6866,19 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, VectorType::GenericVector); break; } + case 'E': { + char *End; + + unsigned NumElements = strtoul(Str, &End, 10); + assert(End != Str && "Missing vector size"); + + Str = End; + + QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE, + false); + Type = Context.getExtVectorType(ElementType, NumElements); + break; + } case 'X': { QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE, false); @@ -6712,9 +7179,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return true; } -CallingConv ASTContext::getDefaultMethodCallConv() { +CallingConv ASTContext::getDefaultCXXMethodCallConv(bool isVariadic) { // Pass through to the C++ ABI object - return ABI->getDefaultMethodCallConv(); + return ABI->getDefaultMethodCallConv(isVariadic); +} + +CallingConv ASTContext::getCanonicalCallConv(CallingConv CC) const { + if (CC == CC_C && !LangOpts.MRTD && getTargetInfo().getCXXABI() != CXXABI_Microsoft) + return CC_Default; + return CC; } bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const { diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index ca4fe268522f..35fcd41f26e5 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -14,7 +14,11 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -225,6 +229,11 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, return S; } +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S); + void clang::FormatASTNodeDiagnosticArgument( DiagnosticsEngine::ArgumentKind Kind, intptr_t Val, @@ -244,6 +253,33 @@ void clang::FormatASTNodeDiagnosticArgument( switch (Kind) { default: llvm_unreachable("unknown ArgumentKind"); + case DiagnosticsEngine::ak_qualtype_pair: { + TemplateDiffTypes &TDT = *reinterpret_cast<TemplateDiffTypes*>(Val); + QualType FromType = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.FromType)); + QualType ToType = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.ToType)); + + if (FormatTemplateTypeDiff(Context, FromType, ToType, TDT.PrintTree, + TDT.PrintFromType, TDT.ElideType, + TDT.ShowColors, S)) { + NeedQuotes = !TDT.PrintTree; + TDT.TemplateDiffUsed = true; + break; + } + + // Don't fall-back during tree printing. The caller will handle + // this case. + if (TDT.PrintTree) + return; + + // Attempting to do a templete diff on non-templates. Set the variables + // and continue with regular type printing of the appropriate type. + Val = TDT.PrintFromType ? TDT.FromType : TDT.ToType; + ModLen = 0; + ArgLen = 0; + // Fall through + } case DiagnosticsEngine::ak_qualtype: { assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for QualType argument"); @@ -329,3 +365,901 @@ void clang::FormatASTNodeDiagnosticArgument( if (NeedQuotes) Output.push_back('\''); } + +/// TemplateDiff - A class that constructs a pretty string for a pair of +/// QualTypes. For the pair of types, a diff tree will be created containing +/// all the information about the templates and template arguments. Afterwards, +/// the tree is transformed to a string according to the options passed in. +namespace { +class TemplateDiff { + /// Context - The ASTContext which is used for comparing template arguments. + ASTContext &Context; + + /// Policy - Used during expression printing. + PrintingPolicy Policy; + + /// ElideType - Option to elide identical types. + bool ElideType; + + /// PrintTree - Format output string as a tree. + bool PrintTree; + + /// ShowColor - Diagnostics support color, so bolding will be used. + bool ShowColor; + + /// FromType - When single type printing is selected, this is the type to be + /// be printed. When tree printing is selected, this type will show up first + /// in the tree. + QualType FromType; + + /// ToType - The type that FromType is compared to. Only in tree printing + /// will this type be outputed. + QualType ToType; + + /// Str - Storage for the output stream. + llvm::SmallString<128> Str; + + /// OS - The stream used to construct the output strings. + llvm::raw_svector_ostream OS; + + /// IsBold - Keeps track of the bold formatting for the output string. + bool IsBold; + + /// DiffTree - A tree representation the differences between two types. + class DiffTree { + /// DiffNode - The root node stores the original type. Each child node + /// stores template arguments of their parents. For templated types, the + /// template decl is also stored. + struct DiffNode { + /// NextNode - The index of the next sibling node or 0. + unsigned NextNode; + + /// ChildNode - The index of the first child node or 0. + unsigned ChildNode; + + /// ParentNode - The index of the parent node. + unsigned ParentNode; + + /// FromType, ToType - The type arguments. + QualType FromType, ToType; + + /// FromExpr, ToExpr - The expression arguments. + Expr *FromExpr, *ToExpr; + + /// FromTD, ToTD - The template decl for template template + /// arguments or the type arguments that are templates. + TemplateDecl *FromTD, *ToTD; + + /// FromDefault, ToDefault - Whether the argument is a default argument. + bool FromDefault, ToDefault; + + /// Same - Whether the two arguments evaluate to the same value. + bool Same; + + DiffNode(unsigned ParentNode = 0) + : NextNode(0), ChildNode(0), ParentNode(ParentNode), + FromType(), ToType(), FromExpr(0), ToExpr(0), FromTD(0), ToTD(0), + FromDefault(false), ToDefault(false), Same(false) { } + }; + + /// FlatTree - A flattened tree used to store the DiffNodes. + llvm::SmallVector<DiffNode, 16> FlatTree; + + /// CurrentNode - The index of the current node being used. + unsigned CurrentNode; + + /// NextFreeNode - The index of the next unused node. Used when creating + /// child nodes. + unsigned NextFreeNode; + + /// ReadNode - The index of the current node being read. + unsigned ReadNode; + + public: + DiffTree() : + CurrentNode(0), NextFreeNode(1) { + FlatTree.push_back(DiffNode()); + } + + // Node writing functions. + /// SetNode - Sets FromTD and ToTD of the current node. + void SetNode(TemplateDecl *FromTD, TemplateDecl *ToTD) { + FlatTree[CurrentNode].FromTD = FromTD; + FlatTree[CurrentNode].ToTD = ToTD; + } + + /// SetNode - Sets FromType and ToType of the current node. + void SetNode(QualType FromType, QualType ToType) { + FlatTree[CurrentNode].FromType = FromType; + FlatTree[CurrentNode].ToType = ToType; + } + + /// SetNode - Set FromExpr and ToExpr of the current node. + void SetNode(Expr *FromExpr, Expr *ToExpr) { + FlatTree[CurrentNode].FromExpr = FromExpr; + FlatTree[CurrentNode].ToExpr = ToExpr; + } + + /// SetSame - Sets the same flag of the current node. + void SetSame(bool Same) { + FlatTree[CurrentNode].Same = Same; + } + + /// SetDefault - Sets FromDefault and ToDefault flags of the current node. + void SetDefault(bool FromDefault, bool ToDefault) { + FlatTree[CurrentNode].FromDefault = FromDefault; + FlatTree[CurrentNode].ToDefault = ToDefault; + } + + /// Up - Changes the node to the parent of the current node. + void Up() { + CurrentNode = FlatTree[CurrentNode].ParentNode; + } + + /// AddNode - Adds a child node to the current node, then sets that node + /// node as the current node. + void AddNode() { + FlatTree.push_back(DiffNode(CurrentNode)); + DiffNode &Node = FlatTree[CurrentNode]; + if (Node.ChildNode == 0) { + // If a child node doesn't exist, add one. + Node.ChildNode = NextFreeNode; + } else { + // If a child node exists, find the last child node and add a + // next node to it. + unsigned i; + for (i = Node.ChildNode; FlatTree[i].NextNode != 0; + i = FlatTree[i].NextNode) { + } + FlatTree[i].NextNode = NextFreeNode; + } + CurrentNode = NextFreeNode; + ++NextFreeNode; + } + + // Node reading functions. + /// StartTraverse - Prepares the tree for recursive traversal. + void StartTraverse() { + ReadNode = 0; + CurrentNode = NextFreeNode; + NextFreeNode = 0; + } + + /// Parent - Move the current read node to its parent. + void Parent() { + ReadNode = FlatTree[ReadNode].ParentNode; + } + + /// NodeIsTemplate - Returns true if a template decl is set, and types are + /// set. + bool NodeIsTemplate() { + return (FlatTree[ReadNode].FromTD && + !FlatTree[ReadNode].ToType.isNull()) || + (FlatTree[ReadNode].ToTD && !FlatTree[ReadNode].ToType.isNull()); + } + + /// NodeIsQualType - Returns true if a Qualtype is set. + bool NodeIsQualType() { + return !FlatTree[ReadNode].FromType.isNull() || + !FlatTree[ReadNode].ToType.isNull(); + } + + /// NodeIsExpr - Returns true if an expr is set. + bool NodeIsExpr() { + return FlatTree[ReadNode].FromExpr || FlatTree[ReadNode].ToExpr; + } + + /// NodeIsTemplateTemplate - Returns true if the argument is a template + /// template type. + bool NodeIsTemplateTemplate() { + return FlatTree[ReadNode].FromType.isNull() && + FlatTree[ReadNode].ToType.isNull() && + (FlatTree[ReadNode].FromTD || FlatTree[ReadNode].ToTD); + } + + /// GetNode - Gets the FromType and ToType. + void GetNode(QualType &FromType, QualType &ToType) { + FromType = FlatTree[ReadNode].FromType; + ToType = FlatTree[ReadNode].ToType; + } + + /// GetNode - Gets the FromExpr and ToExpr. + void GetNode(Expr *&FromExpr, Expr *&ToExpr) { + FromExpr = FlatTree[ReadNode].FromExpr; + ToExpr = FlatTree[ReadNode].ToExpr; + } + + /// GetNode - Gets the FromTD and ToTD. + void GetNode(TemplateDecl *&FromTD, TemplateDecl *&ToTD) { + FromTD = FlatTree[ReadNode].FromTD; + ToTD = FlatTree[ReadNode].ToTD; + } + + /// NodeIsSame - Returns true the arguments are the same. + bool NodeIsSame() { + return FlatTree[ReadNode].Same; + } + + /// HasChildrend - Returns true if the node has children. + bool HasChildren() { + return FlatTree[ReadNode].ChildNode != 0; + } + + /// MoveToChild - Moves from the current node to its child. + void MoveToChild() { + ReadNode = FlatTree[ReadNode].ChildNode; + } + + /// AdvanceSibling - If there is a next sibling, advance to it and return + /// true. Otherwise, return false. + bool AdvanceSibling() { + if (FlatTree[ReadNode].NextNode == 0) + return false; + + ReadNode = FlatTree[ReadNode].NextNode; + return true; + } + + /// HasNextSibling - Return true if the node has a next sibling. + bool HasNextSibling() { + return FlatTree[ReadNode].NextNode != 0; + } + + /// FromDefault - Return true if the from argument is the default. + bool FromDefault() { + return FlatTree[ReadNode].FromDefault; + } + + /// ToDefault - Return true if the to argument is the default. + bool ToDefault() { + return FlatTree[ReadNode].ToDefault; + } + + /// Empty - Returns true if the tree has no information. + bool Empty() { + return !FlatTree[0].FromTD && !FlatTree[0].ToTD && + !FlatTree[0].FromExpr && !FlatTree[0].ToExpr && + FlatTree[0].FromType.isNull() && FlatTree[0].ToType.isNull(); + } + }; + + DiffTree Tree; + + /// TSTiterator - an iterator that is used to enter a + /// TemplateSpecializationType and read TemplateArguments inside template + /// parameter packs in order with the rest of the TemplateArguments. + struct TSTiterator { + typedef const TemplateArgument& reference; + typedef const TemplateArgument* pointer; + + /// TST - the template specialization whose arguments this iterator + /// traverse over. + const TemplateSpecializationType *TST; + + /// Index - the index of the template argument in TST. + unsigned Index; + + /// CurrentTA - if CurrentTA is not the same as EndTA, then CurrentTA + /// points to a TemplateArgument within a parameter pack. + TemplateArgument::pack_iterator CurrentTA; + + /// EndTA - the end iterator of a parameter pack + TemplateArgument::pack_iterator EndTA; + + /// TSTiterator - Constructs an iterator and sets it to the first template + /// argument. + TSTiterator(const TemplateSpecializationType *TST) + : TST(TST), Index(0), CurrentTA(0), EndTA(0) { + if (isEnd()) return; + + // Set to first template argument. If not a parameter pack, done. + TemplateArgument TA = TST->getArg(0); + if (TA.getKind() != TemplateArgument::Pack) return; + + // Start looking into the parameter pack. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // Found a valid template argument. + if (CurrentTA != EndTA) return; + + // Parameter pack is empty, use the increment to get to a valid + // template argument. + ++(*this); + } + + /// isEnd - Returns true if the iterator is one past the end. + bool isEnd() const { + return Index == TST->getNumArgs(); + } + + /// &operator++ - Increment the iterator to the next template argument. + TSTiterator &operator++() { + assert(!isEnd() && "Iterator incremented past end of arguments."); + + // If in a parameter pack, advance in the parameter pack. + if (CurrentTA != EndTA) { + ++CurrentTA; + if (CurrentTA != EndTA) + return *this; + } + + // Loop until a template argument is found, or the end is reached. + while (true) { + // Advance to the next template argument. Break if reached the end. + if (++Index == TST->getNumArgs()) break; + + // If the TemplateArgument is not a parameter pack, done. + TemplateArgument TA = TST->getArg(Index); + if (TA.getKind() != TemplateArgument::Pack) break; + + // Handle parameter packs. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // If the parameter pack is empty, try to advance again. + if (CurrentTA != EndTA) break; + } + return *this; + } + + /// operator* - Returns the appropriate TemplateArgument. + reference operator*() const { + assert(!isEnd() && "Index exceeds number of arguments."); + if (CurrentTA == EndTA) + return TST->getArg(Index); + else + return *CurrentTA; + } + + /// operator-> - Allow access to the underlying TemplateArgument. + pointer operator->() const { + return &operator*(); + } + }; + + // These functions build up the template diff tree, including functions to + // retrieve and compare template arguments. + + static const TemplateSpecializationType * GetTemplateSpecializationType( + ASTContext &Context, QualType Ty) { + if (const TemplateSpecializationType *TST = + Ty->getAs<TemplateSpecializationType>()) + return TST; + + const RecordType *RT = Ty->getAs<RecordType>(); + + if (!RT) + return 0; + + const ClassTemplateSpecializationDecl *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()); + + if (!CTSD) + return 0; + + Ty = Context.getTemplateSpecializationType( + TemplateName(CTSD->getSpecializedTemplate()), + CTSD->getTemplateArgs().data(), + CTSD->getTemplateArgs().size(), + Ty.getCanonicalType()); + + return Ty->getAs<TemplateSpecializationType>(); + } + + /// DiffTemplate - recursively visits template arguments and stores the + /// argument info into a tree. + void DiffTemplate(const TemplateSpecializationType *FromTST, + const TemplateSpecializationType *ToTST) { + // Begin descent into diffing template tree. + TemplateParameterList *Params = + FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); + unsigned TotalArgs = 0; + for (TSTiterator FromIter(FromTST), ToIter(ToTST); + !FromIter.isEnd() || !ToIter.isEnd(); ++TotalArgs) { + Tree.AddNode(); + + // Get the parameter at index TotalArgs. If index is larger + // than the total number of parameters, then there is an + // argument pack, so re-use the last parameter. + NamedDecl *ParamND = Params->getParam( + (TotalArgs < Params->size()) ? TotalArgs + : Params->size() - 1); + // Handle Types + if (TemplateTypeParmDecl *DefaultTTPD = + dyn_cast<TemplateTypeParmDecl>(ParamND)) { + QualType FromType, ToType; + GetType(FromIter, DefaultTTPD, FromType); + GetType(ToIter, DefaultTTPD, ToType); + Tree.SetNode(FromType, ToType); + Tree.SetDefault(FromIter.isEnd() && !FromType.isNull(), + ToIter.isEnd() && !ToType.isNull()); + if (!FromType.isNull() && !ToType.isNull()) { + if (Context.hasSameType(FromType, ToType)) { + Tree.SetSame(true); + } else { + const TemplateSpecializationType *FromArgTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToArgTST = + GetTemplateSpecializationType(Context, ToType); + + if (FromArgTST && ToArgTST) { + bool SameTemplate = hasSameTemplate(FromArgTST, ToArgTST); + if (SameTemplate) { + Tree.SetNode(FromArgTST->getTemplateName().getAsTemplateDecl(), + ToArgTST->getTemplateName().getAsTemplateDecl()); + DiffTemplate(FromArgTST, ToArgTST); + } + } + } + } + } + + // Handle Expressions + if (NonTypeTemplateParmDecl *DefaultNTTPD = + dyn_cast<NonTypeTemplateParmDecl>(ParamND)) { + Expr *FromExpr, *ToExpr; + GetExpr(FromIter, DefaultNTTPD, FromExpr); + GetExpr(ToIter, DefaultNTTPD, ToExpr); + Tree.SetNode(FromExpr, ToExpr); + Tree.SetSame(IsEqualExpr(Context, FromExpr, ToExpr)); + Tree.SetDefault(FromIter.isEnd() && FromExpr, + ToIter.isEnd() && ToExpr); + } + + // Handle Templates + if (TemplateTemplateParmDecl *DefaultTTPD = + dyn_cast<TemplateTemplateParmDecl>(ParamND)) { + TemplateDecl *FromDecl, *ToDecl; + GetTemplateDecl(FromIter, DefaultTTPD, FromDecl); + GetTemplateDecl(ToIter, DefaultTTPD, ToDecl); + Tree.SetNode(FromDecl, ToDecl); + Tree.SetSame(FromDecl && ToDecl && + FromDecl->getIdentifier() == ToDecl->getIdentifier()); + } + + if (!FromIter.isEnd()) ++FromIter; + if (!ToIter.isEnd()) ++ToIter; + Tree.Up(); + } + } + + /// hasSameTemplate - Returns true if both types are specialized from the + /// same template declaration. If they come from different template aliases, + /// do a parallel ascension search to determine the highest template alias in + /// common and set the arguments to them. + static bool hasSameTemplate(const TemplateSpecializationType *&FromTST, + const TemplateSpecializationType *&ToTST) { + // Check the top templates if they are the same. + if (FromTST->getTemplateName().getAsTemplateDecl()->getIdentifier() == + ToTST->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return true; + + // Create vectors of template aliases. + SmallVector<const TemplateSpecializationType*, 1> FromTemplateList, + ToTemplateList; + + const TemplateSpecializationType *TempToTST = ToTST, *TempFromTST = FromTST; + FromTemplateList.push_back(FromTST); + ToTemplateList.push_back(ToTST); + + // Dump every template alias into the vectors. + while (TempFromTST->isTypeAlias()) { + TempFromTST = + TempFromTST->getAliasedType()->getAs<TemplateSpecializationType>(); + if (!TempFromTST) + break; + FromTemplateList.push_back(TempFromTST); + } + while (TempToTST->isTypeAlias()) { + TempToTST = + TempToTST->getAliasedType()->getAs<TemplateSpecializationType>(); + if (!TempToTST) + break; + ToTemplateList.push_back(TempToTST); + } + + SmallVector<const TemplateSpecializationType*, 1>::reverse_iterator + FromIter = FromTemplateList.rbegin(), FromEnd = FromTemplateList.rend(), + ToIter = ToTemplateList.rbegin(), ToEnd = ToTemplateList.rend(); + + // Check if the lowest template types are the same. If not, return. + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return false; + + // Begin searching up the template aliases. The bottom most template + // matches so move up until one pair does not match. Use the template + // right before that one. + for (; FromIter != FromEnd && ToIter != ToEnd; ++FromIter, ++ToIter) { + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + break; + } + + FromTST = FromIter[-1]; + ToTST = ToIter[-1]; + + return true; + } + + /// GetType - Retrieves the template type arguments, including default + /// arguments. + void GetType(const TSTiterator &Iter, TemplateTypeParmDecl *DefaultTTPD, + QualType &ArgType) { + ArgType = QualType(); + bool isVariadic = DefaultTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgType = Iter->getAsType(); + else if (!isVariadic) + ArgType = DefaultTTPD->getDefaultArgument(); + } + + /// GetExpr - Retrieves the template expression argument, including default + /// arguments. + void GetExpr(const TSTiterator &Iter, NonTypeTemplateParmDecl *DefaultNTTPD, + Expr *&ArgExpr) { + ArgExpr = 0; + bool isVariadic = DefaultNTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgExpr = Iter->getAsExpr(); + else if (!isVariadic) + ArgExpr = DefaultNTTPD->getDefaultArgument(); + + if (ArgExpr) + while (SubstNonTypeTemplateParmExpr *SNTTPE = + dyn_cast<SubstNonTypeTemplateParmExpr>(ArgExpr)) + ArgExpr = SNTTPE->getReplacement(); + } + + /// GetTemplateDecl - Retrieves the template template arguments, including + /// default arguments. + void GetTemplateDecl(const TSTiterator &Iter, + TemplateTemplateParmDecl *DefaultTTPD, + TemplateDecl *&ArgDecl) { + ArgDecl = 0; + bool isVariadic = DefaultTTPD->isParameterPack(); + + TemplateArgument TA = DefaultTTPD->getDefaultArgument().getArgument(); + TemplateDecl *DefaultTD = TA.getAsTemplate().getAsTemplateDecl(); + + if (!Iter.isEnd()) + ArgDecl = Iter->getAsTemplate().getAsTemplateDecl(); + else if (!isVariadic) + ArgDecl = DefaultTD; + } + + /// IsEqualExpr - Returns true if the expressions evaluate to the same value. + static bool IsEqualExpr(ASTContext &Context, Expr *FromExpr, Expr *ToExpr) { + if (FromExpr == ToExpr) + return true; + + if (!FromExpr || !ToExpr) + return false; + + FromExpr = FromExpr->IgnoreParens(); + ToExpr = ToExpr->IgnoreParens(); + + DeclRefExpr *FromDRE = dyn_cast<DeclRefExpr>(FromExpr), + *ToDRE = dyn_cast<DeclRefExpr>(ToExpr); + + if (FromDRE || ToDRE) { + if (!FromDRE || !ToDRE) + return false; + return FromDRE->getDecl() == ToDRE->getDecl(); + } + + Expr::EvalResult FromResult, ToResult; + if (!FromExpr->EvaluateAsRValue(FromResult, Context) || + !ToExpr->EvaluateAsRValue(ToResult, Context)) + assert(0 && "Template arguments must be known at compile time."); + + APValue &FromVal = FromResult.Val; + APValue &ToVal = ToResult.Val; + + if (FromVal.getKind() != ToVal.getKind()) return false; + + switch (FromVal.getKind()) { + case APValue::Int: + return FromVal.getInt() == ToVal.getInt(); + case APValue::LValue: { + APValue::LValueBase FromBase = FromVal.getLValueBase(); + APValue::LValueBase ToBase = ToVal.getLValueBase(); + if (FromBase.isNull() && ToBase.isNull()) + return true; + if (FromBase.isNull() || ToBase.isNull()) + return false; + return FromBase.get<const ValueDecl*>() == + ToBase.get<const ValueDecl*>(); + } + case APValue::MemberPointer: + return FromVal.getMemberPointerDecl() == ToVal.getMemberPointerDecl(); + default: + llvm_unreachable("Unknown template argument expression."); + } + } + + // These functions converts the tree representation of the template + // differences into the internal character vector. + + /// TreeToString - Converts the Tree object into a character stream which + /// will later be turned into the output string. + void TreeToString(int Indent = 1) { + if (PrintTree) { + OS << '\n'; + for (int i = 0; i < Indent; ++i) + OS << " "; + ++Indent; + } + + // Handle cases where the difference is not templates with different + // arguments. + if (!Tree.NodeIsTemplate()) { + if (Tree.NodeIsQualType()) { + QualType FromType, ToType; + Tree.GetNode(FromType, ToType); + PrintTypeNames(FromType, ToType, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsExpr()) { + Expr *FromExpr, *ToExpr; + Tree.GetNode(FromExpr, ToExpr); + PrintExpr(FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsTemplateTemplate()) { + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(), + Tree.ToDefault(), Tree.NodeIsSame()); + return; + } + llvm_unreachable("Unable to deduce template difference."); + } + + // Node is root of template. Recurse on children. + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + + assert(Tree.HasChildren() && "Template difference not found in diff tree."); + + OS << FromTD->getNameAsString() << '<'; + Tree.MoveToChild(); + unsigned NumElideArgs = 0; + do { + if (ElideType) { + if (Tree.NodeIsSame()) { + ++NumElideArgs; + continue; + } + if (NumElideArgs > 0) { + PrintElideArgs(NumElideArgs, Indent); + NumElideArgs = 0; + OS << ", "; + } + } + TreeToString(Indent); + if (Tree.HasNextSibling()) + OS << ", "; + } while (Tree.AdvanceSibling()); + if (NumElideArgs > 0) + PrintElideArgs(NumElideArgs, Indent); + + Tree.Parent(); + OS << ">"; + } + + // To signal to the text printer that a certain text needs to be bolded, + // a special character is injected into the character stream which the + // text printer will later strip out. + + /// Bold - Start bolding text. + void Bold() { + assert(!IsBold && "Attempting to bold text that is already bold."); + IsBold = true; + if (ShowColor) + OS << ToggleHighlight; + } + + /// Unbold - Stop bolding text. + void Unbold() { + assert(IsBold && "Attempting to remove bold from unbold text."); + IsBold = false; + if (ShowColor) + OS << ToggleHighlight; + } + + // Functions to print out the arguments and highlighting the difference. + + /// PrintTypeNames - prints the typenames, bolding differences. Will detect + /// typenames that are the same and attempt to disambiguate them by using + /// canonical typenames. + void PrintTypeNames(QualType FromType, QualType ToType, + bool FromDefault, bool ToDefault, bool Same) { + assert((!FromType.isNull() || !ToType.isNull()) && + "Only one template argument may be missing."); + + if (Same) { + OS << FromType.getAsString(); + return; + } + + std::string FromTypeStr = FromType.isNull() ? "(no argument)" + : FromType.getAsString(); + std::string ToTypeStr = ToType.isNull() ? "(no argument)" + : ToType.getAsString(); + // Switch to canonical typename if it is better. + // TODO: merge this with other aka printing above. + if (FromTypeStr == ToTypeStr) { + std::string FromCanTypeStr = FromType.getCanonicalType().getAsString(); + std::string ToCanTypeStr = ToType.getCanonicalType().getAsString(); + if (FromCanTypeStr != ToCanTypeStr) { + FromTypeStr = FromCanTypeStr; + ToTypeStr = ToCanTypeStr; + } + } + + if (PrintTree) OS << '['; + OS << (FromDefault ? "(default) " : ""); + Bold(); + OS << FromTypeStr; + Unbold(); + if (PrintTree) { + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + OS << ToTypeStr; + Unbold(); + OS << "]"; + } + return; + } + + /// PrintExpr - Prints out the expr template arguments, highlighting argument + /// differences. + void PrintExpr(const Expr *FromExpr, const Expr *ToExpr, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromExpr || ToExpr) && + "Only one template argument may be missing."); + if (Same) { + PrintExpr(FromExpr); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) " : ""); + Bold(); + PrintExpr(FromExpr); + Unbold(); + } else { + OS << (FromDefault ? "[(default) " : "["); + Bold(); + PrintExpr(FromExpr); + Unbold(); + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + PrintExpr(ToExpr); + Unbold(); + OS << ']'; + } + } + + /// PrintExpr - Actual formatting and printing of expressions. + void PrintExpr(const Expr *E) { + if (!E) + OS << "(no argument)"; + else + E->printPretty(OS, Context, 0, Policy); return; + } + + /// PrintTemplateTemplate - Handles printing of template template arguments, + /// highlighting argument differences. + void PrintTemplateTemplate(TemplateDecl *FromTD, TemplateDecl *ToTD, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromTD || ToTD) && "Only one template argument may be missing."); + if (Same) { + OS << "template " << FromTD->getNameAsString(); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) template " : "template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + } else { + OS << (FromDefault ? "[(default) template " : "[template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << " != " << (ToDefault ? "(default) template " : "template "); + Bold(); + OS << (ToTD ? ToTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << ']'; + } + } + + // Prints the appropriate placeholder for elided template arguments. + void PrintElideArgs(unsigned NumElideArgs, unsigned Indent) { + if (PrintTree) { + OS << '\n'; + for (unsigned i = 0; i < Indent; ++i) + OS << " "; + } + if (NumElideArgs == 0) return; + if (NumElideArgs == 1) + OS << "[...]"; + else + OS << "[" << NumElideArgs << " * ...]"; + } + +public: + + TemplateDiff(ASTContext &Context, QualType FromType, QualType ToType, + bool PrintTree, bool PrintFromType, bool ElideType, + bool ShowColor) + : Context(Context), + Policy(Context.getLangOpts()), + ElideType(ElideType), + PrintTree(PrintTree), + ShowColor(ShowColor), + // When printing a single type, the FromType is the one printed. + FromType(PrintFromType ? FromType : ToType), + ToType(PrintFromType ? ToType : FromType), + OS(Str), + IsBold(false) { + } + + /// DiffTemplate - Start the template type diffing. + void DiffTemplate() { + const TemplateSpecializationType *FromOrigTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToOrigTST = + GetTemplateSpecializationType(Context, ToType); + + // Only checking templates. + if (!FromOrigTST || !ToOrigTST) + return; + + // Different base templates. + if (!hasSameTemplate(FromOrigTST, ToOrigTST)) { + return; + } + + Tree.SetNode(FromType, ToType); + + // Same base template, but different arguments. + Tree.SetNode(FromOrigTST->getTemplateName().getAsTemplateDecl(), + ToOrigTST->getTemplateName().getAsTemplateDecl()); + + DiffTemplate(FromOrigTST, ToOrigTST); + } + + /// MakeString - When the two types given are templated types with the same + /// base template, a string representation of the type difference will be + /// loaded into S and return true. Otherwise, return false. + bool MakeString(std::string &S) { + Tree.StartTraverse(); + if (Tree.Empty()) + return false; + + TreeToString(); + assert(!IsBold && "Bold is applied to end of string."); + S = OS.str(); + return true; + } +}; // end class TemplateDiff +} // end namespace + +/// FormatTemplateTypeDiff - A helper static function to start the template +/// diff and return the properly formatted string. Returns true if the diff +/// is successful. +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S) { + if (PrintTree) + PrintFromType = true; + TemplateDiff TD(Context, FromType, ToType, PrintTree, PrintFromType, + ElideType, ShowColors); + TD.DiffTemplate(); + return TD.MakeString(S); +} diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 3879907ec6da..3e952acdcb42 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -119,7 +119,8 @@ namespace clang { bool ImportTemplateArguments(const TemplateArgument *FromArgs, unsigned NumFromArgs, SmallVectorImpl<TemplateArgument> &ToArgs); - bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord); + bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord, + bool Complain = true); bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord); bool IsStructuralMatch(ClassTemplateDecl *From, ClassTemplateDecl *To); Decl *VisitDecl(Decl *D); @@ -201,12 +202,16 @@ namespace { /// \brief Whether we're being strict about the spelling of types when /// unifying two types. bool StrictTypeSpelling; - + + /// \brief Whether to complain about failures. + bool Complain; + StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2, llvm::DenseSet<std::pair<Decl *, Decl *> > &NonEquivalentDecls, - bool StrictTypeSpelling = false) + bool StrictTypeSpelling = false, + bool Complain = true) : C1(C1), C2(C2), NonEquivalentDecls(NonEquivalentDecls), - StrictTypeSpelling(StrictTypeSpelling) { } + StrictTypeSpelling(StrictTypeSpelling), Complain(Complain) { } /// \brief Determine whether the two declarations are structurally /// equivalent. @@ -223,10 +228,16 @@ namespace { public: DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID) { + if (!Complain) + return DiagnosticBuilder::getEmpty(); + return C1.getDiagnostics().Report(Loc, DiagID); } DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID) { + if (!Complain) + return DiagnosticBuilder::getEmpty(); + return C2.getDiagnostics().Report(Loc, DiagID); } }; @@ -237,45 +248,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1, Decl *D2); -/// \brief Determine if two APInts have the same value, after zero-extending -/// one of them (if needed!) to ensure that the bit-widths match. -static bool IsSameValue(const llvm::APInt &I1, const llvm::APInt &I2) { - if (I1.getBitWidth() == I2.getBitWidth()) - return I1 == I2; - - if (I1.getBitWidth() > I2.getBitWidth()) - return I1 == I2.zext(I1.getBitWidth()); - - return I1.zext(I2.getBitWidth()) == I2; -} - -/// \brief Determine if two APSInts have the same value, zero- or sign-extending -/// as needed. -static bool IsSameValue(const llvm::APSInt &I1, const llvm::APSInt &I2) { - if (I1.getBitWidth() == I2.getBitWidth() && I1.isSigned() == I2.isSigned()) - return I1 == I2; - - // Check for a bit-width mismatch. - if (I1.getBitWidth() > I2.getBitWidth()) - return IsSameValue(I1, I2.extend(I1.getBitWidth())); - else if (I2.getBitWidth() > I1.getBitWidth()) - return IsSameValue(I1.extend(I2.getBitWidth()), I2); - - // We have a signedness mismatch. Turn the signed value into an unsigned - // value. - if (I1.isSigned()) { - if (I1.isNegative()) - return false; - - return llvm::APSInt(I1, true) == I2; - } - - if (I2.isNegative()) - return false; - - return I1 == llvm::APSInt(I2, true); -} - /// \brief Determine structural equivalence of two expressions. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Expr *E1, Expr *E2) { @@ -322,7 +294,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Arg2.getIntegralType())) return false; - return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral()); + return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), Arg2.getAsIntegral()); case TemplateArgument::Declaration: if (!Arg1.getAsDecl() || !Arg2.getAsDecl()) @@ -467,7 +439,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case Type::ConstantArray: { const ConstantArrayType *Array1 = cast<ConstantArrayType>(T1); const ConstantArrayType *Array2 = cast<ConstantArrayType>(T2); - if (!IsSameValue(Array1->getSize(), Array2->getSize())) + if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize())) return false; if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) @@ -1002,9 +974,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, } // Check the fields for consistency. - CXXRecordDecl::field_iterator Field2 = D2->field_begin(), + RecordDecl::field_iterator Field2 = D2->field_begin(), Field2End = D2->field_end(); - for (CXXRecordDecl::field_iterator Field1 = D1->field_begin(), + for (RecordDecl::field_iterator Field1 = D1->field_begin(), Field1End = D1->field_end(); Field1 != Field1End; ++Field1, ++Field2) { @@ -1053,7 +1025,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, llvm::APSInt Val1 = EC1->getInitVal(); llvm::APSInt Val2 = EC2->getInitVal(); - if (!IsSameValue(Val1, Val2) || + if (!llvm::APSInt::isSameValue(Val1, Val2) || !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) << Context.C2.getTypeDeclType(D2); @@ -1852,19 +1824,14 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.HasPublicFields = FromData.HasPublicFields; ToData.HasMutableFields = FromData.HasMutableFields; ToData.HasOnlyCMembers = FromData.HasOnlyCMembers; + ToData.HasInClassInitializer = FromData.HasInClassInitializer; ToData.HasTrivialDefaultConstructor = FromData.HasTrivialDefaultConstructor; ToData.HasConstexprNonCopyMoveConstructor = FromData.HasConstexprNonCopyMoveConstructor; ToData.DefaultedDefaultConstructorIsConstexpr = FromData.DefaultedDefaultConstructorIsConstexpr; - ToData.DefaultedCopyConstructorIsConstexpr - = FromData.DefaultedCopyConstructorIsConstexpr; - ToData.DefaultedMoveConstructorIsConstexpr - = FromData.DefaultedMoveConstructorIsConstexpr; ToData.HasConstexprDefaultConstructor = FromData.HasConstexprDefaultConstructor; - ToData.HasConstexprCopyConstructor = FromData.HasConstexprCopyConstructor; - ToData.HasConstexprMoveConstructor = FromData.HasConstexprMoveConstructor; ToData.HasTrivialCopyConstructor = FromData.HasTrivialCopyConstructor; ToData.HasTrivialMoveConstructor = FromData.HasTrivialMoveConstructor; ToData.HasTrivialCopyAssignment = FromData.HasTrivialCopyAssignment; @@ -1991,7 +1958,7 @@ ASTNodeImporter::ImportTemplateArgument(const TemplateArgument &From) { QualType ToType = Importer.Import(From.getIntegralType()); if (ToType.isNull()) return TemplateArgument(); - return TemplateArgument(*From.getAsIntegral(), ToType); + return TemplateArgument(From, ToType); } case TemplateArgument::Declaration: @@ -2052,10 +2019,11 @@ bool ASTNodeImporter::ImportTemplateArguments(const TemplateArgument *FromArgs, } bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, - RecordDecl *ToRecord) { + RecordDecl *ToRecord, bool Complain) { StructuralEquivalenceContext Ctx(Importer.getFromContext(), Importer.getToContext(), - Importer.getNonEquivalentDecls()); + Importer.getNonEquivalentDecls(), + false, Complain); return Ctx.IsStructurallyEquivalent(FromRecord, ToRecord); } @@ -2335,7 +2303,7 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { // We may already have a record of the same name; try to find and match it. RecordDecl *AdoptDecl = 0; - if (!DC->isFunctionOrMethod() && SearchName) { + if (!DC->isFunctionOrMethod()) { SmallVector<NamedDecl *, 4> ConflictingDecls; llvm::SmallVector<NamedDecl *, 2> FoundDecls; DC->localUncachedLookup(SearchName, FoundDecls); @@ -2351,25 +2319,31 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { if (RecordDecl *FoundRecord = dyn_cast<RecordDecl>(Found)) { if (RecordDecl *FoundDef = FoundRecord->getDefinition()) { - if (!D->isCompleteDefinition() || IsStructuralMatch(D, FoundDef)) { + if ((SearchName && !D->isCompleteDefinition()) + || (D->isCompleteDefinition() && + D->isAnonymousStructOrUnion() + == FoundDef->isAnonymousStructOrUnion() && + IsStructuralMatch(D, FoundDef))) { // The record types structurally match, or the "from" translation // unit only had a forward declaration anyway; call it the same // function. // FIXME: For C++, we should also merge methods here. return Importer.Imported(D, FoundDef); } - } else { + } else if (!D->isCompleteDefinition()) { // We have a forward declaration of this type, so adopt that forward // declaration rather than building a new one. AdoptDecl = FoundRecord; continue; - } + } else if (!SearchName) { + continue; + } } ConflictingDecls.push_back(FoundDecls[I]); } - if (!ConflictingDecls.empty()) { + if (!ConflictingDecls.empty() && SearchName) { Name = Importer.HandleNameConflict(Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); @@ -2395,6 +2369,8 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { D2->setQualifierInfo(Importer.Import(D->getQualifierLoc())); D2->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(D2); + if (D->isAnonymousStructOrUnion()) + D2->setAnonymousStructOrUnion(true); } Importer.Imported(D, D2); @@ -2661,7 +2637,7 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { Importer.Import(D->getInnerLocStart()), Loc, Name.getAsIdentifierInfo(), T, TInfo, BitWidth, D->isMutable(), - D->hasInClassInitializer()); + D->getInClassInitStyle()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); if (ToField->hasInClassInitializer()) @@ -2686,11 +2662,16 @@ Decl *ASTNodeImporter::VisitIndirectFieldDecl(IndirectFieldDecl *D) { if (IndirectFieldDecl *FoundField = dyn_cast<IndirectFieldDecl>(FoundDecls[I])) { if (Importer.IsStructurallyEquivalent(D->getType(), - FoundField->getType())) { + FoundField->getType(), + Name)) { Importer.Imported(D, FoundField); return FoundField; } - + + // If there are more anonymous fields to check, continue. + if (!Name && I < N-1) + continue; + Importer.ToDiag(Loc, diag::err_odr_field_type_inconsistent) << Name << D->getType() << FoundField->getType(); Importer.ToDiag(FoundField->getLocation(), diag::note_odr_value_here) @@ -4665,12 +4646,14 @@ Decl *ASTImporter::Imported(Decl *From, Decl *To) { return To; } -bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To) { +bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To, + bool Complain) { llvm::DenseMap<const Type *, const Type *>::iterator Pos = ImportedTypes.find(From.getTypePtr()); if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To)) return true; - StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls); + StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls, + false, Complain); return Ctx.IsStructurallyEquivalent(From, To); } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 716459a930cf..bcc96f912e28 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -1,7 +1,5 @@ set(LLVM_LINK_COMPONENTS support) -set(LLVM_USED_LIBS clangBasic clangLex) - add_clang_library(clangAST APValue.cpp ASTConsumer.cpp @@ -10,6 +8,13 @@ add_clang_library(clangAST ASTImporter.cpp AttrImpl.cpp CXXInheritance.cpp + Comment.cpp + CommentBriefParser.cpp + CommentCommandTraits.cpp + CommentDumper.cpp + CommentLexer.cpp + CommentParser.cpp + CommentSema.cpp Decl.cpp DeclarationName.cpp DeclBase.cpp @@ -35,6 +40,7 @@ add_clang_library(clangAST NestedNameSpecifier.cpp NSAPI.cpp ParentMap.cpp + RawCommentList.cpp RecordLayout.cpp RecordLayoutBuilder.cpp SelectorLocationsKind.cpp @@ -53,5 +59,21 @@ add_clang_library(clangAST VTTBuilder.cpp ) -add_dependencies(clangAST ClangARMNeon ClangAttrClasses ClangAttrList - ClangAttrImpl ClangDiagnosticAST ClangDeclNodes ClangStmtNodes) +add_dependencies(clangAST + ClangARMNeon + ClangAttrClasses + ClangAttrList + ClangAttrImpl + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticAST + ClangDiagnosticComment + ClangDiagnosticCommon + ClangDiagnosticSema + ClangStmtNodes + ) + +target_link_libraries(clangAST + clangBasic + clangLex + ) diff --git a/lib/AST/CXXABI.h b/lib/AST/CXXABI.h index 943c43e79198..0d9c869d87ca 100644 --- a/lib/AST/CXXABI.h +++ b/lib/AST/CXXABI.h @@ -32,7 +32,7 @@ public: virtual unsigned getMemberPointerSize(const MemberPointerType *MPT) const = 0; /// Returns the default calling convention for C++ methods. - virtual CallingConv getDefaultMethodCallConv() const = 0; + virtual CallingConv getDefaultMethodCallConv(bool isVariadic) const = 0; // Returns whether the given class is nearly empty, with just virtual pointers // and no data except possibly virtual bases. diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index 218673090aa7..cf3913bac008 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// #include "clang/AST/CXXInheritance.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/DeclCXX.h" #include <algorithm> @@ -96,7 +97,7 @@ bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base, Paths); } -bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const { +bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const { if (!getNumVBases()) return false; @@ -106,8 +107,12 @@ bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const { if (getCanonicalDecl() == Base->getCanonicalDecl()) return false; - Paths.setOrigin(const_cast<CXXRecordDecl*>(this)); - return lookupInBases(&FindVirtualBaseClass, Base->getCanonicalDecl(), Paths); + Paths.setOrigin(const_cast<CXXRecordDecl*>(this)); + + const void *BasePtr = static_cast<const void*>(Base->getCanonicalDecl()); + return lookupInBases(&FindVirtualBaseClass, + const_cast<void *>(BasePtr), + Paths); } static bool BaseIsNot(const CXXRecordDecl *Base, void *OpaqueTarget) { @@ -160,7 +165,7 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches, return AllMatches; } -bool CXXBasePaths::lookupInBases(ASTContext &Context, +bool CXXBasePaths::lookupInBases(ASTContext &Context, const CXXRecordDecl *Record, CXXRecordDecl::BaseMatchesCallback *BaseMatches, void *UserData) { @@ -505,12 +510,17 @@ void FinalOverriderCollector::Collect(const CXXRecordDecl *RD, CXXFinalOverriderMap *BaseOverriders = &ComputedBaseOverriders; if (Base->isVirtual()) { CXXFinalOverriderMap *&MyVirtualOverriders = VirtualOverriders[BaseDecl]; + BaseOverriders = MyVirtualOverriders; if (!MyVirtualOverriders) { MyVirtualOverriders = new CXXFinalOverriderMap; + + // Collect may cause VirtualOverriders to reallocate, invalidating the + // MyVirtualOverriders reference. Set BaseOverriders to the right + // value now. + BaseOverriders = MyVirtualOverriders; + Collect(BaseDecl, true, BaseDecl, *MyVirtualOverriders); } - - BaseOverriders = MyVirtualOverriders; } else Collect(BaseDecl, false, InVirtualSubobject, ComputedBaseOverriders); diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp new file mode 100644 index 000000000000..8a711f0c1f80 --- /dev/null +++ b/lib/AST/Comment.cpp @@ -0,0 +1,264 @@ +//===--- Comment.cpp - Comment AST node implementation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Comment.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace comments { + +const char *Comment::getCommentKindName() const { + switch (getCommentKind()) { + case NoCommentKind: return "NoCommentKind"; +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return #CLASS; +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +void Comment::dump() const { + // It is important that Comment::dump() is defined in a different TU than + // Comment::dump(raw_ostream, SourceManager). If both functions were defined + // in CommentDumper.cpp, that object file would be removed by linker because + // none of its functions are referenced by other object files, despite the + // LLVM_ATTRIBUTE_USED. + dump(llvm::errs(), NULL); +} + +void Comment::dump(SourceManager &SM) const { + dump(llvm::errs(), &SM); +} + +namespace { +struct good {}; +struct bad {}; + +template <typename T> +good implements_child_begin_end(Comment::child_iterator (T::*)() const) { + return good(); +} + +static inline bad implements_child_begin_end( + Comment::child_iterator (Comment::*)() const) { + return bad(); +} + +#define ASSERT_IMPLEMENTS_child_begin(function) \ + (void) sizeof(good(implements_child_begin_end(function))) + +static inline void CheckCommentASTNodes() { +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + ASSERT_IMPLEMENTS_child_begin(&CLASS::child_begin); \ + ASSERT_IMPLEMENTS_child_begin(&CLASS::child_end); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT +} + +#undef ASSERT_IMPLEMENTS_child_begin + +} // end unnamed namespace + +Comment::child_iterator Comment::child_begin() const { + switch (getCommentKind()) { + case NoCommentKind: llvm_unreachable("comment without a kind"); +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return static_cast<const CLASS *>(this)->child_begin(); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +Comment::child_iterator Comment::child_end() const { + switch (getCommentKind()) { + case NoCommentKind: llvm_unreachable("comment without a kind"); +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return static_cast<const CLASS *>(this)->child_end(); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +bool TextComment::isWhitespaceNoCache() const { + for (StringRef::const_iterator I = Text.begin(), E = Text.end(); + I != E; ++I) { + const char C = *I; + if (C != ' ' && C != '\n' && C != '\r' && + C != '\t' && C != '\f' && C != '\v') + return false; + } + return true; +} + +bool ParagraphComment::isWhitespaceNoCache() const { + for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) { + if (const TextComment *TC = dyn_cast<TextComment>(*I)) { + if (!TC->isWhitespace()) + return false; + } else + return false; + } + return true; +} + +const char *ParamCommandComment::getDirectionAsString(PassDirection D) { + switch (D) { + case ParamCommandComment::In: + return "[in]"; + case ParamCommandComment::Out: + return "[out]"; + case ParamCommandComment::InOut: + return "[in,out]"; + } + llvm_unreachable("unknown PassDirection"); +} + +void DeclInfo::fill() { + assert(!IsFilled); + + // Set defaults. + Kind = OtherKind; + TemplateKind = NotTemplate; + IsObjCMethod = false; + IsInstanceMethod = false; + IsClassMethod = false; + ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = NULL; + + if (!ThisDecl) { + // If there is no declaration, the defaults is our only guess. + IsFilled = true; + return; + } + + Decl::Kind K = ThisDecl->getKind(); + switch (K) { + default: + // Defaults are should be good for declarations we don't handle explicitly. + break; + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: { + const FunctionDecl *FD = cast<FunctionDecl>(ThisDecl); + Kind = FunctionKind; + ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), + FD->getNumParams()); + ResultType = FD->getResultType(); + unsigned NumLists = FD->getNumTemplateParameterLists(); + if (NumLists != 0) { + TemplateKind = TemplateSpecialization; + TemplateParameters = + FD->getTemplateParameterList(NumLists - 1); + } + + if (K == Decl::CXXMethod || K == Decl::CXXConstructor || + K == Decl::CXXDestructor || K == Decl::CXXConversion) { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(ThisDecl); + IsInstanceMethod = MD->isInstance(); + IsClassMethod = !IsInstanceMethod; + } + break; + } + case Decl::ObjCMethod: { + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(ThisDecl); + Kind = FunctionKind; + ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(), + MD->param_size()); + ResultType = MD->getResultType(); + IsObjCMethod = true; + IsInstanceMethod = MD->isInstanceMethod(); + IsClassMethod = !IsInstanceMethod; + break; + } + case Decl::FunctionTemplate: { + const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(ThisDecl); + Kind = FunctionKind; + TemplateKind = Template; + const FunctionDecl *FD = FTD->getTemplatedDecl(); + ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), + FD->getNumParams()); + ResultType = FD->getResultType(); + TemplateParameters = FTD->getTemplateParameters(); + break; + } + case Decl::ClassTemplate: { + const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(ThisDecl); + Kind = ClassKind; + TemplateKind = Template; + TemplateParameters = CTD->getTemplateParameters(); + break; + } + case Decl::ClassTemplatePartialSpecialization: { + const ClassTemplatePartialSpecializationDecl *CTPSD = + cast<ClassTemplatePartialSpecializationDecl>(ThisDecl); + Kind = ClassKind; + TemplateKind = TemplatePartialSpecialization; + TemplateParameters = CTPSD->getTemplateParameters(); + break; + } + case Decl::ClassTemplateSpecialization: + Kind = ClassKind; + TemplateKind = TemplateSpecialization; + break; + case Decl::Record: + case Decl::CXXRecord: + Kind = ClassKind; + break; + case Decl::Var: + case Decl::Field: + case Decl::EnumConstant: + case Decl::ObjCIvar: + case Decl::ObjCAtDefsField: + Kind = VariableKind; + break; + case Decl::Namespace: + Kind = NamespaceKind; + break; + case Decl::Typedef: + case Decl::TypeAlias: + Kind = TypedefKind; + break; + case Decl::TypeAliasTemplate: { + const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(ThisDecl); + Kind = TypedefKind; + TemplateKind = Template; + TemplateParameters = TAT->getTemplateParameters(); + break; + } + case Decl::Enum: + Kind = EnumKind; + break; + } + + IsFilled = true; +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp new file mode 100644 index 000000000000..0aebc1e4e3dd --- /dev/null +++ b/lib/AST/CommentBriefParser.cpp @@ -0,0 +1,122 @@ +//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentBriefParser.h" +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +namespace { +/// Convert all whitespace into spaces, remove leading and trailing spaces, +/// compress multiple spaces into one. +void cleanupBrief(std::string &S) { + bool PrevWasSpace = true; + std::string::iterator O = S.begin(); + for (std::string::iterator I = S.begin(), E = S.end(); + I != E; ++I) { + const char C = *I; + if (C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\v' || C == '\f') { + if (!PrevWasSpace) { + *O++ = ' '; + PrevWasSpace = true; + } + continue; + } else { + *O++ = C; + PrevWasSpace = false; + } + } + if (O != S.begin() && *(O - 1) == ' ') + --O; + + S.resize(O - S.begin()); +} +} // unnamed namespace + +BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) : + L(L), Traits(Traits) { + // Get lookahead token. + ConsumeToken(); +} + +std::string BriefParser::Parse() { + std::string FirstParagraphOrBrief; + std::string ReturnsParagraph; + bool InFirstParagraph = true; + bool InBrief = false; + bool InReturns = false; + + while (Tok.isNot(tok::eof)) { + if (Tok.is(tok::text)) { + if (InFirstParagraph || InBrief) + FirstParagraphOrBrief += Tok.getText(); + else if (InReturns) + ReturnsParagraph += Tok.getText(); + ConsumeToken(); + continue; + } + + if (Tok.is(tok::command)) { + StringRef Name = Tok.getCommandName(); + if (Traits.isBriefCommand(Name)) { + FirstParagraphOrBrief.clear(); + InBrief = true; + ConsumeToken(); + continue; + } + if (Traits.isReturnsCommand(Name)) { + InReturns = true; + ReturnsParagraph += "Returns "; + } + // Block commands implicitly start a new paragraph. + if (Traits.isBlockCommand(Name)) { + // We found an implicit paragraph end. + InFirstParagraph = false; + if (InBrief) + break; + } + } + + if (Tok.is(tok::newline)) { + if (InFirstParagraph || InBrief) + FirstParagraphOrBrief += ' '; + else if (InReturns) + ReturnsParagraph += ' '; + ConsumeToken(); + + if (Tok.is(tok::newline)) { + ConsumeToken(); + // We found a paragraph end. + InFirstParagraph = false; + InReturns = false; + if (InBrief) + break; + } + continue; + } + + // We didn't handle this token, so just drop it. + ConsumeToken(); + } + + cleanupBrief(FirstParagraphOrBrief); + if (!FirstParagraphOrBrief.empty()) + return FirstParagraphOrBrief; + + cleanupBrief(ReturnsParagraph); + return ReturnsParagraph; +} + +} // end namespace comments +} // end namespace clang + + diff --git a/lib/AST/CommentCommandTraits.cpp b/lib/AST/CommentCommandTraits.cpp new file mode 100644 index 000000000000..d8ce1f3fe7bd --- /dev/null +++ b/lib/AST/CommentCommandTraits.cpp @@ -0,0 +1,134 @@ +//===--- CommentCommandTraits.cpp - Comment command properties --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +// TODO: tablegen + +bool CommandTraits::isVerbatimBlockCommand(StringRef BeginName, + StringRef &EndName) const { + const char *Result = llvm::StringSwitch<const char *>(BeginName) + .Case("code", "endcode") + .Case("verbatim", "endverbatim") + .Case("htmlonly", "endhtmlonly") + .Case("latexonly", "endlatexonly") + .Case("xmlonly", "endxmlonly") + .Case("manonly", "endmanonly") + .Case("rtfonly", "endrtfonly") + + .Case("dot", "enddot") + .Case("msc", "endmsc") + + .Case("f$", "f$") // Inline LaTeX formula + .Case("f[", "f]") // Displayed LaTeX formula + .Case("f{", "f}") // LaTeX environment + + .Default(NULL); + + if (Result) { + EndName = Result; + return true; + } + + for (VerbatimBlockCommandVector::const_iterator + I = VerbatimBlockCommands.begin(), + E = VerbatimBlockCommands.end(); + I != E; ++I) + if (I->BeginName == BeginName) { + EndName = I->EndName; + return true; + } + + return false; +} + +bool CommandTraits::isVerbatimLineCommand(StringRef Name) const { + bool Result = isDeclarationCommand(Name) || llvm::StringSwitch<bool>(Name) + .Case("defgroup", true) + .Case("ingroup", true) + .Case("addtogroup", true) + .Case("weakgroup", true) + .Case("name", true) + + .Case("section", true) + .Case("subsection", true) + .Case("subsubsection", true) + .Case("paragraph", true) + + .Case("mainpage", true) + .Case("subpage", true) + .Case("ref", true) + + .Default(false); + + if (Result) + return true; + + for (VerbatimLineCommandVector::const_iterator + I = VerbatimLineCommands.begin(), + E = VerbatimLineCommands.end(); + I != E; ++I) + if (I->Name == Name) + return true; + + return false; +} + +bool CommandTraits::isDeclarationCommand(StringRef Name) const { + return llvm::StringSwitch<bool>(Name) + // Doxygen commands. + .Case("fn", true) + .Case("var", true) + .Case("property", true) + .Case("typedef", true) + + .Case("overload", true) + + // HeaderDoc commands. + .Case("class", true) + .Case("interface", true) + .Case("protocol", true) + .Case("category", true) + .Case("template", true) + .Case("function", true) + .Case("method", true) + .Case("callback", true) + .Case("var", true) + .Case("const", true) + .Case("constant", true) + .Case("property", true) + .Case("struct", true) + .Case("union", true) + .Case("typedef", true) + .Case("enum", true) + + .Default(false); +} + +void CommandTraits::addVerbatimBlockCommand(StringRef BeginName, + StringRef EndName) { + VerbatimBlockCommand VBC; + VBC.BeginName = BeginName; + VBC.EndName = EndName; + VerbatimBlockCommands.push_back(VBC); +} + +void CommandTraits::addVerbatimLineCommand(StringRef Name) { + VerbatimLineCommand VLC; + VLC.Name = Name; + VerbatimLineCommands.push_back(VLC); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp new file mode 100644 index 000000000000..dffc8233a0ca --- /dev/null +++ b/lib/AST/CommentDumper.cpp @@ -0,0 +1,231 @@ +//===--- CommentDumper.cpp - Dumping implementation for Comment ASTs ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentVisitor.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace comments { + +namespace { +class CommentDumper: public comments::ConstCommentVisitor<CommentDumper> { + raw_ostream &OS; + SourceManager *SM; + unsigned IndentLevel; + +public: + CommentDumper(raw_ostream &OS, SourceManager *SM) : + OS(OS), SM(SM), IndentLevel(0) + { } + + void dumpIndent() const { + for (unsigned i = 1, e = IndentLevel; i < e; ++i) + OS << " "; + } + + void dumpLocation(SourceLocation Loc) { + if (SM) + Loc.print(OS, *SM); + } + + void dumpSourceRange(const Comment *C); + + void dumpComment(const Comment *C); + + void dumpSubtree(const Comment *C); + + // Inline content. + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + + // Block content. + void visitParagraphComment(const ParagraphComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + void visitFullComment(const FullComment *C); +}; + +void CommentDumper::dumpSourceRange(const Comment *C) { + if (!SM) + return; + + SourceRange SR = C->getSourceRange(); + + OS << " <"; + dumpLocation(SR.getBegin()); + if (SR.getBegin() != SR.getEnd()) { + OS << ", "; + dumpLocation(SR.getEnd()); + } + OS << ">"; +} + +void CommentDumper::dumpComment(const Comment *C) { + dumpIndent(); + OS << "(" << C->getCommentKindName() + << " " << (const void *) C; + dumpSourceRange(C); +} + +void CommentDumper::dumpSubtree(const Comment *C) { + ++IndentLevel; + if (C) { + visit(C); + for (Comment::child_iterator I = C->child_begin(), + E = C->child_end(); + I != E; ++I) { + OS << '\n'; + dumpSubtree(*I); + } + OS << ')'; + } else { + dumpIndent(); + OS << "<<<NULL>>>"; + } + --IndentLevel; +} + +void CommentDumper::visitTextComment(const TextComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitInlineCommandComment(const InlineCommandComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\""; + switch (C->getRenderKind()) { + case InlineCommandComment::RenderNormal: + OS << " RenderNormal"; + break; + case InlineCommandComment::RenderBold: + OS << " RenderBold"; + break; + case InlineCommandComment::RenderMonospaced: + OS << " RenderMonospaced"; + break; + case InlineCommandComment::RenderEmphasized: + OS << " RenderEmphasized"; + break; + } + + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) + OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\""; +} + +void CommentDumper::visitHTMLStartTagComment(const HTMLStartTagComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getTagName() << "\""; + if (C->getNumAttrs() != 0) { + OS << " Attrs: "; + for (unsigned i = 0, e = C->getNumAttrs(); i != e; ++i) { + const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); + OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\""; + } + } + if (C->isSelfClosing()) + OS << " SelfClosing"; +} + +void CommentDumper::visitHTMLEndTagComment(const HTMLEndTagComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getTagName() << "\""; +} + +void CommentDumper::visitParagraphComment(const ParagraphComment *C) { + dumpComment(C); +} + +void CommentDumper::visitBlockCommandComment(const BlockCommandComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\""; + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) + OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\""; +} + +void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) { + dumpComment(C); + + OS << " " << ParamCommandComment::getDirectionAsString(C->getDirection()); + + if (C->isDirectionExplicit()) + OS << " explicitly"; + else + OS << " implicitly"; + + if (C->hasParamName()) + OS << " Param=\"" << C->getParamName() << "\""; + + if (C->isParamIndexValid()) + OS << " ParamIndex=" << C->getParamIndex(); +} + +void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) { + dumpComment(C); + + if (C->hasParamName()) { + OS << " Param=\"" << C->getParamName() << "\""; + } + + if (C->isPositionValid()) { + OS << " Position=<"; + for (unsigned i = 0, e = C->getDepth(); i != e; ++i) { + OS << C->getIndex(i); + if (i != e - 1) + OS << ", "; + } + OS << ">"; + } +} + +void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\"" + " CloseName=\"" << C->getCloseName() << "\""; +} + +void CommentDumper::visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitVerbatimLineComment(const VerbatimLineComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitFullComment(const FullComment *C) { + dumpComment(C); +} + +} // unnamed namespace + +void Comment::dump(llvm::raw_ostream &OS, SourceManager *SM) const { + CommentDumper D(llvm::errs(), SM); + D.dumpSubtree(this); + llvm::errs() << '\n'; +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp new file mode 100644 index 000000000000..b6516ec126f6 --- /dev/null +++ b/lib/AST/CommentLexer.cpp @@ -0,0 +1,815 @@ +#include "clang/AST/CommentLexer.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/Basic/ConvertUTF.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace comments { + +void Token::dump(const Lexer &L, const SourceManager &SM) const { + llvm::errs() << "comments::Token Kind=" << Kind << " "; + Loc.dump(SM); + llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n"; +} + +namespace { +bool isHTMLNamedCharacterReferenceCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z'); +} + +bool isHTMLDecimalCharacterReferenceCharacter(char C) { + return C >= '0' && C <= '9'; +} + +bool isHTMLHexCharacterReferenceCharacter(char C) { + return (C >= '0' && C <= '9') || + (C >= 'a' && C <= 'f') || + (C >= 'A' && C <= 'F'); +} +} // unnamed namespace + +StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const { + return llvm::StringSwitch<StringRef>(Name) + .Case("amp", "&") + .Case("lt", "<") + .Case("gt", ">") + .Case("quot", "\"") + .Case("apos", "\'") + .Default(""); +} + +StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const { + unsigned CodePoint = 0; + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + assert(isHTMLDecimalCharacterReferenceCharacter(Name[i])); + CodePoint *= 10; + CodePoint += Name[i] - '0'; + } + + char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); + char *ResolvedPtr = Resolved; + if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) + return StringRef(Resolved, ResolvedPtr - Resolved); + else + return StringRef(); +} + +StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const { + unsigned CodePoint = 0; + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + CodePoint *= 16; + const char C = Name[i]; + assert(isHTMLHexCharacterReferenceCharacter(C)); + if (C >= '0' && C <= '9') + CodePoint += Name[i] - '0'; + else if (C >= 'a' && C <= 'f') + CodePoint += Name[i] - 'a' + 10; + else + CodePoint += Name[i] - 'A' + 10; + } + + char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); + char *ResolvedPtr = Resolved; + if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) + return StringRef(Resolved, ResolvedPtr - Resolved); + else + return StringRef(); +} + +void Lexer::skipLineStartingDecorations() { + // This function should be called only for C comments + assert(CommentState == LCS_InsideCComment); + + if (BufferPtr == CommentEnd) + return; + + switch (*BufferPtr) { + case ' ': + case '\t': + case '\f': + case '\v': { + const char *NewBufferPtr = BufferPtr; + NewBufferPtr++; + if (NewBufferPtr == CommentEnd) + return; + + char C = *NewBufferPtr; + while (C == ' ' || C == '\t' || C == '\f' || C == '\v') { + NewBufferPtr++; + if (NewBufferPtr == CommentEnd) + return; + C = *NewBufferPtr; + } + if (C == '*') + BufferPtr = NewBufferPtr + 1; + break; + } + case '*': + BufferPtr++; + break; + } +} + +namespace { +/// Returns pointer to the first newline character in the string. +const char *findNewline(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + const char C = *BufferPtr; + if (C == '\n' || C == '\r') + return BufferPtr; + } + return BufferEnd; +} + +const char *skipNewline(const char *BufferPtr, const char *BufferEnd) { + if (BufferPtr == BufferEnd) + return BufferPtr; + + if (*BufferPtr == '\n') + BufferPtr++; + else { + assert(*BufferPtr == '\r'); + BufferPtr++; + if (BufferPtr != BufferEnd && *BufferPtr == '\n') + BufferPtr++; + } + return BufferPtr; +} + +const char *skipNamedCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +const char *skipDecimalCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +const char *skipHexCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +bool isHTMLIdentifierStartingCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z'); +} + +bool isHTMLIdentifierCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z') || + (C >= '0' && C <= '9'); +} + +const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLIdentifierCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +/// Skip HTML string quoted in single or double quotes. Escaping quotes inside +/// string allowed. +/// +/// Returns pointer to closing quote. +const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd) +{ + const char Quote = *BufferPtr; + assert(Quote == '\"' || Quote == '\''); + + BufferPtr++; + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + const char C = *BufferPtr; + if (C == Quote && BufferPtr[-1] != '\\') + return BufferPtr; + } + return BufferEnd; +} + +bool isHorizontalWhitespace(char C) { + return C == ' ' || C == '\t' || C == '\f' || C == '\v'; +} + +bool isWhitespace(char C) { + return C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\f' || C == '\v'; +} + +const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isWhitespace(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +bool isWhitespace(const char *BufferPtr, const char *BufferEnd) { + return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd; +} + +bool isCommandNameCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z') || + (C >= '0' && C <= '9'); +} + +const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isCommandNameCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +/// Return the one past end pointer for BCPL comments. +/// Handles newlines escaped with backslash or trigraph for backslahs. +const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) { + const char *CurPtr = BufferPtr; + while (CurPtr != BufferEnd) { + char C = *CurPtr; + while (C != '\n' && C != '\r') { + CurPtr++; + if (CurPtr == BufferEnd) + return BufferEnd; + C = *CurPtr; + } + // We found a newline, check if it is escaped. + const char *EscapePtr = CurPtr - 1; + while(isHorizontalWhitespace(*EscapePtr)) + EscapePtr--; + + if (*EscapePtr == '\\' || + (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' && + EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) { + // We found an escaped newline. + CurPtr = skipNewline(CurPtr, BufferEnd); + } else + return CurPtr; // Not an escaped newline. + } + return BufferEnd; +} + +/// Return the one past end pointer for C comments. +/// Very dumb, does not handle escaped newlines or trigraphs. +const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (*BufferPtr == '*') { + assert(BufferPtr + 1 != BufferEnd); + if (*(BufferPtr + 1) == '/') + return BufferPtr; + } + } + llvm_unreachable("buffer end hit before '*/' was seen"); +} +} // unnamed namespace + +void Lexer::lexCommentText(Token &T) { + assert(CommentState == LCS_InsideBCPLComment || + CommentState == LCS_InsideCComment); + + switch (State) { + case LS_Normal: + break; + case LS_VerbatimBlockFirstLine: + lexVerbatimBlockFirstLine(T); + return; + case LS_VerbatimBlockBody: + lexVerbatimBlockBody(T); + return; + case LS_VerbatimLineText: + lexVerbatimLineText(T); + return; + case LS_HTMLStartTag: + lexHTMLStartTag(T); + return; + case LS_HTMLEndTag: + lexHTMLEndTag(T); + return; + } + + assert(State == LS_Normal); + + const char *TokenPtr = BufferPtr; + assert(TokenPtr < CommentEnd); + while (TokenPtr != CommentEnd) { + switch(*TokenPtr) { + case '\\': + case '@': { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + char C = *TokenPtr; + switch (C) { + default: + break; + + case '\\': case '@': case '&': case '$': + case '#': case '<': case '>': case '%': + case '\"': case '.': case ':': + // This is one of \\ \@ \& \$ etc escape sequences. + TokenPtr++; + if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') { + // This is the \:: escape sequence. + TokenPtr++; + } + StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1)); + formTokenWithChars(T, TokenPtr, tok::text); + T.setText(UnescapedText); + return; + } + + // Don't make zero-length commands. + if (!isCommandNameCharacter(*TokenPtr)) { + formTextToken(T, TokenPtr); + return; + } + + TokenPtr = skipCommandName(TokenPtr, CommentEnd); + unsigned Length = TokenPtr - (BufferPtr + 1); + + // Hardcoded support for lexing LaTeX formula commands + // \f$ \f[ \f] \f{ \f} as a single command. + if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) { + C = *TokenPtr; + if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') { + TokenPtr++; + Length++; + } + } + + const StringRef CommandName(BufferPtr + 1, Length); + StringRef EndName; + + if (Traits.isVerbatimBlockCommand(CommandName, EndName)) { + setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName); + return; + } + if (Traits.isVerbatimLineCommand(CommandName)) { + setupAndLexVerbatimLine(T, TokenPtr); + return; + } + formTokenWithChars(T, TokenPtr, tok::command); + T.setCommandName(CommandName); + return; + } + + case '&': + lexHTMLCharacterReference(T); + return; + + case '<': { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + const char C = *TokenPtr; + if (isHTMLIdentifierStartingCharacter(C)) + setupAndLexHTMLStartTag(T); + else if (C == '/') + setupAndLexHTMLEndTag(T); + else + formTextToken(T, TokenPtr); + + return; + } + + case '\n': + case '\r': + TokenPtr = skipNewline(TokenPtr, CommentEnd); + formTokenWithChars(T, TokenPtr, tok::newline); + + if (CommentState == LCS_InsideCComment) + skipLineStartingDecorations(); + return; + + default: { + while (true) { + TokenPtr++; + if (TokenPtr == CommentEnd) + break; + const char C = *TokenPtr; + if(C == '\n' || C == '\r' || + C == '\\' || C == '@' || C == '&' || C == '<') + break; + } + formTextToken(T, TokenPtr); + return; + } + } + } +} + +void Lexer::setupAndLexVerbatimBlock(Token &T, + const char *TextBegin, + char Marker, StringRef EndName) { + VerbatimBlockEndCommandName.clear(); + VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@"); + VerbatimBlockEndCommandName.append(EndName); + + StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1)); + formTokenWithChars(T, TextBegin, tok::verbatim_block_begin); + T.setVerbatimBlockName(Name); + + // If there is a newline following the verbatim opening command, skip the + // newline so that we don't create an tok::verbatim_block_line with empty + // text content. + if (BufferPtr != CommentEnd) { + const char C = *BufferPtr; + if (C == '\n' || C == '\r') { + BufferPtr = skipNewline(BufferPtr, CommentEnd); + State = LS_VerbatimBlockBody; + return; + } + } + + State = LS_VerbatimBlockFirstLine; +} + +void Lexer::lexVerbatimBlockFirstLine(Token &T) { +again: + assert(BufferPtr < CommentEnd); + + // FIXME: It would be better to scan the text once, finding either the block + // end command or newline. + // + // Extract current line. + const char *Newline = findNewline(BufferPtr, CommentEnd); + StringRef Line(BufferPtr, Newline - BufferPtr); + + // Look for end command in current line. + size_t Pos = Line.find(VerbatimBlockEndCommandName); + const char *TextEnd; + const char *NextLine; + if (Pos == StringRef::npos) { + // Current line is completely verbatim. + TextEnd = Newline; + NextLine = skipNewline(Newline, CommentEnd); + } else if (Pos == 0) { + // Current line contains just an end command. + const char *End = BufferPtr + VerbatimBlockEndCommandName.size(); + StringRef Name(BufferPtr + 1, End - (BufferPtr + 1)); + formTokenWithChars(T, End, tok::verbatim_block_end); + T.setVerbatimBlockName(Name); + State = LS_Normal; + return; + } else { + // There is some text, followed by end command. Extract text first. + TextEnd = BufferPtr + Pos; + NextLine = TextEnd; + // If there is only whitespace before end command, skip whitespace. + if (isWhitespace(BufferPtr, TextEnd)) { + BufferPtr = TextEnd; + goto again; + } + } + + StringRef Text(BufferPtr, TextEnd - BufferPtr); + formTokenWithChars(T, NextLine, tok::verbatim_block_line); + T.setVerbatimBlockText(Text); + + State = LS_VerbatimBlockBody; +} + +void Lexer::lexVerbatimBlockBody(Token &T) { + assert(State == LS_VerbatimBlockBody); + + if (CommentState == LCS_InsideCComment) + skipLineStartingDecorations(); + + lexVerbatimBlockFirstLine(T); +} + +void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) { + const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1); + formTokenWithChars(T, TextBegin, tok::verbatim_line_name); + T.setVerbatimLineName(Name); + + State = LS_VerbatimLineText; +} + +void Lexer::lexVerbatimLineText(Token &T) { + assert(State == LS_VerbatimLineText); + + // Extract current line. + const char *Newline = findNewline(BufferPtr, CommentEnd); + const StringRef Text(BufferPtr, Newline - BufferPtr); + formTokenWithChars(T, Newline, tok::verbatim_line_text); + T.setVerbatimLineText(Text); + + State = LS_Normal; +} + +void Lexer::lexHTMLCharacterReference(Token &T) { + const char *TokenPtr = BufferPtr; + assert(*TokenPtr == '&'); + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + const char *NamePtr; + bool isNamed = false; + bool isDecimal = false; + char C = *TokenPtr; + if (isHTMLNamedCharacterReferenceCharacter(C)) { + NamePtr = TokenPtr; + TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd); + isNamed = true; + } else if (C == '#') { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + C = *TokenPtr; + if (isHTMLDecimalCharacterReferenceCharacter(C)) { + NamePtr = TokenPtr; + TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd); + isDecimal = true; + } else if (C == 'x' || C == 'X') { + TokenPtr++; + NamePtr = TokenPtr; + TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd); + } else { + formTextToken(T, TokenPtr); + return; + } + } else { + formTextToken(T, TokenPtr); + return; + } + if (NamePtr == TokenPtr || TokenPtr == CommentEnd || + *TokenPtr != ';') { + formTextToken(T, TokenPtr); + return; + } + StringRef Name(NamePtr, TokenPtr - NamePtr); + TokenPtr++; // Skip semicolon. + StringRef Resolved; + if (isNamed) + Resolved = resolveHTMLNamedCharacterReference(Name); + else if (isDecimal) + Resolved = resolveHTMLDecimalCharacterReference(Name); + else + Resolved = resolveHTMLHexCharacterReference(Name); + + if (Resolved.empty()) { + formTextToken(T, TokenPtr); + return; + } + formTokenWithChars(T, TokenPtr, tok::text); + T.setText(Resolved); + return; +} + +void Lexer::setupAndLexHTMLStartTag(Token &T) { + assert(BufferPtr[0] == '<' && + isHTMLIdentifierStartingCharacter(BufferPtr[1])); + const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd); + + StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1)); + formTokenWithChars(T, TagNameEnd, tok::html_start_tag); + T.setHTMLTagStartName(Name); + + BufferPtr = skipWhitespace(BufferPtr, CommentEnd); + + const char C = *BufferPtr; + if (BufferPtr != CommentEnd && + (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C))) + State = LS_HTMLStartTag; +} + +void Lexer::lexHTMLStartTag(Token &T) { + assert(State == LS_HTMLStartTag); + + const char *TokenPtr = BufferPtr; + char C = *TokenPtr; + if (isHTMLIdentifierCharacter(C)) { + TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd); + StringRef Ident(BufferPtr, TokenPtr - BufferPtr); + formTokenWithChars(T, TokenPtr, tok::html_ident); + T.setHTMLIdent(Ident); + } else { + switch (C) { + case '=': + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_equals); + break; + case '\"': + case '\'': { + const char *OpenQuote = TokenPtr; + TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd); + const char *ClosingQuote = TokenPtr; + if (TokenPtr != CommentEnd) // Skip closing quote. + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_quoted_string); + T.setHTMLQuotedString(StringRef(OpenQuote + 1, + ClosingQuote - (OpenQuote + 1))); + break; + } + case '>': + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_greater); + State = LS_Normal; + return; + case '/': + TokenPtr++; + if (TokenPtr != CommentEnd && *TokenPtr == '>') { + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_slash_greater); + } else + formTextToken(T, TokenPtr); + + State = LS_Normal; + return; + } + } + + // Now look ahead and return to normal state if we don't see any HTML tokens + // ahead. + BufferPtr = skipWhitespace(BufferPtr, CommentEnd); + if (BufferPtr == CommentEnd) { + State = LS_Normal; + return; + } + + C = *BufferPtr; + if (!isHTMLIdentifierStartingCharacter(C) && + C != '=' && C != '\"' && C != '\'' && C != '>') { + State = LS_Normal; + return; + } +} + +void Lexer::setupAndLexHTMLEndTag(Token &T) { + assert(BufferPtr[0] == '<' && BufferPtr[1] == '/'); + + const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd); + const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd); + + const char *End = skipWhitespace(TagNameEnd, CommentEnd); + + formTokenWithChars(T, End, tok::html_end_tag); + T.setHTMLTagEndName(StringRef(TagNameBegin, TagNameEnd - TagNameBegin)); + + if (BufferPtr != CommentEnd && *BufferPtr == '>') + State = LS_HTMLEndTag; +} + +void Lexer::lexHTMLEndTag(Token &T) { + assert(BufferPtr != CommentEnd && *BufferPtr == '>'); + + formTokenWithChars(T, BufferPtr + 1, tok::html_greater); + State = LS_Normal; +} + +Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits, + SourceLocation FileLoc, const CommentOptions &CommOpts, + const char *BufferStart, const char *BufferEnd): + Allocator(Allocator), Traits(Traits), + BufferStart(BufferStart), BufferEnd(BufferEnd), + FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart), + CommentState(LCS_BeforeComment), State(LS_Normal) { +} + +void Lexer::lex(Token &T) { +again: + switch (CommentState) { + case LCS_BeforeComment: + if (BufferPtr == BufferEnd) { + formTokenWithChars(T, BufferPtr, tok::eof); + return; + } + + assert(*BufferPtr == '/'); + BufferPtr++; // Skip first slash. + switch(*BufferPtr) { + case '/': { // BCPL comment. + BufferPtr++; // Skip second slash. + + if (BufferPtr != BufferEnd) { + // Skip Doxygen magic marker, if it is present. + // It might be missing because of a typo //< or /*<, or because we + // merged this non-Doxygen comment into a bunch of Doxygen comments + // around it: /** ... */ /* ... */ /** ... */ + const char C = *BufferPtr; + if (C == '/' || C == '!') + BufferPtr++; + } + + // Skip less-than symbol that marks trailing comments. + // Skip it even if the comment is not a Doxygen one, because //< and /*< + // are frequent typos. + if (BufferPtr != BufferEnd && *BufferPtr == '<') + BufferPtr++; + + CommentState = LCS_InsideBCPLComment; + if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine) + State = LS_Normal; + CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd); + goto again; + } + case '*': { // C comment. + BufferPtr++; // Skip star. + + // Skip Doxygen magic marker. + const char C = *BufferPtr; + if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!') + BufferPtr++; + + // Skip less-than symbol that marks trailing comments. + if (BufferPtr != BufferEnd && *BufferPtr == '<') + BufferPtr++; + + CommentState = LCS_InsideCComment; + State = LS_Normal; + CommentEnd = findCCommentEnd(BufferPtr, BufferEnd); + goto again; + } + default: + llvm_unreachable("second character of comment should be '/' or '*'"); + } + + case LCS_BetweenComments: { + // Consecutive comments are extracted only if there is only whitespace + // between them. So we can search for the start of the next comment. + const char *EndWhitespace = BufferPtr; + while(EndWhitespace != BufferEnd && *EndWhitespace != '/') + EndWhitespace++; + + // Turn any whitespace between comments (and there is only whitespace + // between them -- guaranteed by comment extraction) into a newline. We + // have two newlines between C comments in total (first one was synthesized + // after a comment). + formTokenWithChars(T, EndWhitespace, tok::newline); + + CommentState = LCS_BeforeComment; + break; + } + + case LCS_InsideBCPLComment: + case LCS_InsideCComment: + if (BufferPtr != CommentEnd) { + lexCommentText(T); + break; + } else { + // Skip C comment closing sequence. + if (CommentState == LCS_InsideCComment) { + assert(BufferPtr[0] == '*' && BufferPtr[1] == '/'); + BufferPtr += 2; + assert(BufferPtr <= BufferEnd); + + // Synthenize newline just after the C comment, regardless if there is + // actually a newline. + formTokenWithChars(T, BufferPtr, tok::newline); + + CommentState = LCS_BetweenComments; + break; + } else { + // Don't synthesized a newline after BCPL comment. + CommentState = LCS_BetweenComments; + goto again; + } + } + } +} + +StringRef Lexer::getSpelling(const Token &Tok, + const SourceManager &SourceMgr, + bool *Invalid) const { + SourceLocation Loc = Tok.getLocation(); + std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc); + + bool InvalidTemp = false; + StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); + if (InvalidTemp) { + *Invalid = true; + return StringRef(); + } + + const char *Begin = File.data() + LocInfo.second; + return StringRef(Begin, Tok.getLength()); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp new file mode 100644 index 000000000000..43abf6a476a8 --- /dev/null +++ b/lib/AST/CommentParser.cpp @@ -0,0 +1,722 @@ +//===--- CommentParser.cpp - Doxygen comment parser -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentParser.h" +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentDiagnostic.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace comments { + +/// Re-lexes a sequence of tok::text tokens. +class TextTokenRetokenizer { + llvm::BumpPtrAllocator &Allocator; + Parser &P; + + /// This flag is set when there are no more tokens we can fetch from lexer. + bool NoMoreInterestingTokens; + + /// Token buffer: tokens we have processed and lookahead. + SmallVector<Token, 16> Toks; + + /// A position in \c Toks. + struct Position { + unsigned CurToken; + const char *BufferStart; + const char *BufferEnd; + const char *BufferPtr; + SourceLocation BufferStartLoc; + }; + + /// Current position in Toks. + Position Pos; + + bool isEnd() const { + return Pos.CurToken >= Toks.size(); + } + + /// Sets up the buffer pointers to point to current token. + void setupBuffer() { + assert(!isEnd()); + const Token &Tok = Toks[Pos.CurToken]; + + Pos.BufferStart = Tok.getText().begin(); + Pos.BufferEnd = Tok.getText().end(); + Pos.BufferPtr = Pos.BufferStart; + Pos.BufferStartLoc = Tok.getLocation(); + } + + SourceLocation getSourceLocation() const { + const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart; + return Pos.BufferStartLoc.getLocWithOffset(CharNo); + } + + char peek() const { + assert(!isEnd()); + assert(Pos.BufferPtr != Pos.BufferEnd); + return *Pos.BufferPtr; + } + + void consumeChar() { + assert(!isEnd()); + assert(Pos.BufferPtr != Pos.BufferEnd); + Pos.BufferPtr++; + if (Pos.BufferPtr == Pos.BufferEnd) { + Pos.CurToken++; + if (isEnd() && !addToken()) + return; + + assert(!isEnd()); + setupBuffer(); + } + } + + /// Add a token. + /// Returns true on success, false if there are no interesting tokens to + /// fetch from lexer. + bool addToken() { + if (NoMoreInterestingTokens) + return false; + + if (P.Tok.is(tok::newline)) { + // If we see a single newline token between text tokens, skip it. + Token Newline = P.Tok; + P.consumeToken(); + if (P.Tok.isNot(tok::text)) { + P.putBack(Newline); + NoMoreInterestingTokens = true; + return false; + } + } + if (P.Tok.isNot(tok::text)) { + NoMoreInterestingTokens = true; + return false; + } + + Toks.push_back(P.Tok); + P.consumeToken(); + if (Toks.size() == 1) + setupBuffer(); + return true; + } + + static bool isWhitespace(char C) { + return C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\f' || C == '\v'; + } + + void consumeWhitespace() { + while (!isEnd()) { + if (isWhitespace(peek())) + consumeChar(); + else + break; + } + } + + void formTokenWithChars(Token &Result, + SourceLocation Loc, + const char *TokBegin, + unsigned TokLength, + StringRef Text) { + Result.setLocation(Loc); + Result.setKind(tok::text); + Result.setLength(TokLength); +#ifndef NDEBUG + Result.TextPtr1 = "<UNSET>"; + Result.TextLen1 = 7; +#endif + Result.setText(Text); + } + +public: + TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P): + Allocator(Allocator), P(P), NoMoreInterestingTokens(false) { + Pos.CurToken = 0; + addToken(); + } + + /// Extract a word -- sequence of non-whitespace characters. + bool lexWord(Token &Tok) { + if (isEnd()) + return false; + + Position SavedPos = Pos; + + consumeWhitespace(); + SmallString<32> WordText; + const char *WordBegin = Pos.BufferPtr; + SourceLocation Loc = getSourceLocation(); + while (!isEnd()) { + const char C = peek(); + if (!isWhitespace(C)) { + WordText.push_back(C); + consumeChar(); + } else + break; + } + const unsigned Length = WordText.size(); + if (Length == 0) { + Pos = SavedPos; + return false; + } + + char *TextPtr = Allocator.Allocate<char>(Length + 1); + + memcpy(TextPtr, WordText.c_str(), Length + 1); + StringRef Text = StringRef(TextPtr, Length); + + formTokenWithChars(Tok, Loc, WordBegin, + Pos.BufferPtr - WordBegin, Text); + return true; + } + + bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) { + if (isEnd()) + return false; + + Position SavedPos = Pos; + + consumeWhitespace(); + SmallString<32> WordText; + const char *WordBegin = Pos.BufferPtr; + SourceLocation Loc = getSourceLocation(); + bool Error = false; + if (!isEnd()) { + const char C = peek(); + if (C == OpenDelim) { + WordText.push_back(C); + consumeChar(); + } else + Error = true; + } + char C = '\0'; + while (!Error && !isEnd()) { + C = peek(); + WordText.push_back(C); + consumeChar(); + if (C == CloseDelim) + break; + } + if (!Error && C != CloseDelim) + Error = true; + + if (Error) { + Pos = SavedPos; + return false; + } + + const unsigned Length = WordText.size(); + char *TextPtr = Allocator.Allocate<char>(Length + 1); + + memcpy(TextPtr, WordText.c_str(), Length + 1); + StringRef Text = StringRef(TextPtr, Length); + + formTokenWithChars(Tok, Loc, WordBegin, + Pos.BufferPtr - WordBegin, Text); + return true; + } + + /// Put back tokens that we didn't consume. + void putBackLeftoverTokens() { + if (isEnd()) + return; + + bool HavePartialTok = false; + Token PartialTok; + if (Pos.BufferPtr != Pos.BufferStart) { + formTokenWithChars(PartialTok, getSourceLocation(), + Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr, + StringRef(Pos.BufferPtr, + Pos.BufferEnd - Pos.BufferPtr)); + HavePartialTok = true; + Pos.CurToken++; + } + + P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end())); + Pos.CurToken = Toks.size(); + + if (HavePartialTok) + P.putBack(PartialTok); + } +}; + +Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator, + const SourceManager &SourceMgr, DiagnosticsEngine &Diags, + const CommandTraits &Traits): + L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), + Traits(Traits) { + consumeToken(); +} + +void Parser::parseParamCommandArgs(ParamCommandComment *PC, + TextTokenRetokenizer &Retokenizer) { + Token Arg; + // Check if argument looks like direction specification: [dir] + // e.g., [in], [out], [in,out] + if (Retokenizer.lexDelimitedSeq(Arg, '[', ']')) + S.actOnParamCommandDirectionArg(PC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); + + if (Retokenizer.lexWord(Arg)) + S.actOnParamCommandParamNameArg(PC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); +} + +void Parser::parseTParamCommandArgs(TParamCommandComment *TPC, + TextTokenRetokenizer &Retokenizer) { + Token Arg; + if (Retokenizer.lexWord(Arg)) + S.actOnTParamCommandParamNameArg(TPC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); +} + +void Parser::parseBlockCommandArgs(BlockCommandComment *BC, + TextTokenRetokenizer &Retokenizer, + unsigned NumArgs) { + typedef BlockCommandComment::Argument Argument; + Argument *Args = + new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs]; + unsigned ParsedArgs = 0; + Token Arg; + while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) { + Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(), + Arg.getEndLocation()), + Arg.getText()); + ParsedArgs++; + } + + S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs)); +} + +BlockCommandComment *Parser::parseBlockCommand() { + assert(Tok.is(tok::command)); + + ParamCommandComment *PC; + TParamCommandComment *TPC; + BlockCommandComment *BC; + bool IsParam = false; + bool IsTParam = false; + unsigned NumArgs = 0; + if (Traits.isParamCommand(Tok.getCommandName())) { + IsParam = true; + PC = S.actOnParamCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } if (Traits.isTParamCommand(Tok.getCommandName())) { + IsTParam = true; + TPC = S.actOnTParamCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } else { + NumArgs = Traits.getBlockCommandNumArgs(Tok.getCommandName()); + BC = S.actOnBlockCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } + consumeToken(); + + if (Tok.is(tok::command) && Traits.isBlockCommand(Tok.getCommandName())) { + // Block command ahead. We can't nest block commands, so pretend that this + // command has an empty argument. + ParagraphComment *Paragraph = S.actOnParagraphComment( + ArrayRef<InlineContentComment *>()); + if (IsParam) { + S.actOnParamCommandFinish(PC, Paragraph); + return PC; + } else if (IsTParam) { + S.actOnTParamCommandFinish(TPC, Paragraph); + return TPC; + } else { + S.actOnBlockCommandFinish(BC, Paragraph); + return BC; + } + } + + if (IsParam || IsTParam || NumArgs > 0) { + // In order to parse command arguments we need to retokenize a few + // following text tokens. + TextTokenRetokenizer Retokenizer(Allocator, *this); + + if (IsParam) + parseParamCommandArgs(PC, Retokenizer); + else if (IsTParam) + parseTParamCommandArgs(TPC, Retokenizer); + else + parseBlockCommandArgs(BC, Retokenizer, NumArgs); + + Retokenizer.putBackLeftoverTokens(); + } + + BlockContentComment *Block = parseParagraphOrBlockCommand(); + // Since we have checked for a block command, we should have parsed a + // paragraph. + ParagraphComment *Paragraph = cast<ParagraphComment>(Block); + if (IsParam) { + S.actOnParamCommandFinish(PC, Paragraph); + return PC; + } else if (IsTParam) { + S.actOnTParamCommandFinish(TPC, Paragraph); + return TPC; + } else { + S.actOnBlockCommandFinish(BC, Paragraph); + return BC; + } +} + +InlineCommandComment *Parser::parseInlineCommand() { + assert(Tok.is(tok::command)); + + const Token CommandTok = Tok; + consumeToken(); + + TextTokenRetokenizer Retokenizer(Allocator, *this); + + Token ArgTok; + bool ArgTokValid = Retokenizer.lexWord(ArgTok); + + InlineCommandComment *IC; + if (ArgTokValid) { + IC = S.actOnInlineCommand(CommandTok.getLocation(), + CommandTok.getEndLocation(), + CommandTok.getCommandName(), + ArgTok.getLocation(), + ArgTok.getEndLocation(), + ArgTok.getText()); + } else { + IC = S.actOnInlineCommand(CommandTok.getLocation(), + CommandTok.getEndLocation(), + CommandTok.getCommandName()); + } + + Retokenizer.putBackLeftoverTokens(); + + return IC; +} + +HTMLStartTagComment *Parser::parseHTMLStartTag() { + assert(Tok.is(tok::html_start_tag)); + HTMLStartTagComment *HST = + S.actOnHTMLStartTagStart(Tok.getLocation(), + Tok.getHTMLTagStartName()); + consumeToken(); + + SmallVector<HTMLStartTagComment::Attribute, 2> Attrs; + while (true) { + switch (Tok.getKind()) { + case tok::html_ident: { + Token Ident = Tok; + consumeToken(); + if (Tok.isNot(tok::html_equals)) { + Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), + Ident.getHTMLIdent())); + continue; + } + Token Equals = Tok; + consumeToken(); + if (Tok.isNot(tok::html_quoted_string)) { + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_quoted_string) + << SourceRange(Equals.getLocation()); + Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), + Ident.getHTMLIdent())); + while (Tok.is(tok::html_equals) || + Tok.is(tok::html_quoted_string)) + consumeToken(); + continue; + } + Attrs.push_back(HTMLStartTagComment::Attribute( + Ident.getLocation(), + Ident.getHTMLIdent(), + Equals.getLocation(), + SourceRange(Tok.getLocation(), + Tok.getEndLocation()), + Tok.getHTMLQuotedString())); + consumeToken(); + continue; + } + + case tok::html_greater: + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + Tok.getLocation(), + /* IsSelfClosing = */ false); + consumeToken(); + return HST; + + case tok::html_slash_greater: + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + Tok.getLocation(), + /* IsSelfClosing = */ true); + consumeToken(); + return HST; + + case tok::html_equals: + case tok::html_quoted_string: + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater); + while (Tok.is(tok::html_equals) || + Tok.is(tok::html_quoted_string)) + consumeToken(); + if (Tok.is(tok::html_ident) || + Tok.is(tok::html_greater) || + Tok.is(tok::html_slash_greater)) + continue; + + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + SourceLocation(), + /* IsSelfClosing = */ false); + return HST; + + default: + // Not a token from an HTML start tag. Thus HTML tag prematurely ended. + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + SourceLocation(), + /* IsSelfClosing = */ false); + bool StartLineInvalid; + const unsigned StartLine = SourceMgr.getPresumedLineNumber( + HST->getLocation(), + &StartLineInvalid); + bool EndLineInvalid; + const unsigned EndLine = SourceMgr.getPresumedLineNumber( + Tok.getLocation(), + &EndLineInvalid); + if (StartLineInvalid || EndLineInvalid || StartLine == EndLine) + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater) + << HST->getSourceRange(); + else { + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater); + Diag(HST->getLocation(), diag::note_doc_html_tag_started_here) + << HST->getSourceRange(); + } + return HST; + } + } +} + +HTMLEndTagComment *Parser::parseHTMLEndTag() { + assert(Tok.is(tok::html_end_tag)); + Token TokEndTag = Tok; + consumeToken(); + SourceLocation Loc; + if (Tok.is(tok::html_greater)) { + Loc = Tok.getLocation(); + consumeToken(); + } + + return S.actOnHTMLEndTag(TokEndTag.getLocation(), + Loc, + TokEndTag.getHTMLTagEndName()); +} + +BlockContentComment *Parser::parseParagraphOrBlockCommand() { + SmallVector<InlineContentComment *, 8> Content; + + while (true) { + switch (Tok.getKind()) { + case tok::verbatim_block_begin: + case tok::verbatim_line_name: + case tok::eof: + assert(Content.size() != 0); + break; // Block content or EOF ahead, finish this parapgaph. + + case tok::command: + if (Traits.isBlockCommand(Tok.getCommandName())) { + if (Content.size() == 0) + return parseBlockCommand(); + break; // Block command ahead, finish this parapgaph. + } + if (Traits.isInlineCommand(Tok.getCommandName())) { + Content.push_back(parseInlineCommand()); + continue; + } + + // Not a block command, not an inline command ==> an unknown command. + Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName())); + consumeToken(); + continue; + + case tok::newline: { + consumeToken(); + if (Tok.is(tok::newline) || Tok.is(tok::eof)) { + consumeToken(); + break; // Two newlines -- end of paragraph. + } + if (Content.size() > 0) + Content.back()->addTrailingNewline(); + continue; + } + + // Don't deal with HTML tag soup now. + case tok::html_start_tag: + Content.push_back(parseHTMLStartTag()); + continue; + + case tok::html_end_tag: + Content.push_back(parseHTMLEndTag()); + continue; + + case tok::text: + Content.push_back(S.actOnText(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getText())); + consumeToken(); + continue; + + case tok::verbatim_block_line: + case tok::verbatim_block_end: + case tok::verbatim_line_text: + case tok::html_ident: + case tok::html_equals: + case tok::html_quoted_string: + case tok::html_greater: + case tok::html_slash_greater: + llvm_unreachable("should not see this token"); + } + break; + } + + return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); +} + +VerbatimBlockComment *Parser::parseVerbatimBlock() { + assert(Tok.is(tok::verbatim_block_begin)); + + VerbatimBlockComment *VB = + S.actOnVerbatimBlockStart(Tok.getLocation(), + Tok.getVerbatimBlockName()); + consumeToken(); + + // Don't create an empty line if verbatim opening command is followed + // by a newline. + if (Tok.is(tok::newline)) + consumeToken(); + + SmallVector<VerbatimBlockLineComment *, 8> Lines; + while (Tok.is(tok::verbatim_block_line) || + Tok.is(tok::newline)) { + VerbatimBlockLineComment *Line; + if (Tok.is(tok::verbatim_block_line)) { + Line = S.actOnVerbatimBlockLine(Tok.getLocation(), + Tok.getVerbatimBlockText()); + consumeToken(); + if (Tok.is(tok::newline)) { + consumeToken(); + } + } else { + // Empty line, just a tok::newline. + Line = S.actOnVerbatimBlockLine(Tok.getLocation(), ""); + consumeToken(); + } + Lines.push_back(Line); + } + + if (Tok.is(tok::verbatim_block_end)) { + S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), + Tok.getVerbatimBlockName(), + S.copyArray(llvm::makeArrayRef(Lines))); + consumeToken(); + } else { + // Unterminated \\verbatim block + S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", + S.copyArray(llvm::makeArrayRef(Lines))); + } + + return VB; +} + +VerbatimLineComment *Parser::parseVerbatimLine() { + assert(Tok.is(tok::verbatim_line_name)); + + Token NameTok = Tok; + consumeToken(); + + SourceLocation TextBegin; + StringRef Text; + // Next token might not be a tok::verbatim_line_text if verbatim line + // starting command comes just before a newline or comment end. + if (Tok.is(tok::verbatim_line_text)) { + TextBegin = Tok.getLocation(); + Text = Tok.getVerbatimLineText(); + } else { + TextBegin = NameTok.getEndLocation(); + Text = ""; + } + + VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(), + NameTok.getVerbatimLineName(), + TextBegin, + Text); + consumeToken(); + return VL; +} + +BlockContentComment *Parser::parseBlockContent() { + switch (Tok.getKind()) { + case tok::text: + case tok::command: + case tok::html_start_tag: + case tok::html_end_tag: + return parseParagraphOrBlockCommand(); + + case tok::verbatim_block_begin: + return parseVerbatimBlock(); + + case tok::verbatim_line_name: + return parseVerbatimLine(); + + case tok::eof: + case tok::newline: + case tok::verbatim_block_line: + case tok::verbatim_block_end: + case tok::verbatim_line_text: + case tok::html_ident: + case tok::html_equals: + case tok::html_quoted_string: + case tok::html_greater: + case tok::html_slash_greater: + llvm_unreachable("should not see this token"); + } + llvm_unreachable("bogus token kind"); +} + +FullComment *Parser::parseFullComment() { + // Skip newlines at the beginning of the comment. + while (Tok.is(tok::newline)) + consumeToken(); + + SmallVector<BlockContentComment *, 8> Blocks; + while (Tok.isNot(tok::eof)) { + Blocks.push_back(parseBlockContent()); + + // Skip extra newlines after paragraph end. + while (Tok.is(tok::newline)) + consumeToken(); + } + return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); +} + +} // end namespace comments +} // end namespace clang diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp new file mode 100644 index 000000000000..c39ee573ef6f --- /dev/null +++ b/lib/AST/CommentSema.cpp @@ -0,0 +1,739 @@ +//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentDiagnostic.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr, + DiagnosticsEngine &Diags, const CommandTraits &Traits) : + Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits), + ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) { +} + +void Sema::setDecl(const Decl *D) { + if (!D) + return; + + ThisDeclInfo = new (Allocator) DeclInfo; + ThisDeclInfo->ThisDecl = D; + ThisDeclInfo->IsFilled = false; +} + +ParagraphComment *Sema::actOnParagraphComment( + ArrayRef<InlineContentComment *> Content) { + return new (Allocator) ParagraphComment(Content); +} + +BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name); +} + +void Sema::actOnBlockCommandArgs(BlockCommandComment *Command, + ArrayRef<BlockCommandComment::Argument> Args) { + Command->setArgs(Args); +} + +void Sema::actOnBlockCommandFinish(BlockCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); + checkBlockCommandDuplicate(Command); + checkReturnsCommand(Command); +} + +ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + ParamCommandComment *Command = + new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name); + + if (!isFunctionDecl()) + Diag(Command->getLocation(), + diag::warn_doc_param_not_attached_to_a_function_decl) + << Command->getCommandNameRange(); + + return Command; +} + +void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + ParamCommandComment::PassDirection Direction; + std::string ArgLower = Arg.lower(); + // TODO: optimize: lower Name first (need an API in SmallString for that), + // after that StringSwitch. + if (ArgLower == "[in]") + Direction = ParamCommandComment::In; + else if (ArgLower == "[out]") + Direction = ParamCommandComment::Out; + else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") + Direction = ParamCommandComment::InOut; + else { + // Remove spaces. + std::string::iterator O = ArgLower.begin(); + for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end(); + I != E; ++I) { + const char C = *I; + if (C != ' ' && C != '\n' && C != '\r' && + C != '\t' && C != '\v' && C != '\f') + *O++ = C; + } + ArgLower.resize(O - ArgLower.begin()); + + bool RemovingWhitespaceHelped = false; + if (ArgLower == "[in]") { + Direction = ParamCommandComment::In; + RemovingWhitespaceHelped = true; + } else if (ArgLower == "[out]") { + Direction = ParamCommandComment::Out; + RemovingWhitespaceHelped = true; + } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") { + Direction = ParamCommandComment::InOut; + RemovingWhitespaceHelped = true; + } else { + Direction = ParamCommandComment::In; + RemovingWhitespaceHelped = false; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + if (RemovingWhitespaceHelped) + Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction) + << ArgRange + << FixItHint::CreateReplacement( + ArgRange, + ParamCommandComment::getDirectionAsString(Direction)); + else + Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) + << ArgRange; + } + Command->setDirection(Direction, /* Explicit = */ true); +} + +void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + // Parser will not feed us more arguments than needed. + assert(Command->getNumArgs() == 0); + + if (!Command->isDirectionExplicit()) { + // User didn't provide a direction argument. + Command->setDirection(ParamCommandComment::In, /* Explicit = */ false); + } + typedef BlockCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + Command->setArgs(llvm::makeArrayRef(A, 1)); + + if (!isFunctionDecl()) { + // We already warned that this \\param is not attached to a function decl. + return; + } + + ArrayRef<const ParmVarDecl *> ParamVars = getParamVars(); + + // Check that referenced parameter name is in the function decl. + const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars); + if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) { + Command->setParamIndex(ResolvedParamIndex); + if (ParamVarDocs[ResolvedParamIndex]) { + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_param_duplicate) + << Arg << ArgRange; + ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex]; + Diag(PrevCommand->getLocation(), diag::note_doc_param_previous) + << PrevCommand->getParamNameRange(); + } + ParamVarDocs[ResolvedParamIndex] = Command; + return; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_param_not_found) + << Arg << ArgRange; + + // No parameters -- can't suggest a correction. + if (ParamVars.size() == 0) + return; + + unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex; + if (ParamVars.size() == 1) { + // If function has only one parameter then only that parameter + // can be documented. + CorrectedParamIndex = 0; + } else { + // Do typo correction. + CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars); + } + if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) { + const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex]; + if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier()) + Diag(ArgLocBegin, diag::note_doc_param_name_suggestion) + << CorrectedII->getName() + << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName()); + } + + return; +} + +void Sema::actOnParamCommandFinish(ParamCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); +} + +TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + TParamCommandComment *Command = + new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name); + + if (!isTemplateOrSpecialization()) + Diag(Command->getLocation(), + diag::warn_doc_tparam_not_attached_to_a_template_decl) + << Command->getCommandNameRange(); + + return Command; +} + +void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + // Parser will not feed us more arguments than needed. + assert(Command->getNumArgs() == 0); + + typedef BlockCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + Command->setArgs(llvm::makeArrayRef(A, 1)); + + if (!isTemplateOrSpecialization()) { + // We already warned that this \\tparam is not attached to a template decl. + return; + } + + const TemplateParameterList *TemplateParameters = + ThisDeclInfo->TemplateParameters; + SmallVector<unsigned, 2> Position; + if (resolveTParamReference(Arg, TemplateParameters, &Position)) { + Command->setPosition(copyArray(llvm::makeArrayRef(Position))); + llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt = + TemplateParameterDocs.find(Arg); + if (PrevCommandIt != TemplateParameterDocs.end()) { + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate) + << Arg << ArgRange; + TParamCommandComment *PrevCommand = PrevCommandIt->second; + Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous) + << PrevCommand->getParamNameRange(); + } + TemplateParameterDocs[Arg] = Command; + return; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_not_found) + << Arg << ArgRange; + + if (!TemplateParameters || TemplateParameters->size() == 0) + return; + + StringRef CorrectedName; + if (TemplateParameters->size() == 1) { + const NamedDecl *Param = TemplateParameters->getParam(0); + const IdentifierInfo *II = Param->getIdentifier(); + if (II) + CorrectedName = II->getName(); + } else { + CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters); + } + + if (!CorrectedName.empty()) { + Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion) + << CorrectedName + << FixItHint::CreateReplacement(ArgRange, CorrectedName); + } + + return; +} + +void Sema::actOnTParamCommandFinish(TParamCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); +} + +InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, + SourceLocation CommandLocEnd, + StringRef CommandName) { + ArrayRef<InlineCommandComment::Argument> Args; + return new (Allocator) InlineCommandComment( + CommandLocBegin, + CommandLocEnd, + CommandName, + getInlineCommandRenderKind(CommandName), + Args); +} + +InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, + SourceLocation CommandLocEnd, + StringRef CommandName, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + typedef InlineCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + + return new (Allocator) InlineCommandComment( + CommandLocBegin, + CommandLocEnd, + CommandName, + getInlineCommandRenderKind(CommandName), + llvm::makeArrayRef(A, 1)); +} + +InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + ArrayRef<InlineCommandComment::Argument> Args; + return new (Allocator) InlineCommandComment( + LocBegin, LocEnd, Name, + InlineCommandComment::RenderNormal, + Args); +} + +TextComment *Sema::actOnText(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Text) { + return new (Allocator) TextComment(LocBegin, LocEnd, Text); +} + +VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc, + StringRef Name) { + return new (Allocator) VerbatimBlockComment( + Loc, + Loc.getLocWithOffset(1 + Name.size()), + Name); +} + +VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc, + StringRef Text) { + return new (Allocator) VerbatimBlockLineComment(Loc, Text); +} + +void Sema::actOnVerbatimBlockFinish( + VerbatimBlockComment *Block, + SourceLocation CloseNameLocBegin, + StringRef CloseName, + ArrayRef<VerbatimBlockLineComment *> Lines) { + Block->setCloseName(CloseName, CloseNameLocBegin); + Block->setLines(Lines); +} + +VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin, + StringRef Name, + SourceLocation TextBegin, + StringRef Text) { + return new (Allocator) VerbatimLineComment( + LocBegin, + TextBegin.getLocWithOffset(Text.size()), + Name, + TextBegin, + Text); +} + +HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin, + StringRef TagName) { + return new (Allocator) HTMLStartTagComment(LocBegin, TagName); +} + +void Sema::actOnHTMLStartTagFinish( + HTMLStartTagComment *Tag, + ArrayRef<HTMLStartTagComment::Attribute> Attrs, + SourceLocation GreaterLoc, + bool IsSelfClosing) { + Tag->setAttrs(Attrs); + Tag->setGreaterLoc(GreaterLoc); + if (IsSelfClosing) + Tag->setSelfClosing(); + else if (!isHTMLEndTagForbidden(Tag->getTagName())) + HTMLOpenTags.push_back(Tag); +} + +HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef TagName) { + HTMLEndTagComment *HET = + new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName); + if (isHTMLEndTagForbidden(TagName)) { + Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden) + << TagName << HET->getSourceRange(); + return HET; + } + + bool FoundOpen = false; + for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator + I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend(); + I != E; ++I) { + if ((*I)->getTagName() == TagName) { + FoundOpen = true; + break; + } + } + if (!FoundOpen) { + Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced) + << HET->getSourceRange(); + return HET; + } + + while (!HTMLOpenTags.empty()) { + const HTMLStartTagComment *HST = HTMLOpenTags.back(); + HTMLOpenTags.pop_back(); + StringRef LastNotClosedTagName = HST->getTagName(); + if (LastNotClosedTagName == TagName) + break; + + if (isHTMLEndTagOptional(LastNotClosedTagName)) + continue; + + bool OpenLineInvalid; + const unsigned OpenLine = SourceMgr.getPresumedLineNumber( + HST->getLocation(), + &OpenLineInvalid); + bool CloseLineInvalid; + const unsigned CloseLine = SourceMgr.getPresumedLineNumber( + HET->getLocation(), + &CloseLineInvalid); + + if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) + Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) + << HST->getTagName() << HET->getTagName() + << HST->getSourceRange() << HET->getSourceRange(); + else { + Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) + << HST->getTagName() << HET->getTagName() + << HST->getSourceRange(); + Diag(HET->getLocation(), diag::note_doc_html_end_tag) + << HET->getSourceRange(); + } + } + + return HET; +} + +FullComment *Sema::actOnFullComment( + ArrayRef<BlockContentComment *> Blocks) { + return new (Allocator) FullComment(Blocks, ThisDeclInfo); +} + +void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) { + ParagraphComment *Paragraph = Command->getParagraph(); + if (Paragraph->isWhitespace()) { + SourceLocation DiagLoc; + if (Command->getNumArgs() > 0) + DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd(); + if (!DiagLoc.isValid()) + DiagLoc = Command->getCommandNameRange().getEnd(); + Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph) + << Command->getCommandName() + << Command->getSourceRange(); + } +} + +void Sema::checkReturnsCommand(const BlockCommandComment *Command) { + if (!Traits.isReturnsCommand(Command->getCommandName())) + return; + if (isFunctionDecl()) { + if (ThisDeclInfo->ResultType->isVoidType()) { + unsigned DiagKind; + switch (ThisDeclInfo->ThisDecl->getKind()) { + default: + if (ThisDeclInfo->IsObjCMethod) + DiagKind = 3; + else + DiagKind = 0; + break; + case Decl::CXXConstructor: + DiagKind = 1; + break; + case Decl::CXXDestructor: + DiagKind = 2; + break; + } + Diag(Command->getLocation(), + diag::warn_doc_returns_attached_to_a_void_function) + << Command->getCommandName() + << DiagKind + << Command->getSourceRange(); + } + return; + } + Diag(Command->getLocation(), + diag::warn_doc_returns_not_attached_to_a_function_decl) + << Command->getCommandName() + << Command->getSourceRange(); +} + +void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) { + StringRef Name = Command->getCommandName(); + const BlockCommandComment *PrevCommand = NULL; + if (Traits.isBriefCommand(Name)) { + if (!BriefCommand) { + BriefCommand = Command; + return; + } + PrevCommand = BriefCommand; + } else if (Traits.isReturnsCommand(Name)) { + if (!ReturnsCommand) { + ReturnsCommand = Command; + return; + } + PrevCommand = ReturnsCommand; + } else { + // We don't want to check this command for duplicates. + return; + } + Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate) + << Name + << Command->getSourceRange(); + if (Name == PrevCommand->getCommandName()) + Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous) + << PrevCommand->getCommandName() + << Command->getSourceRange(); + else + Diag(PrevCommand->getLocation(), + diag::note_doc_block_command_previous_alias) + << PrevCommand->getCommandName() + << Name; +} + +bool Sema::isFunctionDecl() { + if (!ThisDeclInfo) + return false; + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->getKind() == DeclInfo::FunctionKind; +} + +bool Sema::isTemplateOrSpecialization() { + if (!ThisDeclInfo) + return false; + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate; +} + +ArrayRef<const ParmVarDecl *> Sema::getParamVars() { + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->ParamVars; +} + +void Sema::inspectThisDecl() { + ThisDeclInfo->fill(); + ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL); +} + +unsigned Sema::resolveParmVarReference(StringRef Name, + ArrayRef<const ParmVarDecl *> ParamVars) { + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { + const IdentifierInfo *II = ParamVars[i]->getIdentifier(); + if (II && II->getName() == Name) + return i; + } + return ParamCommandComment::InvalidParamIndex; +} + +namespace { +class SimpleTypoCorrector { + StringRef Typo; + const unsigned MaxEditDistance; + + const NamedDecl *BestDecl; + unsigned BestEditDistance; + unsigned BestIndex; + unsigned NextIndex; + +public: + SimpleTypoCorrector(StringRef Typo) : + Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3), + BestDecl(NULL), BestEditDistance(MaxEditDistance + 1), + BestIndex(0), NextIndex(0) + { } + + void addDecl(const NamedDecl *ND); + + const NamedDecl *getBestDecl() const { + if (BestEditDistance > MaxEditDistance) + return NULL; + + return BestDecl; + } + + unsigned getBestDeclIndex() const { + assert(getBestDecl()); + return BestIndex; + } +}; + +void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { + unsigned CurrIndex = NextIndex++; + + const IdentifierInfo *II = ND->getIdentifier(); + if (!II) + return; + + StringRef Name = II->getName(); + unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); + if (MinPossibleEditDistance > 0 && + Typo.size() / MinPossibleEditDistance < 3) + return; + + unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); + if (EditDistance < BestEditDistance) { + BestEditDistance = EditDistance; + BestDecl = ND; + BestIndex = CurrIndex; + } +} +} // unnamed namespace + +unsigned Sema::correctTypoInParmVarReference( + StringRef Typo, + ArrayRef<const ParmVarDecl *> ParamVars) { + SimpleTypoCorrector Corrector(Typo); + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) + Corrector.addDecl(ParamVars[i]); + if (Corrector.getBestDecl()) + return Corrector.getBestDeclIndex(); + else + return ParamCommandComment::InvalidParamIndex;; +} + +namespace { +bool ResolveTParamReferenceHelper( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + const IdentifierInfo *II = Param->getIdentifier(); + if (II && II->getName() == Name) { + Position->push_back(i); + return true; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) { + Position->push_back(i); + if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), + Position)) + return true; + Position->pop_back(); + } + } + return false; +} +} // unnamed namespace + +bool Sema::resolveTParamReference( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + Position->clear(); + if (!TemplateParameters) + return false; + + return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); +} + +namespace { +void CorrectTypoInTParamReferenceHelper( + const TemplateParameterList *TemplateParameters, + SimpleTypoCorrector &Corrector) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + Corrector.addDecl(Param); + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) + CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), + Corrector); + } +} +} // unnamed namespace + +StringRef Sema::correctTypoInTParamReference( + StringRef Typo, + const TemplateParameterList *TemplateParameters) { + SimpleTypoCorrector Corrector(Typo); + CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); + if (const NamedDecl *ND = Corrector.getBestDecl()) { + const IdentifierInfo *II = ND->getIdentifier(); + assert(II && "SimpleTypoCorrector should not return this decl"); + return II->getName(); + } + return StringRef(); +} + +InlineCommandComment::RenderKind +Sema::getInlineCommandRenderKind(StringRef Name) const { + assert(Traits.isInlineCommand(Name)); + + return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name) + .Case("b", InlineCommandComment::RenderBold) + .Cases("c", "p", InlineCommandComment::RenderMonospaced) + .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized) + .Default(InlineCommandComment::RenderNormal); +} + +bool Sema::isHTMLEndTagOptional(StringRef Name) { + return llvm::StringSwitch<bool>(Name) + .Case("p", true) + .Case("li", true) + .Case("dt", true) + .Case("dd", true) + .Case("tr", true) + .Case("th", true) + .Case("td", true) + .Case("thead", true) + .Case("tfoot", true) + .Case("tbody", true) + .Case("colgroup", true) + .Default(false); +} + +bool Sema::isHTMLEndTagForbidden(StringRef Name) { + return llvm::StringSwitch<bool>(Name) + .Case("br", true) + .Case("hr", true) + .Case("img", true) + .Case("col", true) + .Default(false); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 53032bc64d61..d5b0be3ba4b1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -66,32 +66,6 @@ static llvm::Optional<Visibility> getVisibilityOf(const Decl *D) { typedef NamedDecl::LinkageInfo LinkageInfo; -namespace { -/// Flags controlling the computation of linkage and visibility. -struct LVFlags { - const bool ConsiderGlobalVisibility; - const bool ConsiderVisibilityAttributes; - const bool ConsiderTemplateParameterTypes; - - LVFlags() : ConsiderGlobalVisibility(true), - ConsiderVisibilityAttributes(true), - ConsiderTemplateParameterTypes(true) { - } - - LVFlags(bool Global, bool Attributes, bool Parameters) : - ConsiderGlobalVisibility(Global), - ConsiderVisibilityAttributes(Attributes), - ConsiderTemplateParameterTypes(Parameters) { - } - - /// \brief Returns a set of flags that is only useful for computing the - /// linkage, not the visibility, of a declaration. - static LVFlags CreateOnlyDeclLinkage() { - return LVFlags(false, false, false); - } -}; -} // end anonymous namespace - static LinkageInfo getLVForType(QualType T) { std::pair<Linkage,Visibility> P = T->getLinkageAndVisibility(); return LinkageInfo(P.first, P.second, T->isVisibilityExplicit()); @@ -131,13 +105,13 @@ getLVForTemplateParameterList(const TemplateParameterList *Params) { } /// getLVForDecl - Get the linkage and visibility for the given declaration. -static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags F); +static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate); /// \brief Get the most restrictive linkage for the types and /// declarations in the given template argument list. static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, unsigned NumArgs, - LVFlags &F) { + bool OnlyTemplate) { LinkageInfo LV(ExternalLinkage, DefaultVisibility, false); for (unsigned I = 0; I != NumArgs; ++I) { @@ -148,7 +122,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, break; case TemplateArgument::Type: - LV.merge(getLVForType(Args[I].getAsType())); + LV.mergeWithMin(getLVForType(Args[I].getAsType())); break; case TemplateArgument::Declaration: @@ -156,7 +130,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, // arguments, valid only in C++0x. if (Decl *D = Args[I].getAsDecl()) { if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) - LV = merge(LV, getLVForDecl(ND, F)); + LV.mergeWithMin(getLVForDecl(ND, OnlyTemplate)); } break; @@ -164,13 +138,13 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, case TemplateArgument::TemplateExpansion: if (TemplateDecl *Template = Args[I].getAsTemplateOrTemplatePattern().getAsTemplateDecl()) - LV.merge(getLVForDecl(Template, F)); + LV.mergeWithMin(getLVForDecl(Template, OnlyTemplate)); break; case TemplateArgument::Pack: LV.mergeWithMin(getLVForTemplateArgumentList(Args[I].pack_begin(), Args[I].pack_size(), - F)); + OnlyTemplate)); break; } } @@ -180,21 +154,50 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, static LinkageInfo getLVForTemplateArgumentList(const TemplateArgumentList &TArgs, - LVFlags &F) { - return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), F); + bool OnlyTemplate) { + return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), OnlyTemplate); } -static bool shouldConsiderTemplateLV(const FunctionDecl *fn, +static bool shouldConsiderTemplateVis(const FunctionDecl *fn, const FunctionTemplateSpecializationInfo *spec) { - return !(spec->isExplicitSpecialization() && - fn->hasAttr<VisibilityAttr>()); + return !fn->hasAttr<VisibilityAttr>() || spec->isExplicitSpecialization(); } -static bool shouldConsiderTemplateLV(const ClassTemplateSpecializationDecl *d) { - return !(d->isExplicitSpecialization() && d->hasAttr<VisibilityAttr>()); +static bool +shouldConsiderTemplateVis(const ClassTemplateSpecializationDecl *d) { + return !d->hasAttr<VisibilityAttr>() || d->isExplicitSpecialization(); } -static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { +static bool useInlineVisibilityHidden(const NamedDecl *D) { + // FIXME: we should warn if -fvisibility-inlines-hidden is used with c. + const LangOptions &Opts = D->getASTContext().getLangOpts(); + if (!Opts.CPlusPlus || !Opts.InlineVisibilityHidden) + return false; + + const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); + if (!FD) + return false; + + TemplateSpecializationKind TSK = TSK_Undeclared; + if (FunctionTemplateSpecializationInfo *spec + = FD->getTemplateSpecializationInfo()) { + TSK = spec->getTemplateSpecializationKind(); + } else if (MemberSpecializationInfo *MSI = + FD->getMemberSpecializationInfo()) { + TSK = MSI->getTemplateSpecializationKind(); + } + + const FunctionDecl *Def = 0; + // InlineVisibilityHidden only applies to definitions, and + // isInlined() only gives meaningful answers on definitions + // anyway. + return TSK != TSK_ExplicitInstantiationDeclaration && + TSK != TSK_ExplicitInstantiationDefinition && + FD->hasBody(Def) && Def->isInlined(); +} + +static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, + bool OnlyTemplate) { assert(D->getDeclContext()->getRedeclContext()->isFileContext() && "Not a name having namespace scope"); ASTContext &Context = D->getASTContext(); @@ -271,11 +274,10 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // scope and no storage-class specifier, its linkage is // external. LinkageInfo LV; - LV.mergeVisibility(Context.getLangOpts().getVisibilityMode()); - if (F.ConsiderVisibilityAttributes) { + if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) { - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } else { // If we're declared in a namespace with a visibility attribute, // use that namespace's visibility, but don't call it explicit. @@ -285,13 +287,21 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); if (!ND) continue; if (llvm::Optional<Visibility> Vis = ND->getExplicitVisibility()) { - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); break; } } } } + if (!OnlyTemplate) { + LV.mergeVisibility(Context.getLangOpts().getVisibilityMode()); + // If we're paying attention to global visibility, apply + // -finline-visibility-hidden if this is an inline method. + if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D)) + LV.mergeVisibility(HiddenVisibility, true); + } + // C++ [basic.link]p4: // A name having namespace scope has external linkage if it is the @@ -325,11 +335,11 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { LinkageInfo TypeLV = getLVForType(Var->getType()); if (TypeLV.linkage() != ExternalLinkage) return LinkageInfo::uniqueExternal(); - LV.mergeVisibilityWithMin(TypeLV); + LV.mergeVisibility(TypeLV); } if (Var->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); + LV.mergeVisibility(HiddenVisibility, true); if (!Context.getLangOpts().CPlusPlus && (Var->getStorageClass() == SC_Extern || @@ -345,7 +355,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const VarDecl *PrevVar = Var->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(PrevVar, F); + LinkageInfo PrevLV = getLVForDecl(PrevVar, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -359,7 +369,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // just too painful to make work. if (Function->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); + LV.mergeVisibility(HiddenVisibility, true); // C99 6.2.2p5: // If the declaration of an identifier for a function has no @@ -380,7 +390,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const FunctionDecl *PrevFunc = Function->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(PrevFunc, F); + LinkageInfo PrevLV = getLVForDecl(PrevFunc, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -399,11 +409,16 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // this is an explicit specialization with a visibility attribute. if (FunctionTemplateSpecializationInfo *specInfo = Function->getTemplateSpecializationInfo()) { - if (shouldConsiderTemplateLV(Function, specInfo)) { - LV.merge(getLVForDecl(specInfo->getTemplate(), - LVFlags::CreateOnlyDeclLinkage())); - const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments; - LV.mergeWithMin(getLVForTemplateArgumentList(templateArgs, F)); + LinkageInfo TempLV = getLVForDecl(specInfo->getTemplate(), true); + const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments; + LinkageInfo ArgsLV = getLVForTemplateArgumentList(templateArgs, + OnlyTemplate); + if (shouldConsiderTemplateVis(Function, specInfo)) { + LV.mergeWithMin(TempLV); + LV.mergeWithMin(ArgsLV); + } else { + LV.mergeLinkage(TempLV); + LV.mergeLinkage(ArgsLV); } } @@ -422,20 +437,26 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // linkage of the template and template arguments. if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(Tag)) { - if (shouldConsiderTemplateLV(spec)) { - // From the template. - LV.merge(getLVForDecl(spec->getSpecializedTemplate(), - LVFlags::CreateOnlyDeclLinkage())); - - // The arguments at which the template was instantiated. - const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); - LV.mergeWithMin(getLVForTemplateArgumentList(TemplateArgs, F)); + // From the template. + LinkageInfo TempLV = getLVForDecl(spec->getSpecializedTemplate(), true); + + // The arguments at which the template was instantiated. + const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + if (shouldConsiderTemplateVis(spec)) { + LV.mergeWithMin(TempLV); + LV.mergeWithMin(ArgsLV); + } else { + LV.mergeLinkage(TempLV); + LV.mergeLinkage(ArgsLV); } } // - an enumerator belonging to an enumeration with external linkage; } else if (isa<EnumConstantDecl>(D)) { - LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()), F); + LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()), + OnlyTemplate); if (!isExternalLinkage(EnumLV.linkage())) return LinkageInfo::none(); LV.merge(EnumLV); @@ -443,9 +464,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // - a template, unless it is a function template that has // internal linkage (Clause 14); } else if (const TemplateDecl *temp = dyn_cast<TemplateDecl>(D)) { - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters())); - + LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters())); // - a namespace (7.3), unless it is declared within an unnamed // namespace. } else if (isa<NamespaceDecl>(D) && !D->isInAnonymousNamespace()) { @@ -469,7 +488,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { return LV; } -static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { +static LinkageInfo getLVForClassMember(const NamedDecl *D, bool OnlyTemplate) { // Only certain class members have linkage. Note that fields don't // really have linkage, but it's convenient to say they do for the // purposes of calculating linkage of pointer-to-data-member @@ -482,53 +501,32 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { return LinkageInfo::none(); LinkageInfo LV; - LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode()); - bool DHasExplicitVisibility = false; // If we have an explicit visibility attribute, merge that in. - if (F.ConsiderVisibilityAttributes) { - if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) { + if (!OnlyTemplate) { + if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) LV.mergeVisibility(*Vis, true); - - DHasExplicitVisibility = true; - } - } - // Ignore both global visibility and attributes when computing our - // parent's visibility if we already have an explicit one. - LVFlags ClassF = DHasExplicitVisibility ? - LVFlags::CreateOnlyDeclLinkage() : F; - - // If we're paying attention to global visibility, apply - // -finline-visibility-hidden if this is an inline method. - // - // Note that we do this before merging information about - // the class visibility. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { - TemplateSpecializationKind TSK = TSK_Undeclared; - if (FunctionTemplateSpecializationInfo *spec - = MD->getTemplateSpecializationInfo()) { - TSK = spec->getTemplateSpecializationKind(); - } else if (MemberSpecializationInfo *MSI = - MD->getMemberSpecializationInfo()) { - TSK = MSI->getTemplateSpecializationKind(); - } - - const FunctionDecl *Def = 0; - // InlineVisibilityHidden only applies to definitions, and - // isInlined() only gives meaningful answers on definitions - // anyway. - if (TSK != TSK_ExplicitInstantiationDeclaration && - TSK != TSK_ExplicitInstantiationDefinition && - F.ConsiderGlobalVisibility && - !LV.visibilityExplicit() && - MD->getASTContext().getLangOpts().InlineVisibilityHidden && - MD->hasBody(Def) && Def->isInlined()) + // If we're paying attention to global visibility, apply + // -finline-visibility-hidden if this is an inline method. + // + // Note that we do this before merging information about + // the class visibility. + if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D)) LV.mergeVisibility(HiddenVisibility, true); } - // Class members only have linkage if their class has external - // linkage. - LV.merge(getLVForDecl(cast<RecordDecl>(D->getDeclContext()), ClassF)); + // If this class member has an explicit visibility attribute, the only + // thing that can change its visibility is the template arguments, so + // only look for them when processing the class. + bool ClassOnlyTemplate = LV.visibilityExplicit() ? true : OnlyTemplate; + + // If this member has an visibility attribute, ClassF will exclude + // attributes on the class or command line options, keeping only information + // about the template instantiation. If the member has no visibility + // attributes, mergeWithMin behaves like merge, so in both cases mergeWithMin + // produces the desired result. + LV.mergeWithMin(getLVForDecl(cast<RecordDecl>(D->getDeclContext()), + ClassOnlyTemplate)); if (!isExternalLinkage(LV.linkage())) return LinkageInfo::none(); @@ -536,6 +534,9 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { if (LV.linkage() == UniqueExternalLinkage) return LinkageInfo::uniqueExternal(); + if (!OnlyTemplate) + LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode()); + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { // If the type of the function uses a type with unique-external // linkage, it's not legally usable from outside this translation unit. @@ -546,12 +547,20 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { // the template parameters and arguments. if (FunctionTemplateSpecializationInfo *spec = MD->getTemplateSpecializationInfo()) { - if (shouldConsiderTemplateLV(MD, spec)) { - LV.mergeWithMin(getLVForTemplateArgumentList(*spec->TemplateArguments, - F)); - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList( - spec->getTemplate()->getTemplateParameters())); + const TemplateArgumentList &TemplateArgs = *spec->TemplateArguments; + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + TemplateParameterList *TemplateParams = + spec->getTemplate()->getTemplateParameters(); + LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams); + if (shouldConsiderTemplateVis(MD, spec)) { + LV.mergeWithMin(ArgsLV); + if (!OnlyTemplate) + LV.mergeWithMin(ParamsLV); + } else { + LV.mergeLinkage(ArgsLV); + if (!OnlyTemplate) + LV.mergeLinkage(ParamsLV); } } @@ -561,14 +570,22 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { - if (shouldConsiderTemplateLV(spec)) { - // Merge template argument/parameter information for member - // class template specializations. - LV.mergeWithMin(getLVForTemplateArgumentList(spec->getTemplateArgs(), - F)); - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList( - spec->getSpecializedTemplate()->getTemplateParameters())); + // Merge template argument/parameter information for member + // class template specializations. + const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + TemplateParameterList *TemplateParams = + spec->getSpecializedTemplate()->getTemplateParameters(); + LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams); + if (shouldConsiderTemplateVis(spec)) { + LV.mergeWithMin(ArgsLV); + if (!OnlyTemplate) + LV.mergeWithMin(ParamsLV); + } else { + LV.mergeLinkage(ArgsLV); + if (!OnlyTemplate) + LV.mergeLinkage(ParamsLV); } } @@ -579,8 +596,7 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { LinkageInfo TypeLV = getLVForType(VD->getType()); if (TypeLV.linkage() != ExternalLinkage) LV.mergeLinkage(UniqueExternalLinkage); - if (!LV.visibilityExplicit()) - LV.mergeVisibility(TypeLV); + LV.mergeVisibility(TypeLV); } return LV; @@ -636,18 +652,17 @@ void NamedDecl::ClearLinkageCache() { Linkage NamedDecl::getLinkage() const { if (HasCachedLinkage) { assert(Linkage(CachedLinkage) == - getLVForDecl(this, LVFlags::CreateOnlyDeclLinkage()).linkage()); + getLVForDecl(this, true).linkage()); return Linkage(CachedLinkage); } - CachedLinkage = getLVForDecl(this, - LVFlags::CreateOnlyDeclLinkage()).linkage(); + CachedLinkage = getLVForDecl(this, true).linkage(); HasCachedLinkage = 1; return Linkage(CachedLinkage); } LinkageInfo NamedDecl::getLinkageAndVisibility() const { - LinkageInfo LI = getLVForDecl(this, LVFlags()); + LinkageInfo LI = getLVForDecl(this, false); assert(!HasCachedLinkage || Linkage(CachedLinkage) == LI.linkage()); HasCachedLinkage = 1; CachedLinkage = LI.linkage(); @@ -656,9 +671,19 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const { llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { // Use the most recent declaration of a variable. - if (const VarDecl *var = dyn_cast<VarDecl>(this)) - return getVisibilityOf(var->getMostRecentDecl()); + if (const VarDecl *Var = dyn_cast<VarDecl>(this)) { + if (llvm::Optional<Visibility> V = + getVisibilityOf(Var->getMostRecentDecl())) + return V; + + if (Var->isStaticDataMember()) { + VarDecl *InstantiatedFrom = Var->getInstantiatedFromStaticDataMember(); + if (InstantiatedFrom) + return getVisibilityOf(InstantiatedFrom); + } + return llvm::Optional<Visibility>(); + } // Use the most recent declaration of a function, and also handle // function template specializations. if (const FunctionDecl *fn = dyn_cast<FunctionDecl>(this)) { @@ -685,6 +710,10 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { if (llvm::Optional<Visibility> V = getVisibilityOf(this)) return V; + // The visibility of a template is stored in the templated decl. + if (const TemplateDecl *TD = dyn_cast<TemplateDecl>(this)) + return getVisibilityOf(TD->getTemplatedDecl()); + // If there wasn't explicit visibility there, and this is a // specialization of a class template, check for visibility // on the pattern. @@ -703,7 +732,7 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { return llvm::Optional<Visibility>(); } -static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { +static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate) { // Objective-C: treat all Objective-C declarations as having external // linkage. switch (D->getKind()) { @@ -738,11 +767,12 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { if (isa<ParmVarDecl>(ContextDecl)) DC = ContextDecl->getDeclContext()->getRedeclContext(); else - return getLVForDecl(cast<NamedDecl>(ContextDecl), Flags); + return getLVForDecl(cast<NamedDecl>(ContextDecl), + OnlyTemplate); } if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC)) - return getLVForDecl(ND, Flags); + return getLVForDecl(ND, OnlyTemplate); return LinkageInfo::external(); } @@ -753,7 +783,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { // Handle linkage for namespace-scope names. if (D->getDeclContext()->getRedeclContext()->isFileContext()) - return getLVForNamespaceScopeDecl(D, Flags); + return getLVForNamespaceScopeDecl(D, OnlyTemplate); // C++ [basic.link]p5: // In addition, a member function, static data member, a named @@ -763,7 +793,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { // purposes (7.1.3), has external linkage if the name of the class // has external linkage. if (D->getDeclContext()->isRecord()) - return getLVForClassMember(D, Flags); + return getLVForClassMember(D, OnlyTemplate); // C++ [basic.link]p6: // The name of a function declared in block scope and the name of @@ -783,13 +813,13 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { return LinkageInfo::uniqueExternal(); LinkageInfo LV; - if (Flags.ConsiderVisibilityAttributes) { + if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = Function->getExplicitVisibility()) - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } if (const FunctionDecl *Prev = Function->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(Prev, Flags); + LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -806,14 +836,14 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { LinkageInfo LV; if (Var->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); - else if (Flags.ConsiderVisibilityAttributes) { + LV.mergeVisibility(HiddenVisibility, true); + else if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = Var->getExplicitVisibility()) - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } if (const VarDecl *Prev = Var->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(Prev, Flags); + LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -881,9 +911,7 @@ std::string NamedDecl::getQualifiedNameAsString(const PrintingPolicy &P) const { for (unsigned i = 0; i < NumParams; ++i) { if (i) OS << ", "; - std::string Param; - FD->getParamDecl(i)->getType().getAsStringInternal(Param, P); - OS << Param; + OS << FD->getParamDecl(i)->getType().stream(P); } if (FT->isVariadic()) { @@ -1672,6 +1700,13 @@ void FunctionDecl::setPure(bool P) { Parent->markedVirtualFunctionPure(); } +void FunctionDecl::setConstexpr(bool IC) { + IsConstexpr = IC; + CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(this); + if (IC && CD) + CD->getParent()->markedConstructorConstexpr(CD); +} + bool FunctionDecl::isMain() const { const TranslationUnitDecl *tunit = dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext()); @@ -2446,15 +2481,15 @@ FieldDecl *FieldDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, - bool HasInit) { + InClassInitStyle InitStyle) { return new (C) FieldDecl(Decl::Field, DC, StartLoc, IdLoc, Id, T, TInfo, - BW, Mutable, HasInit); + BW, Mutable, InitStyle); } FieldDecl *FieldDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(FieldDecl)); return new (Mem) FieldDecl(Field, 0, SourceLocation(), SourceLocation(), - 0, QualType(), 0, 0, false, false); + 0, QualType(), 0, 0, false, ICIS_NoInit); } bool FieldDecl::isAnonymousStructOrUnion() const { @@ -2483,15 +2518,15 @@ unsigned FieldDecl::getFieldIndex() const { for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++Index) { - (*I)->CachedFieldIndex = Index + 1; + I->CachedFieldIndex = Index + 1; if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are ignored. - if (getASTContext().ZeroBitfieldFollowsNonBitfield((*I), LastFD)) { + if (getASTContext().ZeroBitfieldFollowsNonBitfield(*I, LastFD)) { --Index; continue; } - LastFD = (*I); + LastFD = *I; } } @@ -2505,11 +2540,16 @@ SourceRange FieldDecl::getSourceRange() const { return DeclaratorDecl::getSourceRange(); } +void FieldDecl::setBitWidth(Expr *Width) { + assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && + "bit width or initializer already set"); + InitializerOrBitWidth.setPointer(Width); +} + void FieldDecl::setInClassInitializer(Expr *Init) { - assert(!InitializerOrBitWidth.getPointer() && + assert(!InitializerOrBitWidth.getPointer() && hasInClassInitializer() && "bit width or initializer already set"); InitializerOrBitWidth.setPointer(Init); - InitializerOrBitWidth.setInt(0); } //===----------------------------------------------------------------------===// diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 47a0d25093d5..f9ce46def5b9 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -411,23 +411,32 @@ AvailabilityResult Decl::getAvailability(std::string *Message) const { bool Decl::canBeWeakImported(bool &IsDefinition) const { IsDefinition = false; + + // Variables, if they aren't definitions. if (const VarDecl *Var = dyn_cast<VarDecl>(this)) { if (!Var->hasExternalStorage() || Var->getInit()) { IsDefinition = true; return false; } + return true; + + // Functions, if they aren't definitions. } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) { if (FD->hasBody()) { IsDefinition = true; return false; } - } else if (isa<ObjCPropertyDecl>(this) || isa<ObjCMethodDecl>(this)) - return false; - else if (!(getASTContext().getLangOpts().ObjCNonFragileABI && - isa<ObjCInterfaceDecl>(this))) - return false; + return true; - return true; + // Objective-C classes, if this is the non-fragile runtime. + } else if (isa<ObjCInterfaceDecl>(this) && + getASTContext().getLangOpts().ObjCRuntime.hasWeakClassImport()) { + return true; + + // Nothing else. + } else { + return false; + } } bool Decl::isWeakImported() const { @@ -974,10 +983,6 @@ DeclContext::decl_iterator DeclContext::noload_decls_begin() const { return decl_iterator(FirstDecl); } -DeclContext::decl_iterator DeclContext::noload_decls_end() const { - return decl_iterator(); -} - DeclContext::decl_iterator DeclContext::decls_begin() const { if (hasExternalLexicalStorage()) LoadLexicalDeclsFromExternalStorage(); @@ -985,13 +990,6 @@ DeclContext::decl_iterator DeclContext::decls_begin() const { return decl_iterator(FirstDecl); } -DeclContext::decl_iterator DeclContext::decls_end() const { - if (hasExternalLexicalStorage()) - LoadLexicalDeclsFromExternalStorage(); - - return decl_iterator(); -} - bool DeclContext::decls_empty() const { if (hasExternalLexicalStorage()) LoadLexicalDeclsFromExternalStorage(); @@ -1192,34 +1190,31 @@ DeclContext::lookup(DeclarationName Name) { return I->second.getLookupResult(); } -DeclContext::lookup_const_result -DeclContext::lookup(DeclarationName Name) const { - return const_cast<DeclContext*>(this)->lookup(Name); -} - void DeclContext::localUncachedLookup(DeclarationName Name, llvm::SmallVectorImpl<NamedDecl *> &Results) { Results.clear(); // If there's no external storage, just perform a normal lookup and copy // the results. - if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage()) { + if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage() && Name) { lookup_result LookupResults = lookup(Name); Results.insert(Results.end(), LookupResults.first, LookupResults.second); return; } // If we have a lookup table, check there first. Maybe we'll get lucky. - if (StoredDeclsMap *Map = LookupPtr.getPointer()) { - StoredDeclsMap::iterator Pos = Map->find(Name); - if (Pos != Map->end()) { - Results.insert(Results.end(), - Pos->second.getLookupResult().first, - Pos->second.getLookupResult().second); - return; + if (Name) { + if (StoredDeclsMap *Map = LookupPtr.getPointer()) { + StoredDeclsMap::iterator Pos = Map->find(Name); + if (Pos != Map->end()) { + Results.insert(Results.end(), + Pos->second.getLookupResult().first, + Pos->second.getLookupResult().second); + return; + } } } - + // Slow case: grovel through the declarations in our chain looking for // matches. for (Decl *D = FirstDecl; D; D = D->getNextDeclInContext()) { diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 114322b1a8a6..eec2e9d3cf74 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -43,13 +43,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), HasMutableFields(false), HasOnlyCMembers(true), + HasInClassInitializer(false), HasTrivialDefaultConstructor(true), HasConstexprNonCopyMoveConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), - DefaultedCopyConstructorIsConstexpr(true), - DefaultedMoveConstructorIsConstexpr(true), - HasConstexprDefaultConstructor(false), HasConstexprCopyConstructor(false), - HasConstexprMoveConstructor(false), HasTrivialCopyConstructor(true), + HasConstexprDefaultConstructor(false), HasTrivialCopyConstructor(true), HasTrivialMoveConstructor(true), HasTrivialCopyAssignment(true), HasTrivialMoveAssignment(true), HasTrivialDestructor(true), HasIrrelevantDestructor(true), @@ -62,6 +60,14 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) { } +CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { + return Bases.get(Definition->getASTContext().getExternalSource()); +} + +CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getVBasesSlowCase() const { + return VBases.get(Definition->getASTContext().getExternalSource()); +} + CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, CXXRecordDecl *PrevDecl) @@ -219,8 +225,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // In the definition of a constexpr constructor [...] // -- the class shall not have any virtual base classes data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; } else { // C++ [class.ctor]p5: // A default constructor is trivial [...] if: @@ -259,25 +263,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // default constructor is constexpr. if (!BaseClassDecl->hasConstexprDefaultConstructor()) data().DefaultedDefaultConstructorIsConstexpr = false; - - // C++11 [class.copy]p13: - // If the implicitly-defined constructor would satisfy the requirements - // of a constexpr constructor, the implicitly-defined constructor is - // constexpr. - // C++11 [dcl.constexpr]p4: - // -- every constructor involved in initializing [...] base class - // sub-objects shall be a constexpr constructor - if (!BaseClassDecl->hasConstexprCopyConstructor()) - data().DefaultedCopyConstructorIsConstexpr = false; - if (BaseClassDecl->hasDeclaredMoveConstructor() || - BaseClassDecl->needsImplicitMoveConstructor()) - // FIXME: If the implicit move constructor generated for the base class - // would be ill-formed, the implicit move constructor generated for the - // derived class calls the base class' copy constructor. - data().DefaultedMoveConstructorIsConstexpr &= - BaseClassDecl->hasConstexprMoveConstructor(); - else if (!BaseClassDecl->hasConstexprCopyConstructor()) - data().DefaultedMoveConstructorIsConstexpr = false; } // C++ [class.ctor]p3: @@ -359,8 +344,8 @@ GetBestOverloadCandidateSimple( if (Cands[Best].second.compatiblyIncludes(Cands[I].second)) Best = I; - for (unsigned I = 1; I != N; ++I) - if (Cands[Best].second.compatiblyIncludes(Cands[I].second)) + for (unsigned I = 0; I != N; ++I) + if (I != Best && Cands[Best].second.compatiblyIncludes(Cands[I].second)) return 0; return Cands[Best].first; @@ -469,6 +454,14 @@ void CXXRecordDecl::markedVirtualFunctionPure() { data().Abstract = true; } +void CXXRecordDecl::markedConstructorConstexpr(CXXConstructorDecl *CD) { + if (!CD->isCopyOrMoveConstructor()) + data().HasConstexprNonCopyMoveConstructor = true; + + if (CD->isDefaultConstructor()) + data().HasConstexprDefaultConstructor = true; +} + void CXXRecordDecl::addedMember(Decl *D) { if (!D->isImplicit() && !isa<FieldDecl>(D) && @@ -545,12 +538,8 @@ void CXXRecordDecl::addedMember(Decl *D) { } } else if (Constructor->isCopyConstructor()) { data().DeclaredCopyConstructor = true; - if (Constructor->isConstexpr()) - data().HasConstexprCopyConstructor = true; } else if (Constructor->isMoveConstructor()) { data().DeclaredMoveConstructor = true; - if (Constructor->isConstexpr()) - data().HasConstexprMoveConstructor = true; } else goto NotASpecialMember; return; @@ -607,9 +596,6 @@ NotASpecialMember:; // user-provided [...] if (UserProvided) data().HasTrivialCopyConstructor = false; - - if (Constructor->isConstexpr()) - data().HasConstexprCopyConstructor = true; } else if (Constructor->isMoveConstructor()) { data().UserDeclaredMoveConstructor = true; data().DeclaredMoveConstructor = true; @@ -619,9 +605,6 @@ NotASpecialMember:; // user-provided [...] if (UserProvided) data().HasTrivialMoveConstructor = false; - - if (Constructor->isConstexpr()) - data().HasConstexprMoveConstructor = true; } } if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) { @@ -663,19 +646,9 @@ NotASpecialMember:; // C++11 [class.dtor]p5: // A destructor is trivial if it is not user-provided and if // -- the destructor is not virtual. - if (DD->isUserProvided() || DD->isVirtual()) { + if (DD->isUserProvided() || DD->isVirtual()) data().HasTrivialDestructor = false; - // C++11 [dcl.constexpr]p1: - // The constexpr specifier shall be applied only to [...] the - // declaration of a static data member of a literal type. - // C++11 [basic.types]p10: - // A type is a literal type if it is [...] a class type that [...] has - // a trivial destructor. - data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; - } - + return; } @@ -792,7 +765,7 @@ NotASpecialMember:; // that does not explicitly have no lifetime makes the class a non-POD. // However, we delay setting PlainOldData to false in this case so that // Sema has a chance to diagnostic causes where the same class will be - // non-POD with Automatic Reference Counting but a POD without Instant Objects. + // non-POD with Automatic Reference Counting but a POD without ARC. // In this case, the class will become a non-POD class when we complete // the definition. ASTContext &Context = getASTContext(); @@ -818,17 +791,19 @@ NotASpecialMember:; data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer()) { - // C++0x [class]p5: + data().HasInClassInitializer = true; + + // C++11 [class]p5: // A default constructor is trivial if [...] no non-static data member // of its class has a brace-or-equal-initializer. data().HasTrivialDefaultConstructor = false; - // C++0x [dcl.init.aggr]p1: + // C++11 [dcl.init.aggr]p1: // An aggregate is a [...] class with [...] no // brace-or-equal-initializers for non-static data members. data().Aggregate = false; - // C++0x [class]p10: + // C++11 [class]p10: // A POD struct is [...] a trivial class. data().PlainOldData = false; } @@ -920,31 +895,15 @@ NotASpecialMember:; // -- every constructor involved in initializing non-static data // members [...] shall be a constexpr constructor if (!Field->hasInClassInitializer() && - !FieldRec->hasConstexprDefaultConstructor()) + !FieldRec->hasConstexprDefaultConstructor() && !isUnion()) // The standard requires any in-class initializer to be a constant // expression. We consider this to be a defect. data().DefaultedDefaultConstructorIsConstexpr = false; - - if (!FieldRec->hasConstexprCopyConstructor()) - data().DefaultedCopyConstructorIsConstexpr = false; - - if (FieldRec->hasDeclaredMoveConstructor() || - FieldRec->needsImplicitMoveConstructor()) - // FIXME: If the implicit move constructor generated for the member's - // class would be ill-formed, the implicit move constructor generated - // for this class calls the member's copy constructor. - data().DefaultedMoveConstructorIsConstexpr &= - FieldRec->hasConstexprMoveConstructor(); - else if (!FieldRec->hasConstexprCopyConstructor()) - data().DefaultedMoveConstructorIsConstexpr = false; } } else { // Base element type of field is a non-class type. - if (!T->isLiteralType()) { - data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; - } else if (!Field->hasInClassInitializer()) + if (!T->isLiteralType() || + (!Field->hasInClassInitializer() && !isUnion())) data().DefaultedDefaultConstructorIsConstexpr = false; } @@ -1018,7 +977,7 @@ static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) { /// Collect the visible conversions of a base class. /// -/// \param Base a base class of the class we're considering +/// \param Record a base class of the class we're considering /// \param InVirtual whether this base class is a virtual base (or a base /// of a virtual base) /// \param Access the access along the inheritance path to this base @@ -1050,8 +1009,10 @@ static void CollectVisibleConversions(ASTContext &Context, HiddenTypes = &HiddenTypesBuffer; for (UnresolvedSetIterator I = Cs.begin(), E = Cs.end(); I != E; ++I) { - bool Hidden = - !HiddenTypesBuffer.insert(GetConversionType(Context, I.getDecl())); + CanQualType ConvType(GetConversionType(Context, I.getDecl())); + bool Hidden = ParentHiddenTypes.count(ConvType); + if (!Hidden) + HiddenTypesBuffer.insert(ConvType); // If this conversion is hidden and we're in a virtual base, // remember that it's hidden along some inheritance path. @@ -1247,13 +1208,16 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { // Objective-C Automatic Reference Counting: // If a class has a non-static data member of Objective-C pointer // type (or array thereof), it is a non-POD type and its - // default constructor (if any), copy constructor, copy assignment - // operator, and destructor are non-trivial. + // default constructor (if any), copy constructor, move constructor, + // copy assignment operator, move assignment operator, and destructor are + // non-trivial. struct DefinitionData &Data = data(); Data.PlainOldData = false; Data.HasTrivialDefaultConstructor = false; Data.HasTrivialCopyConstructor = false; + Data.HasTrivialMoveConstructor = false; Data.HasTrivialCopyAssignment = false; + Data.HasTrivialMoveAssignment = false; Data.HasTrivialDestructor = false; Data.HasIrrelevantDestructor = false; } @@ -1316,6 +1280,55 @@ bool CXXRecordDecl::mayBeAbstract() const { void CXXMethodDecl::anchor() { } +static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, + const CXXMethodDecl *BaseMD) { + for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(), + E = DerivedMD->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + if (MD->getCanonicalDecl() == BaseMD->getCanonicalDecl()) + return true; + if (recursivelyOverrides(MD, BaseMD)) + return true; + } + return false; +} + +CXXMethodDecl * +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { + if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) + return this; + + // Lookup doesn't work for destructors, so handle them separately. + if (isa<CXXDestructorDecl>(this)) { + CXXMethodDecl *MD = RD->getDestructor(); + if (MD && recursivelyOverrides(MD, this)) + return MD; + return NULL; + } + + lookup_const_result Candidates = RD->lookup(getDeclName()); + for (NamedDecl * const * I = Candidates.first; I != Candidates.second; ++I) { + CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(*I); + if (!MD) + continue; + if (recursivelyOverrides(MD, this)) + return MD; + } + + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const RecordType *RT = I->getType()->getAs<RecordType>(); + if (!RT) + continue; + const CXXRecordDecl *Base = cast<CXXRecordDecl>(RT->getDecl()); + CXXMethodDecl *T = this->getCorrespondingMethodInClass(Base); + if (T) + return T; + } + + return NULL; +} + CXXMethodDecl * CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, @@ -1690,7 +1703,9 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const { return (getNumParams() == 0 && getType()->getAs<FunctionProtoType>()->isVariadic()) || (getNumParams() == 1) || - (getNumParams() > 1 && getParamDecl(1)->hasDefaultArg()); + (getNumParams() > 1 && + (getParamDecl(1)->hasDefaultArg() || + getParamDecl(1)->isParameterPack())); } bool CXXConstructorDecl::isSpecializationCopyingObject() const { @@ -1993,15 +2008,17 @@ StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StaticAssertLoc, Expr *AssertExpr, StringLiteral *Message, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, + bool Failed) { return new (C) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message, - RParenLoc); + RParenLoc, Failed); } StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(StaticAssertDecl)); - return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0,SourceLocation()); + return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0, + SourceLocation(), false); } static const char *getAccessName(AccessSpecifier AS) { diff --git a/lib/AST/DeclFriend.cpp b/lib/AST/DeclFriend.cpp index 6e3bd8d4225b..553d170fc3d5 100644 --- a/lib/AST/DeclFriend.cpp +++ b/lib/AST/DeclFriend.cpp @@ -12,12 +12,18 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" using namespace clang; void FriendDecl::anchor() { } +FriendDecl *FriendDecl::getNextFriendSlowCase() { + return cast_or_null<FriendDecl>( + NextFriend.get(getASTContext().getExternalSource())); +} + FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend, diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 2370d3c018f2..4d48ad8e4f5a 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -363,9 +363,12 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, return NULL; } +// Will search "local" class/category implementations for a method decl. +// If failed, then we search in class's root for an instance method. +// Returns 0 if no method is found. ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( const Selector &Sel, - bool Instance) { + bool Instance) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; @@ -377,7 +380,23 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( if (ObjCImplementationDecl *ImpDecl = getImplementation()) Method = Instance ? ImpDecl->getInstanceMethod(Sel) : ImpDecl->getClassMethod(Sel); - + + // Look through local category implementations associated with the class. + if (!Method) + Method = Instance ? getCategoryInstanceMethod(Sel) + : getCategoryClassMethod(Sel); + + // Before we give up, check if the selector is an instance method. + // But only in the root. This matches gcc's behavior and what the + // runtime expects. + if (!Instance && !Method && !getSuperClass()) { + Method = lookupInstanceMethod(Sel); + // Look through local category implementations associated + // with the root class. + if (!Method) + Method = lookupPrivateMethod(Sel, true); + } + if (!Method && getSuperClass()) return getSuperClass()->lookupPrivateMethod(Sel, Instance); return Method; @@ -451,7 +470,8 @@ void ObjCMethodDecl::setMethodParams(ASTContext &C, if (isImplicit()) return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>()); - SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, EndLoc); + SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, + DeclEndLoc); if (SelLocsKind != SelLoc_NonStandard) return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>()); @@ -523,6 +543,12 @@ ObjCMethodDecl *ObjCMethodDecl::getCanonicalDecl() { return this; } +SourceLocation ObjCMethodDecl::getLocEnd() const { + if (Stmt *Body = getBody()) + return Body->getLocEnd(); + return DeclEndLoc; +} + ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const { ObjCMethodFamily family = static_cast<ObjCMethodFamily>(Family); if (family != static_cast<unsigned>(InvalidObjCMethodFamily)) @@ -767,7 +793,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCIvarDecl *curIvar = 0; if (!ivar_empty()) { ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end(); - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; for (curIvar = data().IvarList; I != E; curIvar = *I, ++I) curIvar->setNextIvar(*I); } @@ -778,7 +804,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCCategoryDecl::ivar_iterator I = CDecl->ivar_begin(), E = CDecl->ivar_end(); if (!data().IvarList) { - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; curIvar = data().IvarList; } for ( ;I != E; curIvar = *I, ++I) @@ -791,7 +817,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCImplementationDecl::ivar_iterator I = ImplDecl->ivar_begin(), E = ImplDecl->ivar_end(); if (!data().IvarList) { - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; curIvar = data().IvarList; } for ( ;I != E; curIvar = *I, ++I) @@ -915,16 +941,10 @@ ObjCIvarDecl *ObjCIvarDecl::Create(ASTContext &C, ObjCContainerDecl *DC, // decl contexts, the previously built IvarList must be rebuilt. ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(DC); if (!ID) { - if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC)) { + if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC)) ID = IM->getClassInterface(); - if (BW) - IM->setHasSynthBitfield(true); - } else { - ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(DC); - ID = CD->getClassInterface(); - if (BW) - CD->setHasSynthBitfield(true); - } + else + ID = cast<ObjCCategoryDecl>(DC)->getClassInterface(); } ID->setIvarList(0); } @@ -1169,7 +1189,7 @@ void ObjCImplDecl::setClassInterface(ObjCInterfaceDecl *IFace) { } /// FindPropertyImplIvarDecl - This method lookup the ivar in the list of -/// properties implemented in this category @implementation block and returns +/// properties implemented in this category \@implementation block and returns /// the implemented property that uses it. /// ObjCPropertyImplDecl *ObjCImplDecl:: @@ -1184,8 +1204,8 @@ FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const { } /// FindPropertyImplDecl - This method looks up a previous ObjCPropertyImplDecl -/// added to the list of those properties @synthesized/@dynamic in this -/// category @implementation block. +/// added to the list of those properties \@synthesized/\@dynamic in this +/// category \@implementation block. /// ObjCPropertyImplDecl *ObjCImplDecl:: FindPropertyImplDecl(IdentifierInfo *Id) const { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 74e1c1bb9df9..aad0ca1b536e 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -114,6 +114,8 @@ static QualType GetBaseType(QualType T) { BaseType = FTy->getResultType(); else if (const VectorType *VTy = BaseType->getAs<VectorType>()) BaseType = VTy->getElementType(); + else if (const ReferenceType *RTy = BaseType->getAs<ReferenceType>()) + BaseType = RTy->getPointeeType(); else llvm_unreachable("Unknown declarator!"); } @@ -173,8 +175,10 @@ void DeclContext::dumpDeclContext() const { Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false); } -void Decl::dump() const { - print(llvm::errs()); +void Decl::dump(raw_ostream &Out) const { + PrintingPolicy Policy = getASTContext().getPrintingPolicy(); + Policy.Dump = true; + print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true); } raw_ostream& DeclPrinter::Indent(unsigned Indentation) { @@ -322,15 +326,13 @@ void DeclPrinter::VisitTranslationUnitDecl(TranslationUnitDecl *D) { } void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) { - std::string S = D->getNameAsString(); - D->getUnderlyingType().getAsStringInternal(S, Policy); if (!Policy.SuppressSpecifiers) { Out << "typedef "; if (D->isModulePrivate()) Out << "__module_private__ "; } - Out << S; + D->getUnderlyingType().print(Out, Policy, D->getName()); prettyPrintAttributes(D); } @@ -350,11 +352,8 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) { } Out << *D; - if (D->isFixed()) { - std::string Underlying; - D->getIntegerType().getAsStringInternal(Underlying, Policy); - Out << " : " << Underlying; - } + if (D->isFixed()) + Out << " : " << D->getIntegerType().stream(Policy); if (D->isCompleteDefinition()) { Out << " {\n"; @@ -441,13 +440,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Proto += ")"; - if (FT && FT->getTypeQuals()) { - unsigned TypeQuals = FT->getTypeQuals(); - if (TypeQuals & Qualifiers::Const) + if (FT) { + if (FT->isConst()) Proto += " const"; - if (TypeQuals & Qualifiers::Volatile) + if (FT->isVolatile()) Proto += " volatile"; - if (TypeQuals & Qualifiers::Restrict) + if (FT->isRestrict()) Proto += " restrict"; } @@ -460,9 +458,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (I) Proto += ", "; - std::string ExceptionType; - FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy); - Proto += ExceptionType; + Proto += FT->getExceptionType(I).getAsString(SubPolicy);; } Proto += ")"; } else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) { @@ -542,12 +538,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { } } else - AFT->getResultType().getAsStringInternal(Proto, Policy); + AFT->getResultType().print(Out, Policy, Proto); } else { - Ty.getAsStringInternal(Proto, Policy); + Ty.print(Out, Policy, Proto); } - Out << Proto; prettyPrintAttributes(D); if (D->isPure()) @@ -581,9 +576,7 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { if (!Policy.SuppressSpecifiers && D->isModulePrivate()) Out << "__module_private__ "; - std::string Name = D->getNameAsString(); - D->getType().getAsStringInternal(Name, Policy); - Out << Name; + Out << D->getType().stream(Policy, D->getName()); if (D->isBitField()) { Out << " : "; @@ -592,7 +585,10 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Expr *Init = D->getInClassInitializer(); if (!Policy.SuppressInitializers && Init) { - Out << " = "; + if (D->getInClassInitStyle() == ICIS_ListInit) + Out << " "; + else + Out << " = "; Init->printPretty(Out, Context, 0, Policy, Indentation); } prettyPrintAttributes(D); @@ -613,12 +609,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { if (!Policy.SuppressSpecifiers && D->isModulePrivate()) Out << "__module_private__ "; - std::string Name = D->getNameAsString(); QualType T = D->getType(); if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) T = Parm->getOriginalType(); - T.getAsStringInternal(Name, Policy); - Out << Name; + T.print(Out, Policy, D->getName()); Expr *Init = D->getInit(); if (!Policy.SuppressInitializers && Init) { bool ImplicitInit = false; @@ -666,6 +660,8 @@ void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) { // C++ declarations //---------------------------------------------------------------------------- void DeclPrinter::VisitNamespaceDecl(NamespaceDecl *D) { + if (D->isInline()) + Out << "inline "; Out << "namespace " << *D << " {\n"; VisitDeclContext(D); Indent() << "}"; @@ -923,7 +919,7 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { Indentation += Policy.Indentation; for (ObjCInterfaceDecl::ivar_iterator I = OID->ivar_begin(), E = OID->ivar_end(); I != E; ++I) { - Indent() << (*I)->getType().getAsString(Policy) << ' ' << **I << ";\n"; + Indent() << I->getType().getAsString(Policy) << ' ' << **I << ";\n"; } Indentation -= Policy.Indentation; Out << "}\n"; diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 4590195d6b31..5aebc2b764c0 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -145,7 +145,7 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() { template <class EntryType> typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType* RedeclarableTemplateDecl::findSpecializationImpl( - llvm::FoldingSet<EntryType> &Specs, + llvm::FoldingSetVector<EntryType> &Specs, const TemplateArgument *Args, unsigned NumArgs, void *&InsertPos) { typedef SpecEntryTraits<EntryType> SETraits; @@ -298,13 +298,13 @@ void ClassTemplateDecl::LoadLazySpecializations() { } } -llvm::FoldingSet<ClassTemplateSpecializationDecl> & +llvm::FoldingSetVector<ClassTemplateSpecializationDecl> & ClassTemplateDecl::getSpecializations() { LoadLazySpecializations(); return getCommonPtr()->Specializations; } -llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> & +llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> & ClassTemplateDecl::getPartialSpecializations() { LoadLazySpecializations(); return getCommonPtr()->PartialSpecializations; @@ -363,11 +363,11 @@ void ClassTemplateDecl::AddPartialSpecialization( void ClassTemplateDecl::getPartialSpecializations( SmallVectorImpl<ClassTemplatePartialSpecializationDecl *> &PS) { - llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> &PartialSpecs + llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &PartialSpecs = getPartialSpecializations(); PS.clear(); PS.resize(PartialSpecs.size()); - for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator P = PartialSpecs.begin(), PEnd = PartialSpecs.end(); P != PEnd; ++P) { assert(!PS[P->getSequenceNumber()]); @@ -378,7 +378,8 @@ void ClassTemplateDecl::getPartialSpecializations( ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization(QualType T) { ASTContext &Context = getASTContext(); - typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + using llvm::FoldingSetVector; + typedef FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator partial_spec_iterator; for (partial_spec_iterator P = getPartialSpecializations().begin(), PEnd = getPartialSpecializations().end(); @@ -394,7 +395,7 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecInstantiatedFromMember( ClassTemplatePartialSpecializationDecl *D) { Decl *DCanon = D->getCanonicalDecl(); - for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator P = getPartialSpecializations().begin(), PEnd = getPartialSpecializations().end(); P != PEnd; ++P) { @@ -868,5 +869,6 @@ ClassScopeFunctionSpecializationDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(ClassScopeFunctionSpecializationDecl)); - return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0); + return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0, + false, TemplateArgumentListInfo()); } diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index 64924ad95095..28188d91c10a 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -135,33 +135,6 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { } // end namespace clang -DeclarationName::DeclarationName(Selector Sel) { - if (!Sel.getAsOpaquePtr()) { - Ptr = 0; - return; - } - - switch (Sel.getNumArgs()) { - case 0: - Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); - assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); - Ptr |= StoredObjCZeroArgSelector; - break; - - case 1: - Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); - assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); - Ptr |= StoredObjCOneArgSelector; - break; - - default: - Ptr = Sel.InfoPtr & ~Selector::ArgFlags; - assert((Ptr & PtrMask) == 0 && "Improperly aligned MultiKeywordSelector"); - Ptr |= StoredDeclarationNameExtra; - break; - } -} - DeclarationName::NameKind DeclarationName::getNameKind() const { switch (getStoredNameKind()) { case StoredIdentifier: return Identifier; @@ -305,28 +278,10 @@ IdentifierInfo *DeclarationName::getCXXLiteralIdentifier() const { return 0; } -Selector DeclarationName::getObjCSelector() const { - switch (getNameKind()) { - case ObjCZeroArgSelector: - return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 0); - - case ObjCOneArgSelector: - return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 1); - - case ObjCMultiArgSelector: - return Selector(reinterpret_cast<MultiKeywordSelector *>(Ptr & ~PtrMask)); - - default: - break; - } - - return Selector(); -} - -void *DeclarationName::getFETokenInfoAsVoid() const { +void *DeclarationName::getFETokenInfoAsVoidSlow() const { switch (getNameKind()) { case Identifier: - return getAsIdentifierInfo()->getFETokenInfo<void>(); + llvm_unreachable("Handled by getFETokenInfo()"); case CXXConstructorName: case CXXDestructorName: @@ -481,12 +436,6 @@ DeclarationNameTable::getCXXLiteralOperatorName(IdentifierInfo *II) { return DeclarationName(LiteralName); } -unsigned -llvm::DenseMapInfo<clang::DeclarationName>:: -getHashValue(clang::DeclarationName N) { - return DenseMapInfo<void*>::getHashValue(N.getAsOpaquePtr()); -} - DeclarationNameLoc::DeclarationNameLoc(DeclarationName Name) { switch (Name.getNameKind()) { case DeclarationName::Identifier: diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp index 4c7cd8a6793b..c1432b5720c7 100644 --- a/lib/AST/DumpXML.cpp +++ b/lib/AST/DumpXML.cpp @@ -326,7 +326,7 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, } case TemplateArgument::Integral: { push("integer"); - setInteger("value", *A.getAsIntegral()); + setInteger("value", A.getAsIntegral()); completeAttrs(); pop(); break; @@ -778,7 +778,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, // ObjCCategoryDecl void visitObjCCategoryDeclAttrs(ObjCCategoryDecl *D) { setFlag("extension", D->IsClassExtension()); - setFlag("synth_bitfield", D->hasSynthBitfield()); } void visitObjCCategoryDeclChildren(ObjCCategoryDecl *D) { visitDeclRef("interface", D->getClassInterface()); @@ -804,7 +803,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, // ObjCImplementationDecl void visitObjCImplementationDeclAttrs(ObjCImplementationDecl *D) { - setFlag("synth_bitfield", D->hasSynthBitfield()); set("identifier", D->getName()); } void visitObjCImplementationDeclChildren(ObjCImplementationDecl *D) { @@ -973,9 +971,9 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, } void visitFunctionProtoTypeAttrs(FunctionProtoType *T) { - setFlag("const", T->getTypeQuals() & Qualifiers::Const); - setFlag("volatile", T->getTypeQuals() & Qualifiers::Volatile); - setFlag("restrict", T->getTypeQuals() & Qualifiers::Restrict); + setFlag("const", T->isConst()); + setFlag("volatile", T->isVolatile()); + setFlag("restrict", T->isRestrict()); } void visitFunctionProtoTypeChildren(FunctionProtoType *T) { push("parameters"); @@ -1024,17 +1022,12 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, }; } -void Decl::dumpXML() const { - dumpXML(llvm::errs()); -} - void Decl::dumpXML(raw_ostream &out) const { XMLDumper(out, getASTContext()).dispatch(const_cast<Decl*>(this)); } #else /* ifndef NDEBUG */ -void Decl::dumpXML() const {} void Decl::dumpXML(raw_ostream &out) const {} #endif diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index fcde5429b394..24361efafa6c 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -33,6 +33,21 @@ #include <cstring> using namespace clang; +const CXXRecordDecl *Expr::getBestDynamicClassType() const { + const Expr *E = ignoreParenBaseCasts(); + + QualType DerivedType = E->getType(); + if (const PointerType *PTy = DerivedType->getAs<PointerType>()) + DerivedType = PTy->getPointeeType(); + + if (DerivedType->isDependentType()) + return NULL; + + const RecordType *Ty = DerivedType->castAs<RecordType>(); + Decl *D = Ty->getDecl(); + return cast<CXXRecordDecl>(D); +} + /// isKnownToHaveBooleanValue - Return true if this is an integer expression /// that is known to return 0 or 1. This happens for _Bool/bool expressions /// but also int expressions which are produced by things like comparisons in @@ -196,7 +211,7 @@ static void computeDeclRefDependence(ASTContext &Ctx, NamedDecl *D, QualType T, if ((Ctx.getLangOpts().CPlusPlus0x ? Var->getType()->isLiteralType() : Var->getType()->isIntegralOrEnumerationType()) && - (Var->getType().getCVRQualifiers() == Qualifiers::Const || + (Var->getType().isConstQualified() || Var->getType()->isReferenceType())) { if (const Expr *Init = Var->getAnyInitializer()) if (Init->isValueDependent()) { @@ -414,9 +429,7 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) { if (FT) { for (unsigned i = 0, e = Decl->getNumParams(); i != e; ++i) { if (i) POut << ", "; - std::string Param; - Decl->getParamDecl(i)->getType().getAsStringInternal(Param, Policy); - POut << Param; + POut << Decl->getParamDecl(i)->getType().stream(Policy); } if (FT->isVariadic()) { @@ -427,10 +440,10 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) { POut << ")"; if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { - Qualifiers ThisQuals = Qualifiers::fromCVRMask(MD->getTypeQualifiers()); - if (ThisQuals.hasConst()) + const FunctionType *FT = cast<FunctionType>(MD->getType().getTypePtr()); + if (FT->isConst()) POut << " const"; - if (ThisQuals.hasVolatile()) + if (FT->isVolatile()) POut << " volatile"; RefQualifierKind Ref = MD->getRefQualifier(); if (Ref == RQ_LValue) @@ -545,6 +558,17 @@ void APNumericStorage::setIntValue(ASTContext &C, const llvm::APInt &Val) { VAL = 0; } +IntegerLiteral::IntegerLiteral(ASTContext &C, const llvm::APInt &V, + QualType type, SourceLocation l) + : Expr(IntegerLiteralClass, type, VK_RValue, OK_Ordinary, false, false, + false, false), + Loc(l) { + assert(type->isIntegerType() && "Illegal type in IntegerLiteral"); + assert(V.getBitWidth() == C.getIntWidth(type) && + "Integer type is not the correct size for constant."); + setValue(C, V); +} + IntegerLiteral * IntegerLiteral::Create(ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l) { @@ -556,6 +580,23 @@ IntegerLiteral::Create(ASTContext &C, EmptyShell Empty) { return new (C) IntegerLiteral(Empty); } +FloatingLiteral::FloatingLiteral(ASTContext &C, const llvm::APFloat &V, + bool isexact, QualType Type, SourceLocation L) + : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false, + false, false), Loc(L) { + FloatingLiteralBits.IsIEEE = + &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad; + FloatingLiteralBits.IsExact = isexact; + setValue(C, V); +} + +FloatingLiteral::FloatingLiteral(ASTContext &C, EmptyShell Empty) + : Expr(FloatingLiteralClass, Empty) { + FloatingLiteralBits.IsIEEE = + &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad; + FloatingLiteralBits.IsExact = false; +} + FloatingLiteral * FloatingLiteral::Create(ASTContext &C, const llvm::APFloat &V, bool isexact, QualType Type, SourceLocation L) { @@ -635,6 +676,99 @@ StringLiteral *StringLiteral::CreateEmpty(ASTContext &C, unsigned NumStrs) { return SL; } +void StringLiteral::outputString(raw_ostream &OS) { + switch (getKind()) { + case Ascii: break; // no prefix. + case Wide: OS << 'L'; break; + case UTF8: OS << "u8"; break; + case UTF16: OS << 'u'; break; + case UTF32: OS << 'U'; break; + } + OS << '"'; + static const char Hex[] = "0123456789ABCDEF"; + + unsigned LastSlashX = getLength(); + for (unsigned I = 0, N = getLength(); I != N; ++I) { + switch (uint32_t Char = getCodeUnit(I)) { + default: + // FIXME: Convert UTF-8 back to codepoints before rendering. + + // Convert UTF-16 surrogate pairs back to codepoints before rendering. + // Leave invalid surrogates alone; we'll use \x for those. + if (getKind() == UTF16 && I != N - 1 && Char >= 0xd800 && + Char <= 0xdbff) { + uint32_t Trail = getCodeUnit(I + 1); + if (Trail >= 0xdc00 && Trail <= 0xdfff) { + Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00); + ++I; + } + } + + if (Char > 0xff) { + // If this is a wide string, output characters over 0xff using \x + // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a + // codepoint: use \x escapes for invalid codepoints. + if (getKind() == Wide || + (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) { + // FIXME: Is this the best way to print wchar_t? + OS << "\\x"; + int Shift = 28; + while ((Char >> Shift) == 0) + Shift -= 4; + for (/**/; Shift >= 0; Shift -= 4) + OS << Hex[(Char >> Shift) & 15]; + LastSlashX = I; + break; + } + + if (Char > 0xffff) + OS << "\\U00" + << Hex[(Char >> 20) & 15] + << Hex[(Char >> 16) & 15]; + else + OS << "\\u"; + OS << Hex[(Char >> 12) & 15] + << Hex[(Char >> 8) & 15] + << Hex[(Char >> 4) & 15] + << Hex[(Char >> 0) & 15]; + break; + } + + // If we used \x... for the previous character, and this character is a + // hexadecimal digit, prevent it being slurped as part of the \x. + if (LastSlashX + 1 == I) { + switch (Char) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + OS << "\"\""; + } + } + + assert(Char <= 0xff && + "Characters above 0xff should already have been handled."); + + if (isprint(Char)) + OS << (char)Char; + else // Output anything hard as an octal escape. + OS << '\\' + << (char)('0' + ((Char >> 6) & 7)) + << (char)('0' + ((Char >> 3) & 7)) + << (char)('0' + ((Char >> 0) & 7)); + break; + // Handle some common non-printable cases to make dumps prettier. + case '\\': OS << "\\\\"; break; + case '"': OS << "\\\""; break; + case '\n': OS << "\\n"; break; + case '\t': OS << "\\t"; break; + case '\a': OS << "\\a"; break; + case '\b': OS << "\\b"; break; + } + } + OS << '"'; +} + void StringLiteral::setString(ASTContext &C, StringRef Str, StringKind Kind, bool IsPascal) { //FIXME: we assume that the string data comes from a target that uses the same @@ -681,7 +815,8 @@ void StringLiteral::setString(ASTContext &C, StringRef Str, SourceLocation StringLiteral:: getLocationOfByte(unsigned ByteNo, const SourceManager &SM, const LangOptions &Features, const TargetInfo &Target) const { - assert(Kind == StringLiteral::Ascii && "This only works for ASCII strings"); + assert((Kind == StringLiteral::Ascii || Kind == StringLiteral::UTF8) && + "Only narrow string literals are currently supported"); // Loop over all of the tokens in this string until we find the one that // contains the byte we're looking for. @@ -704,14 +839,9 @@ getLocationOfByte(unsigned ByteNo, const SourceManager &SM, const char *StrData = Buffer.data()+LocInfo.second; - // Create a langops struct and enable trigraphs. This is sufficient for - // relexing tokens. - LangOptions LangOpts; - LangOpts.Trigraphs = true; - // Create a lexer starting at the beginning of this token. - Lexer TheLexer(StrTokSpellingLoc, Features, Buffer.begin(), StrData, - Buffer.end()); + Lexer TheLexer(SM.getLocForStartOfFile(LocInfo.first), Features, + Buffer.begin(), StrData, Buffer.end()); Token TheTok; TheLexer.LexFromRawLexer(TheTok); @@ -1656,8 +1786,9 @@ Stmt *BlockExpr::getBody() { /// be warned about if the result is unused. If so, fill in Loc and Ranges /// with location to warn on and the source range[s] to report with the /// warning. -bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, - SourceRange &R2, ASTContext &Ctx) const { +bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx) const { // Don't warn if the expr is type dependent. The type could end up // instantiating to void. if (isTypeDependent()) @@ -1667,30 +1798,32 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, default: if (getType()->isVoidType()) return false; + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; case ParenExprClass: return cast<ParenExpr>(this)->getSubExpr()-> - isUnusedResultAWarning(Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); case GenericSelectionExprClass: return cast<GenericSelectionExpr>(this)->getResultExpr()-> - isUnusedResultAWarning(Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); case UnaryOperatorClass: { const UnaryOperator *UO = cast<UnaryOperator>(this); switch (UO->getOpcode()) { - default: break; + case UO_Plus: + case UO_Minus: + case UO_AddrOf: + case UO_Not: + case UO_LNot: + case UO_Deref: + break; case UO_PostInc: case UO_PostDec: case UO_PreInc: case UO_PreDec: // ++/-- return false; // Not a warning. - case UO_Deref: - // Dereferencing a volatile pointer is a side-effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; - break; case UO_Real: case UO_Imag: // accessing a piece of a volatile complex is a side-effect. @@ -1699,8 +1832,9 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } + WarnE = this; Loc = UO->getOperatorLoc(); R1 = UO->getSubExpr()->getSourceRange(); return true; @@ -1719,17 +1853,18 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, dyn_cast<IntegerLiteral>(BO->getRHS()->IgnoreParens())) if (IE->getValue() == 0) return false; - return BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); // Consider '||', '&&' to have side effects if the LHS or RHS does. case BO_LAnd: case BO_LOr: - if (!BO->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx) || - !BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx)) + if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) || + !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) return false; break; } if (BO->isAssignmentOp()) return false; + WarnE = this; Loc = BO->getOperatorLoc(); R1 = BO->getLHS()->getSourceRange(); R2 = BO->getRHS()->getSourceRange(); @@ -1745,28 +1880,22 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, // be being used for control flow. Only warn if both the LHS and // RHS are warnings. const ConditionalOperator *Exp = cast<ConditionalOperator>(this); - if (!Exp->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx)) + if (!Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) return false; if (!Exp->getLHS()) return true; - return Exp->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } case MemberExprClass: - // If the base pointer or element is to a volatile pointer/field, accessing - // it is a side effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; + WarnE = this; Loc = cast<MemberExpr>(this)->getMemberLoc(); R1 = SourceRange(Loc, Loc); R2 = cast<MemberExpr>(this)->getBase()->getSourceRange(); return true; case ArraySubscriptExprClass: - // If the base pointer or element is to a volatile pointer/field, accessing - // it is a side effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; + WarnE = this; Loc = cast<ArraySubscriptExpr>(this)->getRBracketLoc(); R1 = cast<ArraySubscriptExpr>(this)->getLHS()->getSourceRange(); R2 = cast<ArraySubscriptExpr>(this)->getRHS()->getSourceRange(); @@ -1782,6 +1911,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const CXXOperatorCallExpr *Op = cast<CXXOperatorCallExpr>(this); if (Op->getOperator() == OO_EqualEqual || Op->getOperator() == OO_ExclaimEqual) { + WarnE = this; Loc = Op->getOperatorLoc(); R1 = Op->getSourceRange(); return true; @@ -1802,6 +1932,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, // updated to match for QoI. if (FD->getAttr<WarnUnusedResultAttr>() || FD->getAttr<PureAttr>() || FD->getAttr<ConstAttr>()) { + WarnE = this; Loc = CE->getCallee()->getLocStart(); R1 = CE->getCallee()->getSourceRange(); @@ -1826,6 +1957,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, ME->getSelector().getIdentifierInfoForSlot(0) && ME->getSelector().getIdentifierInfoForSlot(0) ->getName().startswith("init")) { + WarnE = this; Loc = getExprLoc(); R1 = ME->getSourceRange(); return true; @@ -1833,6 +1965,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD && MD->getAttr<WarnUnusedResultAttr>()) { + WarnE = this; Loc = getExprLoc(); return true; } @@ -1840,6 +1973,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, } case ObjCPropertyRefExprClass: + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; @@ -1852,6 +1986,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, isa<BinaryOperator>(PO->getSyntacticForm())) return false; + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; @@ -1866,50 +2001,67 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const CompoundStmt *CS = cast<StmtExpr>(this)->getSubStmt(); if (!CS->body_empty()) { if (const Expr *E = dyn_cast<Expr>(CS->body_back())) - return E->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); if (const LabelStmt *Label = dyn_cast<LabelStmt>(CS->body_back())) if (const Expr *E = dyn_cast<Expr>(Label->getSubStmt())) - return E->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } if (getType()->isVoidType()) return false; + WarnE = this; Loc = cast<StmtExpr>(this)->getLParenLoc(); R1 = getSourceRange(); return true; } - case CStyleCastExprClass: - // If this is an explicit cast to void, allow it. People do this when they - // think they know what they're doing :). - if (getType()->isVoidType()) - return false; - Loc = cast<CStyleCastExpr>(this)->getLParenLoc(); - R1 = cast<CStyleCastExpr>(this)->getSubExpr()->getSourceRange(); - return true; - case CXXFunctionalCastExprClass: { - if (getType()->isVoidType()) - return false; + case CStyleCastExprClass: { + // Ignore an explicit cast to void unless the operand is a non-trivial + // volatile lvalue. const CastExpr *CE = cast<CastExpr>(this); - - // If this is a cast to void or a constructor conversion, check the operand. + if (CE->getCastKind() == CK_ToVoid) { + if (CE->getSubExpr()->isGLValue() && + CE->getSubExpr()->getType().isVolatileQualified()) { + const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(CE->getSubExpr()->IgnoreParens()); + if (!(DRE && isa<VarDecl>(DRE->getDecl()) && + cast<VarDecl>(DRE->getDecl())->hasLocalStorage())) { + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, + R1, R2, Ctx); + } + } + return false; + } + + // If this is a cast to a constructor conversion, check the operand. // Otherwise, the result of the cast is unused. - if (CE->getCastKind() == CK_ToVoid || - CE->getCastKind() == CK_ConstructorConversion) - return (cast<CastExpr>(this)->getSubExpr() - ->isUnusedResultAWarning(Loc, R1, R2, Ctx)); - Loc = cast<CXXFunctionalCastExpr>(this)->getTypeBeginLoc(); - R1 = cast<CXXFunctionalCastExpr>(this)->getSubExpr()->getSourceRange(); + if (CE->getCastKind() == CK_ConstructorConversion) + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + + WarnE = this; + if (const CXXFunctionalCastExpr *CXXCE = + dyn_cast<CXXFunctionalCastExpr>(this)) { + Loc = CXXCE->getTypeBeginLoc(); + R1 = CXXCE->getSubExpr()->getSourceRange(); + } else { + const CStyleCastExpr *CStyleCE = cast<CStyleCastExpr>(this); + Loc = CStyleCE->getLParenLoc(); + R1 = CStyleCE->getSubExpr()->getSourceRange(); + } return true; } + case ImplicitCastExprClass: { + const CastExpr *ICE = cast<ImplicitCastExpr>(this); - case ImplicitCastExprClass: - // Check the operand, since implicit casts are inserted by Sema - return (cast<ImplicitCastExpr>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + // lvalue-to-rvalue conversion on a volatile lvalue is a side-effect. + if (ICE->getCastKind() == CK_LValueToRValue && + ICE->getSubExpr()->getType().isVolatileQualified()) + return false; + return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + } case CXXDefaultArgExprClass: return (cast<CXXDefaultArgExpr>(this) - ->getExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -1918,10 +2070,10 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, return false; case CXXBindTemporaryExprClass: return (cast<CXXBindTemporaryExpr>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case ExprWithCleanupsClass: return (cast<ExprWithCleanups>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); } } @@ -2096,7 +2248,27 @@ Expr *Expr::IgnoreParenLValueCasts() { } return E; } - + +Expr *Expr::ignoreParenBaseCasts() { + Expr *E = this; + while (true) { + if (ParenExpr *P = dyn_cast<ParenExpr>(E)) { + E = P->getSubExpr(); + continue; + } + if (CastExpr *CE = dyn_cast<CastExpr>(E)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) { + E = CE->getSubExpr(); + continue; + } + } + + return E; + } +} + Expr *Expr::IgnoreParenImpCasts() { Expr *E = this; while (true) { @@ -2267,6 +2439,10 @@ bool Expr::isTemporaryObject(ASTContext &C, const CXXRecordDecl *TempTy) const { if (isa<MemberExpr>(E)) return false; + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) + if (BO->isPtrMemOp()) + return false; + // - opaque values (all) if (isa<OpaqueValueExpr>(E)) return false; @@ -2446,6 +2622,207 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { return isEvaluatable(Ctx); } +bool Expr::HasSideEffects(const ASTContext &Ctx) const { + if (isInstantiationDependent()) + return true; + + switch (getStmtClass()) { + case NoStmtClass: + #define ABSTRACT_STMT(Type) + #define STMT(Type, Base) case Type##Class: + #define EXPR(Type, Base) + #include "clang/AST/StmtNodes.inc" + llvm_unreachable("unexpected Expr kind"); + + case DependentScopeDeclRefExprClass: + case CXXUnresolvedConstructExprClass: + case CXXDependentScopeMemberExprClass: + case UnresolvedLookupExprClass: + case UnresolvedMemberExprClass: + case PackExpansionExprClass: + case SubstNonTypeTemplateParmPackExprClass: + llvm_unreachable("shouldn't see dependent / unresolved nodes here"); + + case DeclRefExprClass: + case ObjCIvarRefExprClass: + case PredefinedExprClass: + case IntegerLiteralClass: + case FloatingLiteralClass: + case ImaginaryLiteralClass: + case StringLiteralClass: + case CharacterLiteralClass: + case OffsetOfExprClass: + case ImplicitValueInitExprClass: + case UnaryExprOrTypeTraitExprClass: + case AddrLabelExprClass: + case GNUNullExprClass: + case CXXBoolLiteralExprClass: + case CXXNullPtrLiteralExprClass: + case CXXThisExprClass: + case CXXScalarValueInitExprClass: + case TypeTraitExprClass: + case UnaryTypeTraitExprClass: + case BinaryTypeTraitExprClass: + case ArrayTypeTraitExprClass: + case ExpressionTraitExprClass: + case CXXNoexceptExprClass: + case SizeOfPackExprClass: + case ObjCStringLiteralClass: + case ObjCEncodeExprClass: + case ObjCBoolLiteralExprClass: + case CXXUuidofExprClass: + case OpaqueValueExprClass: + // These never have a side-effect. + return false; + + case CallExprClass: + case CompoundAssignOperatorClass: + case VAArgExprClass: + case AtomicExprClass: + case StmtExprClass: + case CXXOperatorCallExprClass: + case CXXMemberCallExprClass: + case UserDefinedLiteralClass: + case CXXThrowExprClass: + case CXXNewExprClass: + case CXXDeleteExprClass: + case ExprWithCleanupsClass: + case CXXBindTemporaryExprClass: + case BlockExprClass: + case CUDAKernelCallExprClass: + // These always have a side-effect. + return true; + + case ParenExprClass: + case ArraySubscriptExprClass: + case MemberExprClass: + case ConditionalOperatorClass: + case BinaryConditionalOperatorClass: + case CompoundLiteralExprClass: + case ExtVectorElementExprClass: + case DesignatedInitExprClass: + case ParenListExprClass: + case CXXPseudoDestructorExprClass: + case SubstNonTypeTemplateParmExprClass: + case MaterializeTemporaryExprClass: + case ShuffleVectorExprClass: + case AsTypeExprClass: + // These have a side-effect if any subexpression does. + break; + + case UnaryOperatorClass: + if (cast<UnaryOperator>(this)->isIncrementDecrementOp()) + return true; + break; + + case BinaryOperatorClass: + if (cast<BinaryOperator>(this)->isAssignmentOp()) + return true; + break; + + case InitListExprClass: + // FIXME: The children for an InitListExpr doesn't include the array filler. + if (const Expr *E = cast<InitListExpr>(this)->getArrayFiller()) + if (E->HasSideEffects(Ctx)) + return true; + break; + + case GenericSelectionExprClass: + return cast<GenericSelectionExpr>(this)->getResultExpr()-> + HasSideEffects(Ctx); + + case ChooseExprClass: + return cast<ChooseExpr>(this)->getChosenSubExpr(Ctx)->HasSideEffects(Ctx); + + case CXXDefaultArgExprClass: + return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx); + + case CXXDynamicCastExprClass: { + // A dynamic_cast expression has side-effects if it can throw. + const CXXDynamicCastExpr *DCE = cast<CXXDynamicCastExpr>(this); + if (DCE->getTypeAsWritten()->isReferenceType() && + DCE->getCastKind() == CK_Dynamic) + return true; + } // Fall through. + case ImplicitCastExprClass: + case CStyleCastExprClass: + case CXXStaticCastExprClass: + case CXXReinterpretCastExprClass: + case CXXConstCastExprClass: + case CXXFunctionalCastExprClass: { + const CastExpr *CE = cast<CastExpr>(this); + if (CE->getCastKind() == CK_LValueToRValue && + CE->getSubExpr()->getType().isVolatileQualified()) + return true; + break; + } + + case CXXTypeidExprClass: + // typeid might throw if its subexpression is potentially-evaluated, so has + // side-effects in that case whether or not its subexpression does. + return cast<CXXTypeidExpr>(this)->isPotentiallyEvaluated(); + + case CXXConstructExprClass: + case CXXTemporaryObjectExprClass: { + const CXXConstructExpr *CE = cast<CXXConstructExpr>(this); + if (!CE->getConstructor()->isTrivial()) + return true; + // A trivial constructor does not add any side-effects of its own. Just look + // at its arguments. + break; + } + + case LambdaExprClass: { + const LambdaExpr *LE = cast<LambdaExpr>(this); + for (LambdaExpr::capture_iterator I = LE->capture_begin(), + E = LE->capture_end(); I != E; ++I) + if (I->getCaptureKind() == LCK_ByCopy) + // FIXME: Only has a side-effect if the variable is volatile or if + // the copy would invoke a non-trivial copy constructor. + return true; + return false; + } + + case PseudoObjectExprClass: { + // Only look for side-effects in the semantic form, and look past + // OpaqueValueExpr bindings in that form. + const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(this); + for (PseudoObjectExpr::const_semantics_iterator I = PO->semantics_begin(), + E = PO->semantics_end(); + I != E; ++I) { + const Expr *Subexpr = *I; + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Subexpr)) + Subexpr = OVE->getSourceExpr(); + if (Subexpr->HasSideEffects(Ctx)) + return true; + } + return false; + } + + case ObjCBoxedExprClass: + case ObjCArrayLiteralClass: + case ObjCDictionaryLiteralClass: + case ObjCMessageExprClass: + case ObjCSelectorExprClass: + case ObjCProtocolExprClass: + case ObjCPropertyRefExprClass: + case ObjCIsaExprClass: + case ObjCIndirectCopyRestoreExprClass: + case ObjCSubscriptRefExprClass: + case ObjCBridgedCastExprClass: + // FIXME: Classify these cases better. + return true; + } + + // Recurse to children. + for (const_child_range SubStmts = children(); SubStmts; ++SubStmts) + if (const Stmt *S = *SubStmts) + if (cast<Expr>(S)->HasSideEffects(Ctx)) + return true; + + return false; +} + namespace { /// \brief Look for a call to a non-trivial function within an expression. class NonTrivialCallFinder : public EvaluatedExprVisitor<NonTrivialCallFinder> @@ -2514,7 +2891,7 @@ Expr::isNullPointerConstant(ASTContext &Ctx, llvm_unreachable("Unexpected value dependent expression!"); case NPC_ValueDependentIsNull: if (isTypeDependent() || getType()->isIntegralType(Ctx)) - return NPCK_ZeroInteger; + return NPCK_ZeroExpression; else return NPCK_NotNull; @@ -2588,7 +2965,12 @@ Expr::isNullPointerConstant(ASTContext &Ctx, return NPCK_NotNull; } - return (EvaluateKnownConstInt(Ctx) == 0) ? NPCK_ZeroInteger : NPCK_NotNull; + if (EvaluateKnownConstInt(Ctx) != 0) + return NPCK_NotNull; + + if (isa<IntegerLiteral>(this)) + return NPCK_ZeroLiteral; + return NPCK_ZeroExpression; } /// \brief If this expression is an l-value for an Objective C diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 8cf519c93dc2..3fa49e0e18b9 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/IdentifierTable.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" @@ -23,6 +24,21 @@ using namespace clang; // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// +bool CXXTypeidExpr::isPotentiallyEvaluated() const { + if (isTypeOperand()) + return false; + + // C++11 [expr.typeid]p3: + // When typeid is applied to an expression other than a glvalue of + // polymorphic class type, [...] the expression is an unevaluated operand. + const Expr *E = getExprOperand(); + if (const CXXRecordDecl *RD = E->getType()->getAsCXXRecordDecl()) + if (RD->isPolymorphic() && E->isGLValue()) + return true; + + return false; +} + QualType CXXTypeidExpr::getTypeOperand() const { assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)"); return Operand.get<TypeSourceInfo *>()->getType().getNonReferenceType() @@ -126,13 +142,6 @@ SourceLocation CXXNewExpr::getEndLoc() const { // CXXDeleteExpr QualType CXXDeleteExpr::getDestroyedType() const { const Expr *Arg = getArgument(); - while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { - if (ICE->getCastKind() != CK_UserDefinedConversion && - ICE->getType()->isVoidPointerType()) - Arg = ICE->getSubExpr(); - else - break; - } // The type-to-delete may not be a pointer if it's a dependent type. const QualType ArgType = Arg->getType(); @@ -268,6 +277,7 @@ OverloadExpr::OverloadExpr(StmtClass K, ASTContext &C, isa<UnresolvedUsingValueDecl>(*I)) { ExprBits.TypeDependent = true; ExprBits.ValueDependent = true; + ExprBits.InstantiationDependent = true; } } @@ -415,37 +425,37 @@ SourceRange CXXConstructExpr::getSourceRange() const { return SourceRange(Loc, End); } -SourceRange CXXOperatorCallExpr::getSourceRange() const { +SourceRange CXXOperatorCallExpr::getSourceRangeImpl() const { OverloadedOperatorKind Kind = getOperator(); if (Kind == OO_PlusPlus || Kind == OO_MinusMinus) { if (getNumArgs() == 1) // Prefix operator - return SourceRange(getOperatorLoc(), - getArg(0)->getSourceRange().getEnd()); + return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd()); else // Postfix operator - return SourceRange(getArg(0)->getSourceRange().getBegin(), - getOperatorLoc()); + return SourceRange(getArg(0)->getLocStart(), getOperatorLoc()); } else if (Kind == OO_Arrow) { return getArg(0)->getSourceRange(); } else if (Kind == OO_Call) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc()); + return SourceRange(getArg(0)->getLocStart(), getRParenLoc()); } else if (Kind == OO_Subscript) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc()); + return SourceRange(getArg(0)->getLocStart(), getRParenLoc()); } else if (getNumArgs() == 1) { - return SourceRange(getOperatorLoc(), getArg(0)->getSourceRange().getEnd()); + return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd()); } else if (getNumArgs() == 2) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), - getArg(1)->getSourceRange().getEnd()); + return SourceRange(getArg(0)->getLocStart(), getArg(1)->getLocEnd()); } else { - return SourceRange(); + return getOperatorLoc(); } } Expr *CXXMemberCallExpr::getImplicitObjectArgument() const { - if (const MemberExpr *MemExpr = - dyn_cast<MemberExpr>(getCallee()->IgnoreParens())) + const Expr *Callee = getCallee()->IgnoreParens(); + if (const MemberExpr *MemExpr = dyn_cast<MemberExpr>(Callee)) return MemExpr->getBase(); + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Callee)) + if (BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI) + return BO->getLHS(); // FIXME: Will eventually need to cope with member pointers. return 0; @@ -461,7 +471,7 @@ CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const { } -CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() { +CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() const { Expr* ThisArg = getImplicitObjectArgument(); if (!ThisArg) return 0; @@ -556,6 +566,9 @@ bool CXXDynamicCastExpr::isAlwaysNull() const DestType = DestType->castAs<PointerType>()->getPointeeType(); } + if (DestType->isVoidType()) + return false; + const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl()); @@ -796,10 +809,11 @@ LambdaExpr::LambdaExpr(QualType T, ArrayRef<Expr *> CaptureInits, ArrayRef<VarDecl *> ArrayIndexVars, ArrayRef<unsigned> ArrayIndexStarts, - SourceLocation ClosingBrace) + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) : Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(), T->isDependentType(), T->isDependentType(), - /*ContainsUnexpandedParameterPack=*/false), + ContainsUnexpandedParameterPack), IntroducerRange(IntroducerRange), NumCaptures(Captures.size()), CaptureDefault(CaptureDefault), @@ -856,7 +870,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, ArrayRef<Expr *> CaptureInits, ArrayRef<VarDecl *> ArrayIndexVars, ArrayRef<unsigned> ArrayIndexStarts, - SourceLocation ClosingBrace) { + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) { // Determine the type of the expression (i.e., the type of the // function object we're creating). QualType T = Context.getTypeDeclType(Class); @@ -869,7 +884,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault, Captures, ExplicitParams, ExplicitResultType, CaptureInits, ArrayIndexVars, ArrayIndexStarts, - ClosingBrace); + ClosingBrace, ContainsUnexpandedParameterPack); } LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures, @@ -944,7 +959,7 @@ CompoundStmt *LambdaExpr::getBody() const { } bool LambdaExpr::isMutable() const { - return (getCallOperator()->getTypeQualifiers() & Qualifiers::Const) == 0; + return !getCallOperator()->isConst(); } ExprWithCleanups::ExprWithCleanups(Expr *subexpr, diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index b091e19a8a95..f16d70b5da1b 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -77,6 +77,7 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: case Cl::CL_SubObjCPropertySetting: case Cl::CL_ClassTemporary: + case Cl::CL_ArrayTemporary: case Cl::CL_ObjCMessageRValue: case Cl::CL_PRValue: assert(getValueKind() == VK_RValue); break; } @@ -87,6 +88,18 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { return Classification(kind, modifiable); } +/// Classify an expression which creates a temporary, based on its type. +static Cl::Kinds ClassifyTemporary(QualType T) { + if (T->isRecordType()) + return Cl::CL_ClassTemporary; + if (T->isArrayType()) + return Cl::CL_ArrayTemporary; + + // No special classification: these don't behave differently from normal + // prvalues. + return Cl::CL_PRValue; +} + static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // This function takes the first stab at classifying expressions. const LangOptions &Lang = Ctx.getLangOpts(); @@ -124,10 +137,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { return Cl::CL_LValue; // C99 6.5.2.5p5 says that compound literals are lvalues. - // In C++, they're class temporaries. + // In C++, they're prvalue temporaries. case Expr::CompoundLiteralExprClass: - return Ctx.getLangOpts().CPlusPlus? Cl::CL_ClassTemporary - : Cl::CL_LValue; + return Ctx.getLangOpts().CPlusPlus ? ClassifyTemporary(E->getType()) + : Cl::CL_LValue; // Expressions that are prvalues. case Expr::CXXBoolLiteralExprClass: @@ -158,7 +171,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCSelectorExprClass: case Expr::ObjCProtocolExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoolLiteralExprClass: @@ -417,7 +430,7 @@ static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) { return Cl::CL_LValue; const RValueReferenceType *RV = T->getAs<RValueReferenceType>(); if (!RV) // Could still be a class temporary, though. - return T->isRecordType() ? Cl::CL_ClassTemporary : Cl::CL_PRValue; + return ClassifyTemporary(T); return RV->getPointeeType()->isFunctionType() ? Cl::CL_LValue : Cl::CL_XValue; } @@ -602,6 +615,7 @@ Expr::LValueClassification Expr::ClassifyLValue(ASTContext &Ctx) const { case Cl::CL_MemberFunction: return LV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return LV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return LV_ClassTemporary; + case Cl::CL_ArrayTemporary: return LV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return LV_InvalidMessageExpression; case Cl::CL_PRValue: return LV_InvalidExpression; } @@ -622,6 +636,7 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: return MLV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return MLV_ClassTemporary; + case Cl::CL_ArrayTemporary: return MLV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return MLV_InvalidMessageExpression; case Cl::CL_PRValue: return VC.getModifiable() == Cl::CM_LValueCast ? diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 66a88b065ce1..06c41a2e7ea2 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -287,7 +287,9 @@ namespace { /// parameters' function scope indices. const APValue *Arguments; - typedef llvm::DenseMap<const Expr*, APValue> MapTy; + // Note that we intentionally use std::map here so that references to + // values are stable. + typedef std::map<const Expr*, APValue> MapTy; typedef MapTy::const_iterator temp_iterator; /// Temporaries - Temporary lvalues materialized within this stack frame. MapTy Temporaries; @@ -361,11 +363,6 @@ namespace { /// NextCallIndex - The next call index to assign. unsigned NextCallIndex; - typedef llvm::DenseMap<const OpaqueValueExpr*, APValue> MapTy; - /// OpaqueValues - Values used as the common expression in a - /// BinaryConditionalOperator. - MapTy OpaqueValues; - /// BottomFrame - The frame in which evaluation started. This must be /// initialized after CurrentCall and CallStackDepth. CallStackFrame BottomFrame; @@ -394,12 +391,6 @@ namespace { EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false) {} - const APValue *getOpaqueValue(const OpaqueValueExpr *e) const { - MapTy::const_iterator i = OpaqueValues.find(e); - if (i == OpaqueValues.end()) return 0; - return &i->second; - } - void setEvaluatingDecl(const VarDecl *VD, APValue &Value) { EvaluatingDecl = VD; EvaluatingDeclValue = &Value; @@ -1072,8 +1063,8 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, } for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { - if (!CheckConstantExpression(Info, DiagLoc, (*I)->getType(), - Value.getStructField((*I)->getFieldIndex()))) + if (!CheckConstantExpression(Info, DiagLoc, I->getType(), + Value.getStructField(I->getFieldIndex()))) return false; } } @@ -1160,11 +1151,10 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, } template<typename T> -static bool HandleOverflow(EvalInfo &Info, const Expr *E, +static void HandleOverflow(EvalInfo &Info, const Expr *E, const T &SrcValue, QualType DestType) { - Info.Diag(E, diag::note_constexpr_overflow) + Info.CCEDiag(E, diag::note_constexpr_overflow) << SrcValue << DestType; - return false; } static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, @@ -1178,7 +1168,7 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, bool ignored; if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored) & APFloat::opInvalidOp) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1190,7 +1180,7 @@ static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, if (Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), APFloat::rmNearestTiesToEven, &ignored) & APFloat::opOverflow) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1213,7 +1203,7 @@ static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, if (Result.convertFromAPInt(Value, Value.isSigned(), APFloat::rmNearestTiesToEven) & APFloat::opOverflow) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1282,6 +1272,7 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, // Truncate the path to the subobject, and remove any derived-to-base offsets. const RecordDecl *RD = TruncatedType; for (unsigned I = TruncatedElements, N = D.Entries.size(); I != N; ++I) { + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]); if (isVirtualBaseClass(D.Entries[I])) @@ -1294,13 +1285,18 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, return true; } -static void HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj, +static bool HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXRecordDecl *Derived, const CXXRecordDecl *Base, const ASTRecordLayout *RL = 0) { - if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived); + if (!RL) { + if (Derived->isInvalidDecl()) return false; + RL = &Info.Ctx.getASTRecordLayout(Derived); + } + Obj.getLValueOffset() += RL->getBaseClassOffset(Base); Obj.addDecl(Info, E, Base, /*Virtual*/ false); + return true; } static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, @@ -1308,10 +1304,8 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXBaseSpecifier *Base) { const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); - if (!Base->isVirtual()) { - HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl); - return true; - } + if (!Base->isVirtual()) + return HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl); SubobjectDesignator &D = Obj.Designator; if (D.Invalid) @@ -1323,6 +1317,7 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, return false; // Find the virtual base class. + if (DerivedDecl->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl); Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl); Obj.addDecl(Info, E, BaseDecl, /*Virtual*/ true); @@ -1331,24 +1326,29 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, /// Update LVal to refer to the given field, which must be a member of the type /// currently described by LVal. -static void HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, +static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, const FieldDecl *FD, const ASTRecordLayout *RL = 0) { - if (!RL) + if (!RL) { + if (FD->getParent()->isInvalidDecl()) return false; RL = &Info.Ctx.getASTRecordLayout(FD->getParent()); + } unsigned I = FD->getFieldIndex(); LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I)); LVal.addDecl(Info, E, FD); + return true; } /// Update LVal to refer to the given indirect field. -static void HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, +static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, LValue &LVal, const IndirectFieldDecl *IFD) { for (IndirectFieldDecl::chain_iterator C = IFD->chain_begin(), CE = IFD->chain_end(); C != CE; ++C) - HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C)); + if (!HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C))) + return false; + return true; } /// Get the size of the given type in char units. @@ -1952,22 +1952,27 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, // The first class in the path is that of the lvalue. for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) { const CXXRecordDecl *Base = MemPtr.Path[N - I - 1]; - HandleLValueDirectBase(Info, BO, LV, RD, Base); + if (!HandleLValueDirectBase(Info, BO, LV, RD, Base)) + return 0; RD = Base; } // Finally cast to the class containing the member. - HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord()); + if (!HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord())) + return 0; } // Add the member. Note that we cannot build bound member functions here. if (IncludeMember) { - if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) - HandleLValueMember(Info, BO, LV, FD); - else if (const IndirectFieldDecl *IFD = - dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) - HandleLValueIndirectMember(Info, BO, LV, IFD); - else + if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) { + if (!HandleLValueMember(Info, BO, LV, FD)) + return 0; + } else if (const IndirectFieldDecl *IFD = + dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) { + if (!HandleLValueIndirectMember(Info, BO, LV, IFD)) + return 0; + } else { llvm_unreachable("can't construct reference to bound member function"); + } } return MemPtr.getDecl(); @@ -2189,6 +2194,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, Result = APValue(APValue::UninitStruct(), RD->getNumBases(), std::distance(RD->field_begin(), RD->field_end())); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); bool Success = true; @@ -2212,11 +2218,13 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, "base class initializers not in expected order"); ++BaseIt; #endif - HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD, - BaseType->getAsCXXRecordDecl(), &Layout); + if (!HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD, + BaseType->getAsCXXRecordDecl(), &Layout)) + return false; Value = &Result.getStructBase(BasesSeen++); } else if (FieldDecl *FD = (*I)->getMember()) { - HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout); + if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout)) + return false; if (RD->isUnion()) { Result = APValue(FD); Value = &Result.getUnionValue(); @@ -2244,7 +2252,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, *Value = APValue(APValue::UninitStruct(), CD->getNumBases(), std::distance(CD->field_begin(), CD->field_end())); } - HandleLValueMember(Info, (*I)->getInit(), Subobject, FD); + if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD)) + return false; if (CD->isUnion()) Value = &Value->getUnionValue(); else @@ -2268,107 +2277,6 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, return Success; } -namespace { -class HasSideEffect - : public ConstStmtVisitor<HasSideEffect, bool> { - const ASTContext &Ctx; -public: - - HasSideEffect(const ASTContext &C) : Ctx(C) {} - - // Unhandled nodes conservatively default to having side effects. - bool VisitStmt(const Stmt *S) { - return true; - } - - bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } - bool VisitGenericSelectionExpr(const GenericSelectionExpr *E) { - return Visit(E->getResultExpr()); - } - bool VisitDeclRefExpr(const DeclRefExpr *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return false; - } - bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return false; - } - - // We don't want to evaluate BlockExprs multiple times, as they generate - // a ton of code. - bool VisitBlockExpr(const BlockExpr *E) { return true; } - bool VisitPredefinedExpr(const PredefinedExpr *E) { return false; } - bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) - { return Visit(E->getInitializer()); } - bool VisitMemberExpr(const MemberExpr *E) { return Visit(E->getBase()); } - bool VisitIntegerLiteral(const IntegerLiteral *E) { return false; } - bool VisitFloatingLiteral(const FloatingLiteral *E) { return false; } - bool VisitStringLiteral(const StringLiteral *E) { return false; } - bool VisitCharacterLiteral(const CharacterLiteral *E) { return false; } - bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) - { return false; } - bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E) - { return Visit(E->getLHS()) || Visit(E->getRHS()); } - bool VisitChooseExpr(const ChooseExpr *E) - { return Visit(E->getChosenSubExpr(Ctx)); } - bool VisitCastExpr(const CastExpr *E) { return Visit(E->getSubExpr()); } - bool VisitBinAssign(const BinaryOperator *E) { return true; } - bool VisitCompoundAssignOperator(const BinaryOperator *E) { return true; } - bool VisitBinaryOperator(const BinaryOperator *E) - { return Visit(E->getLHS()) || Visit(E->getRHS()); } - bool VisitUnaryPreInc(const UnaryOperator *E) { return true; } - bool VisitUnaryPostInc(const UnaryOperator *E) { return true; } - bool VisitUnaryPreDec(const UnaryOperator *E) { return true; } - bool VisitUnaryPostDec(const UnaryOperator *E) { return true; } - bool VisitUnaryDeref(const UnaryOperator *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return Visit(E->getSubExpr()); - } - bool VisitUnaryOperator(const UnaryOperator *E) { return Visit(E->getSubExpr()); } - - // Has side effects if any element does. - bool VisitInitListExpr(const InitListExpr *E) { - for (unsigned i = 0, e = E->getNumInits(); i != e; ++i) - if (Visit(E->getInit(i))) return true; - if (const Expr *filler = E->getArrayFiller()) - return Visit(filler); - return false; - } - - bool VisitSizeOfPackExpr(const SizeOfPackExpr *) { return false; } -}; - -class OpaqueValueEvaluation { - EvalInfo &info; - OpaqueValueExpr *opaqueValue; - -public: - OpaqueValueEvaluation(EvalInfo &info, OpaqueValueExpr *opaqueValue, - Expr *value) - : info(info), opaqueValue(opaqueValue) { - - // If evaluation fails, fail immediately. - if (!Evaluate(info.OpaqueValues[opaqueValue], info, value)) { - this->opaqueValue = 0; - return; - } - } - - bool hasError() const { return opaqueValue == 0; } - - ~OpaqueValueEvaluation() { - // FIXME: For a recursive constexpr call, an outer stack frame might have - // been using this opaque value too, and will now have to re-evaluate the - // source expression. - if (opaqueValue) info.OpaqueValues.erase(opaqueValue); - } -}; - -} // end anonymous namespace - //===----------------------------------------------------------------------===// // Generic Evaluation //===----------------------------------------------------------------------===// @@ -2509,9 +2417,10 @@ public: } RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { - // Cache the value of the common expression. - OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon()); - if (opaque.hasError()) + // Evaluate and cache the common expression. We treat it as a temporary, + // even though it's not quite the same thing. + if (!Evaluate(Info.CurrentCall->Temporaries[E->getOpaqueValue()], + Info, E->getCommon())) return false; return HandleConditionalOperator(E); @@ -2545,8 +2454,8 @@ public: } RetTy VisitOpaqueValueExpr(const OpaqueValueExpr *E) { - const APValue *Value = Info.getOpaqueValue(E); - if (!Value) { + APValue &Value = Info.CurrentCall->Temporaries[E]; + if (Value.isUninit()) { const Expr *Source = E->getSourceExpr(); if (!Source) return Error(E); @@ -2556,7 +2465,7 @@ public: } return StmtVisitorTy::Visit(Source); } - return DerivedSuccess(*Value, E); + return DerivedSuccess(Value, E); } RetTy VisitCallExpr(const CallExpr *E) { @@ -2773,9 +2682,11 @@ public: assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); (void)BaseTy; - HandleLValueMember(this->Info, E, Result, FD); + if (!HandleLValueMember(this->Info, E, Result, FD)) + return false; } else if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(MD)) { - HandleLValueIndirectMember(this->Info, E, Result, IFD); + if (!HandleLValueIndirectMember(this->Info, E, Result, IFD)) + return false; } else return this->Error(E); @@ -2970,6 +2881,9 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { if (E->isTypeOperand()) return Success(E); CXXRecordDecl *RD = E->getExprOperand()->getType()->getAsCXXRecordDecl(); + // FIXME: The standard says "a typeid expression whose operand is of a + // polymorphic class type" is not a constant expression, but it probably + // means "a typeid expression whose operand is potentially evaluated". if (RD && RD->isPolymorphic()) { Info.Diag(E, diag::note_constexpr_typeid_polymorphic) << E->getExprOperand()->getType() @@ -3073,7 +2987,7 @@ public: bool VisitUnaryAddrOf(const UnaryOperator *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } - bool VisitObjCNumericLiteral(const ObjCNumericLiteral *E) + bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { return Success(E); } bool VisitAddrLabelExpr(const AddrLabelExpr *E) { return Success(E); } @@ -3373,6 +3287,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, Result = APValue(APValue::UninitStruct(), CD ? CD->getNumBases() : 0, std::distance(RD->field_begin(), RD->field_end())); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); if (CD) { @@ -3381,7 +3296,8 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, End = CD->bases_end(); I != End; ++I, ++Index) { const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl(); LValue Subobject = This; - HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout); + if (!HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout)) + return false; if (!HandleClassZeroInitialization(Info, E, Base, Subobject, Result.getStructBase(Index))) return false; @@ -3391,15 +3307,16 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, for (RecordDecl::field_iterator I = RD->field_begin(), End = RD->field_end(); I != End; ++I) { // -- if T is a reference type, no initialization is performed. - if ((*I)->getType()->isReferenceType()) + if (I->getType()->isReferenceType()) continue; LValue Subobject = This; - HandleLValueMember(Info, E, Subobject, *I, &Layout); + if (!HandleLValueMember(Info, E, Subobject, *I, &Layout)) + return false; - ImplicitValueInitExpr VIE((*I)->getType()); + ImplicitValueInitExpr VIE(I->getType()); if (!EvaluateInPlace( - Result.getStructField((*I)->getFieldIndex()), Info, Subobject, &VIE)) + Result.getStructField(I->getFieldIndex()), Info, Subobject, &VIE)) return false; } @@ -3408,6 +3325,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); + if (RD->isInvalidDecl()) return false; if (RD->isUnion()) { // C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the // object's first non-static named data member is zero-initialized @@ -3418,9 +3336,10 @@ bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { } LValue Subobject = This; - HandleLValueMember(Info, E, Subobject, *I); + if (!HandleLValueMember(Info, E, Subobject, *I)) + return false; Result = APValue(*I); - ImplicitValueInitExpr VIE((*I)->getType()); + ImplicitValueInitExpr VIE(I->getType()); return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, &VIE); } @@ -3470,6 +3389,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { return false; const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); if (RD->isUnion()) { @@ -3484,7 +3404,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; LValue Subobject = This; - HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout); + if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) + return false; return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } @@ -3507,15 +3428,16 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // FIXME: Diagnostics here should point to the end of the initializer // list, not the start. - HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, Subobject, - *Field, &Layout); + if (!HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, + Subobject, *Field, &Layout)) + return false; // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); if (!EvaluateInPlace( - Result.getStructField((*Field)->getFieldIndex()), + Result.getStructField(Field->getFieldIndex()), Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) { if (!Info.keepEvaluatingAfterFailure()) return false; @@ -3528,6 +3450,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { const CXXConstructorDecl *FD = E->getConstructor(); + if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl()) return false; + bool ZeroInit = E->requiresZeroInitialization(); if (CheckTrivialDefaultConstructor(Info, E->getExprLoc(), FD, ZeroInit)) { // If we've already performed zero-initialization, we're already done. @@ -3870,8 +3794,24 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool Success = true; + assert((!Result.isArray() || Result.getArrayInitializedElts() == 0) && + "zero-initialized array shouldn't have any initialized elts"); + APValue Filler; + if (Result.isArray() && Result.hasArrayFiller()) + Filler = Result.getArrayFiller(); + Result = APValue(APValue::UninitArray(), E->getNumInits(), CAT->getSize().getZExtValue()); + + // If the array was previously zero-initialized, preserve the + // zero-initialized values. + if (!Filler.isUninit()) { + for (unsigned I = 0, E = Result.getArrayInitializedElts(); I != E; ++I) + Result.getArrayInitializedElt(I) = Filler; + if (Result.hasArrayFiller()) + Result.getArrayFiller() = Filler; + } + LValue Subobject = This; Subobject.addArray(Info, E, CAT); unsigned Index = 0; @@ -3898,15 +3838,29 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); - if (!CAT) - return Error(E); + // FIXME: The Subobject here isn't necessarily right. This rarely matters, + // but sometimes does: + // struct S { constexpr S() : p(&p) {} void *p; }; + // S s[10]; + LValue Subobject = This; - bool HadZeroInit = !Result.isUninit(); - if (!HadZeroInit) - Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); - if (!Result.hasArrayFiller()) - return true; + APValue *Value = &Result; + bool HadZeroInit = true; + QualType ElemTy = E->getType(); + while (const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(ElemTy)) { + Subobject.addArray(Info, E, CAT); + HadZeroInit &= !Value->isUninit(); + if (!HadZeroInit) + *Value = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); + if (!Value->hasArrayFiller()) + return true; + Value = &Value->getArrayFiller(); + ElemTy = CAT->getElementType(); + } + + if (!ElemTy->isRecordType()) + return Error(E); const CXXConstructorDecl *FD = E->getConstructor(); @@ -3916,17 +3870,15 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { return true; if (ZeroInit) { - LValue Subobject = This; - Subobject.addArray(Info, E, CAT); - ImplicitValueInitExpr VIE(CAT->getElementType()); - return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE); + ImplicitValueInitExpr VIE(ElemTy); + return EvaluateInPlace(*Value, Info, Subobject, &VIE); } const CXXRecordDecl *RD = FD->getParent(); if (RD->isUnion()) - Result.getArrayFiller() = APValue((FieldDecl*)0); + *Value = APValue((FieldDecl*)0); else - Result.getArrayFiller() = + *Value = APValue(APValue::UninitStruct(), RD->getNumBases(), std::distance(RD->field_begin(), RD->field_end())); return true; @@ -3938,23 +3890,16 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition)) return false; - // FIXME: The Subobject here isn't necessarily right. This rarely matters, - // but sometimes does: - // struct S { constexpr S() : p(&p) {} void *p; }; - // S s[10]; - LValue Subobject = This; - Subobject.addArray(Info, E, CAT); - if (ZeroInit && !HadZeroInit) { - ImplicitValueInitExpr VIE(CAT->getElementType()); - if (!EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE)) + ImplicitValueInitExpr VIE(ElemTy); + if (!EvaluateInPlace(*Value, Info, Subobject, &VIE)) return false; } llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs()); return HandleConstructorCall(E->getExprLoc(), Subobject, Args, cast<CXXConstructorDecl>(Definition), - Info, Result.getArrayFiller()); + Info, *Value); } //===----------------------------------------------------------------------===// @@ -4288,10 +4233,16 @@ QualType IntExprEvaluator::GetObjectType(APValue::LValueBase B) { } bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E) { - // TODO: Perhaps we should let LLVM lower this? LValue Base; - if (!EvaluatePointer(E->getArg(0), Base, Info)) - return false; + + { + // The operand of __builtin_object_size is never evaluated for side-effects. + // If there are any, but we can determine the pointed-to object anyway, then + // ignore the side-effects. + SpeculativeEvaluationRAII SpeculativeEval(Info); + if (!EvaluatePointer(E->getArg(0), Base, Info)) + return false; + } // If we can prove the base is null, lower to zero now. if (!Base.getLValueBase()) return Success(0, E); @@ -4323,14 +4274,17 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { if (TryEvaluateBuiltinObjectSize(E)) return true; - // If evaluating the argument has side-effects we can't determine - // the size of the object and lower it to unknown now. + // If evaluating the argument has side-effects, we can't determine the size + // of the object, and so we lower it to unknown now. CodeGen relies on us to + // handle all cases where the expression has side-effects. if (E->getArg(0)->HasSideEffects(Info.Ctx)) { if (E->getArg(1)->EvaluateKnownConstInt(Info.Ctx).getZExtValue() <= 1) return Success(-1ULL, E); return Success(0, E); } + // Expression had no side effects, but we couldn't statically determine the + // size of the referenced object. return Error(E); } @@ -5280,6 +5234,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { if (!RT) return Error(OOE); RecordDecl *RD = RT->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); unsigned i = MemberDecl->getFieldIndex(); assert(i < RL.getFieldCount() && "offsetof field in wrong type"); @@ -5301,6 +5256,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { if (!RT) return Error(OOE); RecordDecl *RD = RT->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); // Find the base class itself. @@ -6385,10 +6341,6 @@ bool Expr::isEvaluatable(const ASTContext &Ctx) const { return EvaluateAsRValue(Result, Ctx) && !Result.HasSideEffects; } -bool Expr::HasSideEffects(const ASTContext &Ctx) const { - return HasSideEffect(Ctx).Visit(this); -} - APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx) const { EvalResult EvalResult; bool Result = EvaluateAsRValue(EvalResult, Ctx); @@ -6501,7 +6453,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXDependentScopeMemberExprClass: case Expr::UnresolvedMemberExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCEncodeExprClass: diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp index 0027dbf1915b..ce1244c54272 100644 --- a/lib/AST/ItaniumCXXABI.cpp +++ b/lib/AST/ItaniumCXXABI.cpp @@ -39,7 +39,7 @@ public: return 1; } - CallingConv getDefaultMethodCallConv() const { + CallingConv getDefaultMethodCallConv(bool isVariadic) const { return CC_C; } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 0d405f1f57f2..7c7a5e5de387 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -657,7 +657,7 @@ void CXXNameMangler::mangleFloat(const llvm::APFloat &f) { // mistake; see the discussion on cxx-abi-dev beginning on // 2012-01-16. - // Our requirements here are just barely wierd enough to justify + // Our requirements here are just barely weird enough to justify // using a custom algorithm instead of post-processing APInt::toString(). llvm::APInt valueBits = f.bitcastToAPInt(); @@ -1032,17 +1032,14 @@ static const FieldDecl *FindFirstNamedDataMember(const RecordDecl *RD) { for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { - const FieldDecl *FD = *I; + if (I->getIdentifier()) + return *I; - if (FD->getIdentifier()) - return FD; - - if (const RecordType *RT = FD->getType()->getAs<RecordType>()) { + if (const RecordType *RT = I->getType()->getAs<RecordType>()) if (const FieldDecl *NamedDataMember = FindFirstNamedDataMember(RT->getDecl())) return NamedDataMember; } - } // We didn't find a named data member. return 0; @@ -1892,12 +1889,23 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { } // <type> ::= <function-type> -// <function-type> ::= F [Y] <bare-function-type> E +// <function-type> ::= [<CV-qualifiers>] F [Y] +// <bare-function-type> [<ref-qualifier>] E +// (Proposal to cxx-abi-dev, 2012-05-11) void CXXNameMangler::mangleType(const FunctionProtoType *T) { + // Mangle CV-qualifiers, if present. These are 'this' qualifiers, + // e.g. "const" in "int (A::*)() const". + mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals())); + Out << 'F'; + // FIXME: We don't have enough information in the AST to produce the 'Y' // encoding for extern "C" function types. mangleBareFunctionType(T, /*MangleReturnType=*/true); + + // Mangle the ref-qualifier, if present. + mangleRefQualifier(T->getRefQualifier()); + Out << 'E'; } void CXXNameMangler::mangleType(const FunctionNoProtoType *T) { @@ -1990,8 +1998,6 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) { mangleType(QualType(T->getClass(), 0)); QualType PointeeType = T->getPointeeType(); if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) { - mangleQualifiers(Qualifiers::fromCVRMask(FPT->getTypeQuals())); - mangleRefQualifier(FPT->getRefQualifier()); mangleType(FPT); // Itanium C++ ABI 5.1.8: @@ -2005,9 +2011,11 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) { // which the function is a member is considered part of the type of // function. + // Given that we already substitute member function pointers as a + // whole, the net effect of this rule is just to unconditionally + // suppress substitution on the function type in a member pointer. // We increment the SeqID here to emulate adding an entry to the - // substitution table. We can't actually add it because we don't want this - // particular function type to be substituted. + // substitution table. ++SeqID; } else mangleType(PointeeType); @@ -2390,7 +2398,7 @@ recurse: case Expr::ObjCProtocolExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCSubscriptRefExprClass: @@ -2981,7 +2989,7 @@ void CXXNameMangler::mangleFunctionParam(const ParmVarDecl *parm) { // Top-level qualifiers. We don't have to worry about arrays here, // because parameters declared as arrays should already have been - // tranformed to have pointer type. FIXME: apparently these don't + // transformed to have pointer type. FIXME: apparently these don't // get mangled if used as an rvalue of a known non-class type? assert(!parm->getType()->isArrayType() && "parameter's type is still an array type?"); @@ -3124,7 +3132,7 @@ void CXXNameMangler::mangleTemplateArg(const NamedDecl *P, break; } case TemplateArgument::Integral: - mangleIntegerLiteral(A.getIntegralType(), *A.getAsIntegral()); + mangleIntegerLiteral(A.getIntegralType(), A.getAsIntegral()); break; case TemplateArgument::Declaration: { assert(P && "Missing template parameter for declaration argument"); diff --git a/lib/AST/LambdaMangleContext.cpp b/lib/AST/LambdaMangleContext.cpp index f5272a7fdbce..6f4fe2d4b4eb 100644 --- a/lib/AST/LambdaMangleContext.cpp +++ b/lib/AST/LambdaMangleContext.cpp @@ -11,7 +11,9 @@ // the Itanium C++ ABI mangling numbers for lambda expressions. // //===----------------------------------------------------------------------===// + #include "clang/AST/LambdaMangleContext.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" using namespace clang; diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 73c9f5778f41..d5f83719ec62 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -40,7 +40,11 @@ static void mangleFunctionBlock(MangleContext &Context, StringRef Outer, const BlockDecl *BD, raw_ostream &Out) { - Out << "__" << Outer << "_block_invoke_" << Context.getBlockId(BD, true); + unsigned discriminator = Context.getBlockId(BD, true); + if (discriminator == 0) + Out << "__" << Outer << "_block_invoke"; + else + Out << "__" << Outer << "_block_invoke_" << discriminator+1; } static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) { @@ -62,8 +66,20 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) { void MangleContext::anchor() { } void MangleContext::mangleGlobalBlock(const BlockDecl *BD, + const NamedDecl *ID, raw_ostream &Out) { - Out << "__block_global_" << getBlockId(BD, false); + unsigned discriminator = getBlockId(BD, false); + if (ID) { + if (shouldMangleDeclName(ID)) + mangleName(ID, Out); + else { + Out << ID->getIdentifier()->getName(); + } + } + if (discriminator == 0) + Out << "_block_invoke"; + else + Out << "_block_invoke_" << discriminator+1; } void MangleContext::mangleCtorBlock(const CXXConstructorDecl *CD, @@ -99,8 +115,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD, mangleObjCMethodName(Method, Stream); } else { const NamedDecl *ND = cast<NamedDecl>(DC); - if (IdentifierInfo *II = ND->getIdentifier()) - Stream << II->getName(); + if (!shouldMangleDeclName(ND) && ND->getIdentifier()) + Stream << ND->getIdentifier()->getName(); else { // FIXME: We were doing a mangleUnqualifiedName() before, but that's // a private member of a class that will soon itself be private to the @@ -131,12 +147,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, } void MangleContext::mangleBlock(const BlockDecl *BD, - raw_ostream &Out) { + raw_ostream &Out, + const NamedDecl *ID) { const DeclContext *DC = BD->getDeclContext(); while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC)) DC = DC->getParent(); if (DC->isFunctionOrMethod()) mangleBlock(DC, BD, Out); else - mangleGlobalBlock(BD, Out); + mangleGlobalBlock(BD, ID, Out); } diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index f33d6fe1f563..51308ea0c0f5 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -29,8 +29,8 @@ public: unsigned getMemberPointerSize(const MemberPointerType *MPT) const; - CallingConv getDefaultMethodCallConv() const { - if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) + CallingConv getDefaultMethodCallConv(bool isVariadic) const { + if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) return CC_X86ThisCall; else return CC_C; diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index ba9856a8432f..e2cee7f52cfb 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -21,6 +21,8 @@ #include "clang/AST/ExprCXX.h" #include "clang/Basic/ABI.h" +#include <map> + using namespace clang; namespace { @@ -31,36 +33,59 @@ class MicrosoftCXXNameMangler { MangleContext &Context; raw_ostream &Out; + // FIXME: audit the performance of BackRefMap as it might do way too many + // copying of strings. + typedef std::map<std::string, unsigned> BackRefMap; + BackRefMap NameBackReferences; + bool UseNameBackReferences; + + typedef llvm::DenseMap<void*, unsigned> ArgBackRefMap; + ArgBackRefMap TypeBackReferences; + ASTContext &getASTContext() const { return Context.getASTContext(); } public: MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) - : Context(C), Out(Out_) { } + : Context(C), Out(Out_), UseNameBackReferences(true) { } + + raw_ostream &getStream() const { return Out; } - void mangle(const NamedDecl *D, StringRef Prefix = "?"); + void mangle(const NamedDecl *D, StringRef Prefix = "\01?"); void mangleName(const NamedDecl *ND); void mangleFunctionEncoding(const FunctionDecl *FD); void mangleVariableEncoding(const VarDecl *VD); void mangleNumber(int64_t Number); - void mangleType(QualType T); + void mangleNumber(const llvm::APSInt &Value); + void mangleType(QualType T, SourceRange Range); private: + void disableBackReferences() { UseNameBackReferences = false; } void mangleUnqualifiedName(const NamedDecl *ND) { mangleUnqualifiedName(ND, ND->getDeclName()); } void mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name); void mangleSourceName(const IdentifierInfo *II); void manglePostfix(const DeclContext *DC, bool NoFunction=false); - void mangleOperatorName(OverloadedOperatorKind OO); + void mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc); void mangleQualifiers(Qualifiers Quals, bool IsMember); + void mangleUnscopedTemplateName(const TemplateDecl *ND); + void mangleTemplateInstantiationName(const TemplateDecl *TD, + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs); void mangleObjCMethodName(const ObjCMethodDecl *MD); + void mangleLocalName(const FunctionDecl *FD); + + void mangleTypeRepeated(QualType T, SourceRange Range); // Declare manglers for every type class. #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) -#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T); +#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \ + SourceRange Range); #include "clang/AST/TypeNodes.def" +#undef ABSTRACT_TYPE +#undef NON_CANONICAL_TYPE +#undef TYPE void mangleType(const TagType*); void mangleType(const FunctionType *T, const FunctionDecl *D, @@ -69,8 +94,12 @@ private: void mangleExtraDimensions(QualType T); void mangleFunctionClass(const FunctionDecl *FD); void mangleCallingConvention(const FunctionType *T, bool IsInstMethod = false); + void mangleIntegerLiteral(QualType T, const llvm::APSInt &Number); void mangleThrowSpecification(const FunctionProtoType *T); + void mangleTemplateArgs( + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs); + }; /// MicrosoftMangleContext - Overrides the default MangleContext for the @@ -157,15 +186,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, StringRef Prefix) { // MSVC doesn't mangle C++ names the same way it mangles extern "C" names. // Therefore it's really important that we don't decorate the - // name with leading underscores or leading/trailing at signs. So, emit a - // asm marker at the start so we get the name right. - Out << '\01'; // LLVM IR Marker for __asm("foo") + // name with leading underscores or leading/trailing at signs. So, by + // default, we emit an asm marker at the start so we get the name right. + // Callers can override this with a custom prefix. // Any decl can be declared with __asm("foo") on it, and this takes precedence // over all other naming in the .o file. if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) { // If we have an asm name, then we use it as the mangling. - Out << ALA->getLabel(); + Out << '\01' << ALA->getLabel(); return; } @@ -176,7 +205,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, mangleFunctionEncoding(FD); else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) mangleVariableEncoding(VD); - // TODO: Fields? Can MSVC even mangle them? + else { + // TODO: Fields? Can MSVC even mangle them? + // Issue a diagnostic for now. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this declaration yet"); + Diags.Report(D->getLocation(), DiagID) + << D->getSourceRange(); + } } void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { @@ -188,7 +225,7 @@ void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { // We should never ever see a FunctionNoProtoType at this point. // We don't even know how to mangle their types anyway :). - const FunctionProtoType *FT = cast<FunctionProtoType>(FD->getType()); + const FunctionProtoType *FT = FD->getType()->castAs<FunctionProtoType>(); bool InStructor = false, InInstMethod = false; const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); @@ -232,16 +269,17 @@ void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) { // ::= <type> A # pointers, references, arrays // Pointers and references are odd. The type of 'int * const foo;' gets // mangled as 'QAHA' instead of 'PAHB', for example. - QualType Ty = VD->getType(); + TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc(); + QualType Ty = TL.getType(); if (Ty->isPointerType() || Ty->isReferenceType()) { - mangleType(Ty); + mangleType(Ty, TL.getSourceRange()); Out << 'A'; - } else if (Ty->isArrayType()) { + } else if (const ArrayType *AT = getASTContext().getAsArrayType(Ty)) { // Global arrays are funny, too. - mangleType(cast<ArrayType>(Ty.getTypePtr()), true); + mangleType(AT, true); Out << 'A'; } else { - mangleType(Ty.getLocalUnqualifiedType()); + mangleType(Ty.getLocalUnqualifiedType(), TL.getSourceRange()); mangleQualifiers(Ty.getLocalQualifiers(), false); } } @@ -266,35 +304,156 @@ void MicrosoftCXXNameMangler::mangleName(const NamedDecl *ND) { } void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) { - // <number> ::= [?] <decimal digit> # <= 9 - // ::= [?] <hex digit>+ @ # > 9; A = 0, B = 1, etc... + // <number> ::= [?] <decimal digit> # 1 <= Number <= 10 + // ::= [?] <hex digit>+ @ # 0 or > 9; A = 0, B = 1, etc... + // ::= [?] @ # 0 (alternate mangling, not emitted by VC) if (Number < 0) { Out << '?'; Number = -Number; } - if (Number >= 1 && Number <= 10) { + // There's a special shorter mangling for 0, but Microsoft + // chose not to use it. Instead, 0 gets mangled as "A@". Oh well... + if (Number >= 1 && Number <= 10) Out << Number-1; - } else { + else { // We have to build up the encoding in reverse order, so it will come // out right when we write it out. char Encoding[16]; char *EndPtr = Encoding+sizeof(Encoding); char *CurPtr = EndPtr; - while (Number) { + do { *--CurPtr = 'A' + (Number % 16); Number /= 16; + } while (Number); + Out.write(CurPtr, EndPtr-CurPtr); + Out << '@'; + } +} + +void MicrosoftCXXNameMangler::mangleNumber(const llvm::APSInt &Value) { + if (Value.isSigned() && Value.isNegative()) { + Out << '?'; + mangleNumber(llvm::APSInt(Value.abs())); + return; + } + llvm::APSInt Temp(Value); + if (Value.uge(1) && Value.ule(10)) { + --Temp; + Temp.print(Out, false); + } else { + // We have to build up the encoding in reverse order, so it will come + // out right when we write it out. + char Encoding[64]; + char *EndPtr = Encoding+sizeof(Encoding); + char *CurPtr = EndPtr; + llvm::APSInt NibbleMask(Value.getBitWidth(), Value.isUnsigned()); + NibbleMask = 0xf; + for (int i = 0, e = Value.getActiveBits() / 4; i != e; ++i) { + *--CurPtr = 'A' + Temp.And(NibbleMask).getLimitedValue(0xf); + Temp = Temp.lshr(4); } Out.write(CurPtr, EndPtr-CurPtr); Out << '@'; } } +static const TemplateDecl * +isTemplate(const NamedDecl *ND, + SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // Check if we have a function template. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)){ + if (const TemplateDecl *TD = FD->getPrimaryTemplate()) { + if (FD->getTemplateSpecializationArgsAsWritten()) { + const ASTTemplateArgumentListInfo *ArgList = + FD->getTemplateSpecializationArgsAsWritten(); + TemplateArgs.append(ArgList->getTemplateArgs(), + ArgList->getTemplateArgs() + + ArgList->NumTemplateArgs); + } else { + const TemplateArgumentList *ArgList = + FD->getTemplateSpecializationArgs(); + TemplateArgumentListInfo LI; + for (unsigned i = 0, e = ArgList->size(); i != e; ++i) + TemplateArgs.push_back(TemplateArgumentLoc(ArgList->get(i), + FD->getTypeSourceInfo())); + } + return TD; + } + } + + // Check if we have a class template. + if (const ClassTemplateSpecializationDecl *Spec = + dyn_cast<ClassTemplateSpecializationDecl>(ND)) { + TypeSourceInfo *TSI = Spec->getTypeAsWritten(); + if (TSI) { + TemplateSpecializationTypeLoc &TSTL = + cast<TemplateSpecializationTypeLoc>(TSI->getTypeLoc()); + TemplateArgumentListInfo LI(TSTL.getLAngleLoc(), TSTL.getRAngleLoc()); + for (unsigned i = 0, e = TSTL.getNumArgs(); i != e; ++i) + TemplateArgs.push_back(TSTL.getArgLoc(i)); + } else { + TemplateArgumentListInfo LI; + const TemplateArgumentList &ArgList = + Spec->getTemplateArgs(); + for (unsigned i = 0, e = ArgList.size(); i != e; ++i) + TemplateArgs.push_back(TemplateArgumentLoc(ArgList[i], + TemplateArgumentLocInfo())); + } + return Spec->getSpecializedTemplate(); + } + + return 0; +} + void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name) { // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> // ::= <source-name> + // ::= <template-name> + SmallVector<TemplateArgumentLoc, 2> TemplateArgs; + // Check if we have a template. + if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) { + // We have a template. + // Here comes the tricky thing: if we need to mangle something like + // void foo(A::X<Y>, B::X<Y>), + // the X<Y> part is aliased. However, if you need to mangle + // void foo(A::X<A::Y>, A::X<B::Y>), + // the A::X<> part is not aliased. + // That said, from the mangler's perspective we have a structure like this: + // namespace[s] -> type[ -> template-parameters] + // but from the Clang perspective we have + // type [ -> template-parameters] + // \-> namespace[s] + // What we do is we create a new mangler, mangle the same type (without + // a namespace suffix) using the extra mangler with back references + // disabled (to avoid infinite recursion) and then use the mangled type + // name as a key to check the mangling of different types for aliasing. + + std::string BackReferenceKey; + BackRefMap::iterator Found; + if (UseNameBackReferences) { + llvm::raw_string_ostream Stream(BackReferenceKey); + MicrosoftCXXNameMangler Extra(Context, Stream); + Extra.disableBackReferences(); + Extra.mangleUnqualifiedName(ND, Name); + Stream.flush(); + + Found = NameBackReferences.find(BackReferenceKey); + } + if (!UseNameBackReferences || Found == NameBackReferences.end()) { + mangleTemplateInstantiationName(TD, TemplateArgs); + if (UseNameBackReferences && NameBackReferences.size() < 10) { + size_t Size = NameBackReferences.size(); + NameBackReferences[BackReferenceKey] = Size; + } + } else { + Out << Found->second; + } + return; + } + switch (Name.getNameKind()) { case DeclarationName::Identifier: { if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) { @@ -349,12 +508,17 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, break; case DeclarationName::CXXOperatorName: - mangleOperatorName(Name.getCXXOverloadedOperator()); + mangleOperatorName(Name.getCXXOverloadedOperator(), ND->getLocation()); break; - case DeclarationName::CXXLiteralOperatorName: + case DeclarationName::CXXLiteralOperatorName: { // FIXME: Was this added in VS2010? Does MS even know how to mangle this? - llvm_unreachable("Don't know how to mangle literal operators yet!"); + DiagnosticsEngine Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this literal operator yet"); + Diags.Report(ND->getLocation(), DiagID); + break; + } case DeclarationName::CXXUsingDirective: llvm_unreachable("Can't mangle a using directive name!"); @@ -364,8 +528,6 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, bool NoFunction) { // <postfix> ::= <unqualified-name> [<postfix>] - // ::= <template-postfix> <template-args> [<postfix>] - // ::= <template-param> // ::= <substitution> [<postfix>] if (!DC) return; @@ -386,13 +548,16 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, return; else if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(DC)) mangleObjCMethodName(Method); + else if (const FunctionDecl *Func = dyn_cast<FunctionDecl>(DC)) + mangleLocalName(Func); else { mangleUnqualifiedName(cast<NamedDecl>(DC)); manglePostfix(DC->getParent(), NoFunction); } } -void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { +void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, + SourceLocation Loc) { switch (OO) { // ?0 # constructor // ?1 # destructor @@ -509,8 +674,13 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { // <operator-name> ::= ?_V # delete[] case OO_Array_Delete: Out << "?_V"; break; - case OO_Conditional: - llvm_unreachable("Don't know how to mangle ?:"); + case OO_Conditional: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this conditional operator yet"); + Diags.Report(Loc, DiagID); + break; + } case OO_None: case NUM_OVERLOADED_OPERATORS: @@ -520,13 +690,141 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { void MicrosoftCXXNameMangler::mangleSourceName(const IdentifierInfo *II) { // <source name> ::= <identifier> @ - Out << II->getName() << '@'; + std::string key = II->getNameStart(); + BackRefMap::iterator Found; + if (UseNameBackReferences) + Found = NameBackReferences.find(key); + if (!UseNameBackReferences || Found == NameBackReferences.end()) { + Out << II->getName() << '@'; + if (UseNameBackReferences && NameBackReferences.size() < 10) { + size_t Size = NameBackReferences.size(); + NameBackReferences[key] = Size; + } + } else { + Out << Found->second; + } } void MicrosoftCXXNameMangler::mangleObjCMethodName(const ObjCMethodDecl *MD) { Context.mangleObjCMethodName(MD, Out); } +// Find out how many function decls live above this one and return an integer +// suitable for use as the number in a numbered anonymous scope. +// TODO: Memoize. +static unsigned getLocalNestingLevel(const FunctionDecl *FD) { + const DeclContext *DC = FD->getParent(); + int level = 1; + + while (DC && !DC->isTranslationUnit()) { + if (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC)) level++; + DC = DC->getParent(); + } + + return 2*level; +} + +void MicrosoftCXXNameMangler::mangleLocalName(const FunctionDecl *FD) { + // <nested-name> ::= <numbered-anonymous-scope> ? <mangled-name> + // <numbered-anonymous-scope> ::= ? <number> + // Even though the name is rendered in reverse order (e.g. + // A::B::C is rendered as C@B@A), VC numbers the scopes from outermost to + // innermost. So a method bar in class C local to function foo gets mangled + // as something like: + // ?bar@C@?1??foo@@YAXXZ@QAEXXZ + // This is more apparent when you have a type nested inside a method of a + // type nested inside a function. A method baz in class D local to method + // bar of class C local to function foo gets mangled as: + // ?baz@D@?3??bar@C@?1??foo@@YAXXZ@QAEXXZ@QAEXXZ + // This scheme is general enough to support GCC-style nested + // functions. You could have a method baz of class C inside a function bar + // inside a function foo, like so: + // ?baz@C@?3??bar@?1??foo@@YAXXZ@YAXXZ@QAEXXZ + int NestLevel = getLocalNestingLevel(FD); + Out << '?'; + mangleNumber(NestLevel); + Out << '?'; + mangle(FD, "?"); +} + +void MicrosoftCXXNameMangler::mangleTemplateInstantiationName( + const TemplateDecl *TD, + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // <template-name> ::= <unscoped-template-name> <template-args> + // ::= <substitution> + // Always start with the unqualified name. + + // Templates have their own context for back references. + BackRefMap TemplateContext; + NameBackReferences.swap(TemplateContext); + + mangleUnscopedTemplateName(TD); + mangleTemplateArgs(TemplateArgs); + + NameBackReferences.swap(TemplateContext); +} + +void +MicrosoftCXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *TD) { + // <unscoped-template-name> ::= ?$ <unqualified-name> + Out << "?$"; + mangleUnqualifiedName(TD); +} + +void +MicrosoftCXXNameMangler::mangleIntegerLiteral(QualType T, + const llvm::APSInt &Value) { + // <integer-literal> ::= $0 <number> + Out << "$0"; + // Make sure booleans are encoded as 0/1. + if (T->isBooleanType()) + Out << (Value.getBoolValue() ? "0" : "A@"); + else + mangleNumber(Value); +} + +void +MicrosoftCXXNameMangler::mangleTemplateArgs( + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // <template-args> ::= {<type> | <integer-literal>}+ @ + unsigned NumTemplateArgs = TemplateArgs.size(); + for (unsigned i = 0; i < NumTemplateArgs; ++i) { + const TemplateArgumentLoc &TAL = TemplateArgs[i]; + const TemplateArgument &TA = TAL.getArgument(); + switch (TA.getKind()) { + case TemplateArgument::Null: + llvm_unreachable("Can't mangle null template arguments!"); + case TemplateArgument::Type: + mangleType(TA.getAsType(), TAL.getSourceRange()); + break; + case TemplateArgument::Integral: + mangleIntegerLiteral(TA.getIntegralType(), TA.getAsIntegral()); + break; + case TemplateArgument::Expression: { + // See if this is a constant expression. + Expr *TAE = TA.getAsExpr(); + llvm::APSInt Value; + if (TAE->isIntegerConstantExpr(Value, Context.getASTContext())) { + mangleIntegerLiteral(TAE->getType(), Value); + break; + } + /* fallthrough */ + } default: { + // Issue a diagnostic. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this %select{ERROR|ERROR|pointer/reference|ERROR|" + "template|template pack expansion|expression|parameter pack}0 " + "template argument yet"); + Diags.Report(TAL.getLocation(), DiagID) + << TA.getKind() + << TAL.getSourceRange(); + } + } + } + Out << '@'; +} + void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals, bool IsMember) { // <cvr-qualifiers> ::= [E] [F] [I] <base-cvr-qualifiers> @@ -610,7 +908,29 @@ void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals, // FIXME: For now, just drop all extension qualifiers on the floor. } -void MicrosoftCXXNameMangler::mangleType(QualType T) { +void MicrosoftCXXNameMangler::mangleTypeRepeated(QualType T, SourceRange Range) { + void *TypePtr = getASTContext().getCanonicalType(T).getAsOpaquePtr(); + ArgBackRefMap::iterator Found = TypeBackReferences.find(TypePtr); + + if (Found == TypeBackReferences.end()) { + size_t OutSizeBefore = Out.GetNumBytesInBuffer(); + + mangleType(T,Range); + + // See if it's worth creating a back reference. + // Only types longer than 1 character are considered + // and only 10 back references slots are available: + bool LongerThanOneChar = (Out.GetNumBytesInBuffer() - OutSizeBefore > 1); + if (LongerThanOneChar && TypeBackReferences.size() < 10) { + size_t Size = TypeBackReferences.size(); + TypeBackReferences[TypePtr] = Size; + } + } else { + Out << Found->second; + } +} + +void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range) { // Only operate on the canonical type! T = getASTContext().getCanonicalType(T); @@ -644,18 +964,22 @@ void MicrosoftCXXNameMangler::mangleType(QualType T) { switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) \ -case Type::CLASS: \ -llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \ -return; + case Type::CLASS: \ + llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \ + return; #define TYPE(CLASS, PARENT) \ -case Type::CLASS: \ -mangleType(static_cast<const CLASS##Type*>(T.getTypePtr())); \ -break; + case Type::CLASS: \ + mangleType(static_cast<const CLASS##Type*>(T.getTypePtr()), Range); \ + break; #include "clang/AST/TypeNodes.def" +#undef ABSTRACT_TYPE +#undef NON_CANONICAL_TYPE +#undef TYPE } } -void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) { +void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, + SourceRange Range) { // <type> ::= <builtin-type> // <builtin-type> ::= X # void // ::= C # signed char @@ -713,24 +1037,32 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) { case BuiltinType::ObjCId: Out << "PAUobjc_object@@"; break; case BuiltinType::ObjCClass: Out << "PAUobjc_class@@"; break; case BuiltinType::ObjCSel: Out << "PAUobjc_selector@@"; break; + + case BuiltinType::NullPtr: Out << "$$T"; break; case BuiltinType::Char16: case BuiltinType::Char32: - case BuiltinType::Half: - case BuiltinType::NullPtr: - assert(0 && "Don't know how to mangle this type yet"); + case BuiltinType::Half: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this built-in %0 type yet"); + Diags.Report(Range.getBegin(), DiagID) + << T->getName(Context.getASTContext().getPrintingPolicy()) + << Range; + break; + } } } // <type> ::= <function-type> -void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T) { +void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T, + SourceRange) { // Structors only appear in decls, so at this point we know it's not a // structor type. - // I'll probably have mangleType(MemberPointerType) call the mangleType() - // method directly. mangleType(T, NULL, false, false); } -void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T) { +void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T, + SourceRange) { llvm_unreachable("Can't mangle K&R function prototypes"); } @@ -753,8 +1085,23 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, // ::= @ # structors (they have no declared return type) if (IsStructor) Out << '@'; - else - mangleType(Proto->getResultType()); + else { + QualType Result = Proto->getResultType(); + const Type* RT = Result.getTypePtr(); + if (!RT->isAnyPointerType() && !RT->isReferenceType()) { + if (Result.hasQualifiers() || !RT->isBuiltinType()) + Out << '?'; + if (!RT->isBuiltinType() && !Result.hasQualifiers()) { + // Lack of qualifiers for user types is mangled as 'A'. + Out << 'A'; + } + } + + // FIXME: Get the source range for the result type. Or, better yet, + // implement the unimplemented stuff so we don't need accurate source + // location info anymore :). + mangleType(Result, SourceRange()); + } // <argument-list> ::= X # void // ::= <type>+ @ @@ -763,17 +1110,21 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, Out << 'X'; } else { if (D) { - // If we got a decl, use the "types-as-written" to make sure arrays - // get mangled right. + // If we got a decl, use the type-as-written to make sure arrays + // get mangled right. Note that we can't rely on the TSI + // existing if (for example) the parameter was synthesized. for (FunctionDecl::param_const_iterator Parm = D->param_begin(), - ParmEnd = D->param_end(); - Parm != ParmEnd; ++Parm) - mangleType((*Parm)->getTypeSourceInfo()->getType()); + ParmEnd = D->param_end(); Parm != ParmEnd; ++Parm) { + TypeSourceInfo *TSI = (*Parm)->getTypeSourceInfo(); + QualType Type = TSI ? TSI->getType() : (*Parm)->getType(); + mangleTypeRepeated(Type, (*Parm)->getSourceRange()); + } } else { + // Happens for function pointer type arguments for example. for (FunctionProtoType::arg_type_iterator Arg = Proto->arg_type_begin(), ArgEnd = Proto->arg_type_end(); Arg != ArgEnd; ++Arg) - mangleType(*Arg); + mangleTypeRepeated(*Arg, SourceRange()); } // <builtin-type> ::= Z # ellipsis if (Proto->isVariadic()) @@ -860,8 +1211,16 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(const FunctionType *T, // that they could be in a DLL and somebody from another module could call // them.) CallingConv CC = T->getCallConv(); - if (CC == CC_Default) - CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C; + if (CC == CC_Default) { + if (IsInstMethod) { + const FunctionProtoType *FPT = + T->getCanonicalTypeUnqualified().getAs<FunctionProtoType>(); + bool isVariadic = FPT->isVariadic(); + CC = getASTContext().getDefaultCXXMethodCallConv(isVariadic); + } else { + CC = CC_C; + } + } switch (CC) { default: llvm_unreachable("Unsupported CC for mangling"); @@ -884,8 +1243,15 @@ void MicrosoftCXXNameMangler::mangleThrowSpecification( Out << 'Z'; } -void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) { - llvm_unreachable("Don't know how to mangle UnresolvedUsingTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T, + SourceRange Range) { + // Probably should be mangled as a template instantiation; need to see what + // VC does first. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this unresolved dependent type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } // <type> ::= <union-type> | <struct-type> | <class-type> | <enum-type> @@ -893,10 +1259,10 @@ void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) { // <struct-type> ::= U <name> // <class-type> ::= V <name> // <enum-type> ::= W <size> <name> -void MicrosoftCXXNameMangler::mangleType(const EnumType *T) { +void MicrosoftCXXNameMangler::mangleType(const EnumType *T, SourceRange) { mangleType(static_cast<const TagType*>(T)); } -void MicrosoftCXXNameMangler::mangleType(const RecordType *T) { +void MicrosoftCXXNameMangler::mangleType(const RecordType *T, SourceRange) { mangleType(static_cast<const TagType*>(T)); } void MicrosoftCXXNameMangler::mangleType(const TagType *T) { @@ -936,31 +1302,48 @@ void MicrosoftCXXNameMangler::mangleType(const ArrayType *T, bool IsGlobal) { Out << 'Q'; mangleExtraDimensions(T->getElementType()); } -void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { SmallVector<llvm::APInt, 3> Dimensions; for (;;) { - if (ElementTy->isConstantArrayType()) { - const ConstantArrayType *CAT = - static_cast<const ConstantArrayType *>(ElementTy.getTypePtr()); + if (const ConstantArrayType *CAT = + getASTContext().getAsConstantArrayType(ElementTy)) { Dimensions.push_back(CAT->getSize()); ElementTy = CAT->getElementType(); } else if (ElementTy->isVariableArrayType()) { - llvm_unreachable("Don't know how to mangle VLAs!"); + const VariableArrayType *VAT = + getASTContext().getAsVariableArrayType(ElementTy); + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this variable-length array yet"); + Diags.Report(VAT->getSizeExpr()->getExprLoc(), DiagID) + << VAT->getBracketsRange(); + return; } else if (ElementTy->isDependentSizedArrayType()) { // The dependent expression has to be folded into a constant (TODO). - llvm_unreachable("Don't know how to mangle dependent-sized arrays!"); + const DependentSizedArrayType *DSAT = + getASTContext().getAsDependentSizedArrayType(ElementTy); + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent-length array yet"); + Diags.Report(DSAT->getSizeExpr()->getExprLoc(), DiagID) + << DSAT->getBracketsRange(); + return; } else if (ElementTy->isIncompleteArrayType()) continue; else break; } @@ -974,151 +1357,246 @@ void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { mangleNumber(Dimensions[Dim].getLimitedValue()); } } - mangleType(ElementTy.getLocalUnqualifiedType()); + mangleType(ElementTy.getLocalUnqualifiedType(), SourceRange()); } // <type> ::= <pointer-to-member-type> // <pointer-to-member-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> // <class name> <type> -void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T, + SourceRange Range) { QualType PointeeType = T->getPointeeType(); - if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) { + if (const FunctionProtoType *FPT = PointeeType->getAs<FunctionProtoType>()) { Out << '8'; - mangleName(cast<RecordType>(T->getClass())->getDecl()); + mangleName(T->getClass()->castAs<RecordType>()->getDecl()); mangleType(FPT, NULL, false, true); } else { mangleQualifiers(PointeeType.getQualifiers(), true); - mangleName(cast<RecordType>(T->getClass())->getDecl()); - mangleType(PointeeType.getLocalUnqualifiedType()); + mangleName(T->getClass()->castAs<RecordType>()->getDecl()); + mangleType(PointeeType.getLocalUnqualifiedType(), Range); } } -void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T) { - llvm_unreachable("Don't know how to mangle TemplateTypeParmTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this template type parameter type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftCXXNameMangler::mangleType( - const SubstTemplateTypeParmPackType *T) { - llvm_unreachable( - "Don't know how to mangle SubstTemplateTypeParmPackTypes yet!"); + const SubstTemplateTypeParmPackType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this substituted parameter pack yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } // <type> ::= <pointer-type> // <pointer-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> <type> -void MicrosoftCXXNameMangler::mangleType(const PointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const PointerType *T, + SourceRange Range) { QualType PointeeTy = T->getPointeeType(); if (PointeeTy->isArrayType()) { // Pointers to arrays are mangled like arrays. - mangleExtraDimensions(T->getPointeeType()); - } else if (PointeeTy->isFunctionType()) { + mangleExtraDimensions(PointeeTy); + } else if (const FunctionType *FT = PointeeTy->getAs<FunctionType>()) { // Function pointers are special. Out << '6'; - mangleType(static_cast<const FunctionType *>(PointeeTy.getTypePtr()), - NULL, false, false); + mangleType(FT, NULL, false, false); } else { if (!PointeeTy.hasQualifiers()) // Lack of qualifiers is mangled as 'A'. Out << 'A'; - mangleType(PointeeTy); + mangleType(PointeeTy, Range); } } -void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T, + SourceRange Range) { // Object pointers never have qualifiers. Out << 'A'; - mangleType(T->getPointeeType()); + mangleType(T->getPointeeType(), Range); } // <type> ::= <reference-type> // <reference-type> ::= A <cvr-qualifiers> <type> -void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T) { +void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T, + SourceRange Range) { Out << 'A'; QualType PointeeTy = T->getPointeeType(); if (!PointeeTy.hasQualifiers()) // Lack of qualifiers is mangled as 'A'. Out << 'A'; - mangleType(PointeeTy); -} - -void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T) { - llvm_unreachable("Don't know how to mangle RValueReferenceTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const ComplexType *T) { - llvm_unreachable("Don't know how to mangle ComplexTypes yet!"); + mangleType(PointeeTy, Range); } -void MicrosoftCXXNameMangler::mangleType(const VectorType *T) { - llvm_unreachable("Don't know how to mangle VectorTypes yet!"); -} -void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T) { - llvm_unreachable("Don't know how to mangle ExtVectorTypes yet!"); -} -void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T) { - llvm_unreachable( - "Don't know how to mangle DependentSizedExtVectorTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T) { +// <type> ::= <r-value-reference-type> +// <r-value-reference-type> ::= $$Q <cvr-qualifiers> <type> +void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T, + SourceRange Range) { + Out << "$$Q"; + QualType PointeeTy = T->getPointeeType(); + if (!PointeeTy.hasQualifiers()) + // Lack of qualifiers is mangled as 'A'. + Out << 'A'; + mangleType(PointeeTy, Range); +} + +void MicrosoftCXXNameMangler::mangleType(const ComplexType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this complex number type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const VectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} +void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this extended vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} +void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent-sized extended vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T, + SourceRange) { // ObjC interfaces have structs underlying them. Out << 'U'; mangleName(T->getDecl()); } -void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T) { +void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T, + SourceRange Range) { // We don't allow overloading by different protocol qualification, // so mangling them isn't necessary. - mangleType(T->getBaseType()); + mangleType(T->getBaseType(), Range); } -void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T, + SourceRange Range) { Out << "_E"; - mangleType(T->getPointeeType()); + mangleType(T->getPointeeType(), Range); } -void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T) { - llvm_unreachable("Don't know how to mangle InjectedClassNameTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this injected class name type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } -void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T) { - llvm_unreachable("Don't know how to mangle TemplateSpecializationTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this template specialization type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } -void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T) { - llvm_unreachable("Don't know how to mangle DependentNameTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent name type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftCXXNameMangler::mangleType( - const DependentTemplateSpecializationType *T) { - llvm_unreachable( - "Don't know how to mangle DependentTemplateSpecializationTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T) { - llvm_unreachable("Don't know how to mangle PackExpansionTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T) { - llvm_unreachable("Don't know how to mangle TypeOfTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T) { - llvm_unreachable("Don't know how to mangle TypeOfExprTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T) { - llvm_unreachable("Don't know how to mangle DecltypeTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T) { - llvm_unreachable("Don't know how to mangle UnaryTransformationTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const AutoType *T) { - llvm_unreachable("Don't know how to mangle AutoTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const AtomicType *T) { - llvm_unreachable("Don't know how to mangle AtomicTypes yet!"); + const DependentTemplateSpecializationType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent template specialization type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this pack expansion yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this typeof(type) yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this typeof(expression) yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this decltype() yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this unary transform type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const AutoType *T, SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this 'auto' type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const AtomicType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this C11 atomic type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftMangleContext::mangleName(const NamedDecl *D, @@ -1138,17 +1616,35 @@ void MicrosoftMangleContext::mangleName(const NamedDecl *D, void MicrosoftMangleContext::mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, raw_ostream &) { - llvm_unreachable("Can't yet mangle thunks!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle thunk for this method yet"); + getDiags().Report(MD->getLocation(), DiagID); } void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, const ThisAdjustment &, raw_ostream &) { - llvm_unreachable("Can't yet mangle destructor thunks!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle thunk for this destructor yet"); + getDiags().Report(DD->getLocation(), DiagID); } void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD, - raw_ostream &) { - llvm_unreachable("Can't yet mangle virtual tables!"); + raw_ostream &Out) { + // <mangled-name> ::= ? <operator-name> <class-name> <storage-class> + // <cvr-qualifiers> [<name>] @ + // <operator-name> ::= _7 # vftable + // ::= _8 # vbtable + // NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class> + // is always '6' for vftables and '7' for vbtables. (The difference is + // beyond me.) + // TODO: vbtables. + MicrosoftCXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "\01??_7"; + Mangler.mangleName(RD); + Mangler.getStream() << "6B"; + // TODO: If the class has more than one vtable, mangle in the class it came + // from. + Mangler.getStream() << '@'; } void MicrosoftMangleContext::mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) { @@ -1162,11 +1658,19 @@ void MicrosoftMangleContext::mangleCXXCtorVTable(const CXXRecordDecl *RD, } void MicrosoftMangleContext::mangleCXXRTTI(QualType T, raw_ostream &) { - llvm_unreachable("Can't yet mangle RTTI!"); + // FIXME: Give a location... + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle RTTI descriptors for type %0 yet"); + getDiags().Report(DiagID) + << T.getBaseTypeIdentifier(); } void MicrosoftMangleContext::mangleCXXRTTIName(QualType T, raw_ostream &) { - llvm_unreachable("Can't yet mangle RTTI names!"); + // FIXME: Give a location... + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle the name of type %0 into RTTI descriptors yet"); + getDiags().Report(DiagID) + << T.getBaseTypeIdentifier(); } void MicrosoftMangleContext::mangleCXXCtor(const CXXConstructorDecl *D, CXXCtorType Type, @@ -1180,9 +1684,11 @@ void MicrosoftMangleContext::mangleCXXDtor(const CXXDestructorDecl *D, MicrosoftCXXNameMangler mangler(*this, Out); mangler.mangle(D); } -void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *, +void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *VD, raw_ostream &) { - llvm_unreachable("Can't yet mangle reference temporaries!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this reference temporary yet"); + getDiags().Report(VD->getLocation(), DiagID); } MangleContext *clang::createMicrosoftMangleContext(ASTContext &Context, diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp index f5ea2c54ee2a..39077d1766e8 100644 --- a/lib/AST/NSAPI.cpp +++ b/lib/AST/NSAPI.cpp @@ -9,11 +9,13 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" using namespace clang; NSAPI::NSAPI(ASTContext &ctx) - : Ctx(ctx), ClassIds() { + : Ctx(ctx), ClassIds(), BOOLId(0), NSIntegerId(0), NSUIntegerId(0), + NSASCIIStringEncodingId(0), NSUTF8StringEncodingId(0) { } IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const { @@ -40,6 +42,21 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { case NSStr_stringWithString: Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString")); break; + case NSStr_stringWithUTF8String: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("stringWithUTF8String")); + break; + case NSStr_stringWithCStringEncoding: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("stringWithCString"), + &Ctx.Idents.get("encoding") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + case NSStr_stringWithCString: + Sel= Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithCString")); + break; case NSStr_initWithString: Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString")); break; @@ -50,6 +67,17 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { return NSStringSelectors[MK]; } +llvm::Optional<NSAPI::NSStringMethodKind> +NSAPI::getNSStringMethodKind(Selector Sel) const { + for (unsigned i = 0; i != NumNSStringMethods; ++i) { + NSStringMethodKind MK = NSStringMethodKind(i); + if (Sel == getNSStringSelector(MK)) + return MK; + } + + return llvm::Optional<NSStringMethodKind>(); +} + Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { if (NSArraySelectors[MK].isNull()) { Selector Sel; @@ -251,11 +279,22 @@ NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { } llvm::Optional<NSAPI::NSNumberLiteralMethodKind> -NSAPI::getNSNumberFactoryMethodKind(QualType T) { +NSAPI::getNSNumberFactoryMethodKind(QualType T) const { const BuiltinType *BT = T->getAs<BuiltinType>(); if (!BT) return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); - + + const TypedefType *TDT = T->getAs<TypedefType>(); + if (TDT) { + QualType TDTTy = QualType(TDT, 0); + if (isObjCBOOLType(TDTTy)) + return NSAPI::NSNumberWithBool; + if (isObjCNSIntegerType(TDTTy)) + return NSAPI::NSNumberWithInteger; + if (isObjCNSUIntegerType(TDTTy)) + return NSAPI::NSNumberWithUnsignedInteger; + } + switch (BT->getKind()) { case BuiltinType::Char_S: case BuiltinType::SChar: @@ -310,3 +349,65 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) { return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); } + +/// \brief Returns true if \param T is a typedef of "BOOL" in objective-c. +bool NSAPI::isObjCBOOLType(QualType T) const { + return isObjCTypedef(T, "BOOL", BOOLId); +} +/// \brief Returns true if \param T is a typedef of "NSInteger" in objective-c. +bool NSAPI::isObjCNSIntegerType(QualType T) const { + return isObjCTypedef(T, "NSInteger", NSIntegerId); +} +/// \brief Returns true if \param T is a typedef of "NSUInteger" in objective-c. +bool NSAPI::isObjCNSUIntegerType(QualType T) const { + return isObjCTypedef(T, "NSUInteger", NSUIntegerId); +} + +bool NSAPI::isObjCTypedef(QualType T, + StringRef name, IdentifierInfo *&II) const { + if (!Ctx.getLangOpts().ObjC1) + return false; + if (T.isNull()) + return false; + + if (!II) + II = &Ctx.Idents.get(name); + + while (const TypedefType *TDT = T->getAs<TypedefType>()) { + if (TDT->getDecl()->getDeclName().getAsIdentifierInfo() == II) + return true; + T = TDT->desugar(); + } + + return false; +} + +bool NSAPI::isObjCEnumerator(const Expr *E, + StringRef name, IdentifierInfo *&II) const { + if (!Ctx.getLangOpts().ObjC1) + return false; + if (!E) + return false; + + if (!II) + II = &Ctx.Idents.get(name); + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) + if (const EnumConstantDecl * + EnumD = dyn_cast_or_null<EnumConstantDecl>(DRE->getDecl())) + return EnumD->getIdentifier() == II; + + return false; +} + +Selector NSAPI::getOrInitSelector(ArrayRef<StringRef> Ids, + Selector &Sel) const { + if (Sel.isNull()) { + SmallVector<IdentifierInfo *, 4> Idents; + for (ArrayRef<StringRef>::const_iterator + I = Ids.begin(), E = Ids.end(); I != E; ++I) + Idents.push_back(&Ctx.Idents.get(*I)); + Sel = Ctx.Selectors.getSelector(Idents.size(), Idents.data()); + } + return Sel; +} diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp index 64016d9cdd6d..fa87afd0fa22 100644 --- a/lib/AST/ParentMap.cpp +++ b/lib/AST/ParentMap.cpp @@ -23,13 +23,20 @@ typedef llvm::DenseMap<Stmt*, Stmt*> MapTy; static void BuildParentMap(MapTy& M, Stmt* S) { for (Stmt::child_range I = S->children(); I; ++I) if (*I) { - M[*I] = S; - BuildParentMap(M, *I); + // Prefer the first time we see this statement in the traversal. + // This is important for PseudoObjectExprs. + Stmt *&Parent = M[*I]; + if (!Parent) { + Parent = S; + BuildParentMap(M, *I); + } } // Also include the source expr tree of an OpaqueValueExpr in the map. - if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) { + M[OVE->getSourceExpr()] = S; BuildParentMap(M, OVE->getSourceExpr()); + } } ParentMap::ParentMap(Stmt* S) : Impl(0) { diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp new file mode 100644 index 000000000000..c704cabe69f7 --- /dev/null +++ b/lib/AST/RawCommentList.cpp @@ -0,0 +1,271 @@ +//===--- RawCommentList.cpp - Processing raw comments -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RawCommentList.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentLexer.h" +#include "clang/AST/CommentBriefParser.h" +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentParser.h" +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; + +namespace { +/// Get comment kind and bool describing if it is a trailing comment. +std::pair<RawComment::CommentKind, bool> getCommentKind(StringRef Comment) { + if (Comment.size() < 3 || Comment[0] != '/') + return std::make_pair(RawComment::RCK_Invalid, false); + + RawComment::CommentKind K; + if (Comment[1] == '/') { + if (Comment.size() < 3) + return std::make_pair(RawComment::RCK_OrdinaryBCPL, false); + + if (Comment[2] == '/') + K = RawComment::RCK_BCPLSlash; + else if (Comment[2] == '!') + K = RawComment::RCK_BCPLExcl; + else + return std::make_pair(RawComment::RCK_OrdinaryBCPL, false); + } else { + assert(Comment.size() >= 4); + + // Comment lexer does not understand escapes in comment markers, so pretend + // that this is not a comment. + if (Comment[1] != '*' || + Comment[Comment.size() - 2] != '*' || + Comment[Comment.size() - 1] != '/') + return std::make_pair(RawComment::RCK_Invalid, false); + + if (Comment[2] == '*') + K = RawComment::RCK_JavaDoc; + else if (Comment[2] == '!') + K = RawComment::RCK_Qt; + else + return std::make_pair(RawComment::RCK_OrdinaryC, false); + } + const bool TrailingComment = (Comment.size() > 3) && (Comment[3] == '<'); + return std::make_pair(K, TrailingComment); +} + +bool mergedCommentIsTrailingComment(StringRef Comment) { + return (Comment.size() > 3) && (Comment[3] == '<'); +} +} // unnamed namespace + +RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, + bool Merged) : + Range(SR), RawTextValid(false), BriefTextValid(false), + IsAlmostTrailingComment(false), + BeginLineValid(false), EndLineValid(false) { + // Extract raw comment text, if possible. + if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) { + Kind = RCK_Invalid; + return; + } + + if (!Merged) { + // Guess comment kind. + std::pair<CommentKind, bool> K = getCommentKind(RawText); + Kind = K.first; + IsTrailingComment = K.second; + + IsAlmostTrailingComment = RawText.startswith("//<") || + RawText.startswith("/*<"); + } else { + Kind = RCK_Merged; + IsTrailingComment = mergedCommentIsTrailingComment(RawText); + } +} + +const Decl *RawComment::getDecl() const { + if (DeclOrParsedComment.isNull()) + return NULL; + + if (const Decl *D = DeclOrParsedComment.dyn_cast<const Decl *>()) + return D; + + return DeclOrParsedComment.get<comments::FullComment *>()->getDecl(); +} + +unsigned RawComment::getBeginLine(const SourceManager &SM) const { + if (BeginLineValid) + return BeginLine; + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin()); + BeginLine = SM.getLineNumber(LocInfo.first, LocInfo.second); + BeginLineValid = true; + return BeginLine; +} + +unsigned RawComment::getEndLine(const SourceManager &SM) const { + if (EndLineValid) + return EndLine; + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getEnd()); + EndLine = SM.getLineNumber(LocInfo.first, LocInfo.second); + EndLineValid = true; + return EndLine; +} + +StringRef RawComment::getRawTextSlow(const SourceManager &SourceMgr) const { + FileID BeginFileID; + FileID EndFileID; + unsigned BeginOffset; + unsigned EndOffset; + + llvm::tie(BeginFileID, BeginOffset) = + SourceMgr.getDecomposedLoc(Range.getBegin()); + llvm::tie(EndFileID, EndOffset) = + SourceMgr.getDecomposedLoc(Range.getEnd()); + + const unsigned Length = EndOffset - BeginOffset; + if (Length < 2) + return StringRef(); + + // The comment can't begin in one file and end in another. + assert(BeginFileID == EndFileID); + + bool Invalid = false; + const char *BufferStart = SourceMgr.getBufferData(BeginFileID, + &Invalid).data(); + if (Invalid) + return StringRef(); + + return StringRef(BufferStart + BeginOffset, Length); +} + +const char *RawComment::extractBriefText(const ASTContext &Context) const { + // Make sure that RawText is valid. + getRawText(Context.getSourceManager()); + + // Since we will be copying the resulting text, all allocations made during + // parsing are garbage after resulting string is formed. Thus we can use + // a separate allocator for all temporary stuff. + llvm::BumpPtrAllocator Allocator; + + comments::CommandTraits Traits; + comments::Lexer L(Allocator, Traits, + Range.getBegin(), comments::CommentOptions(), + RawText.begin(), RawText.end()); + comments::BriefParser P(L, Traits); + + const std::string Result = P.Parse(); + const unsigned BriefTextLength = Result.size(); + char *BriefTextPtr = new (Context) char[BriefTextLength + 1]; + memcpy(BriefTextPtr, Result.c_str(), BriefTextLength + 1); + BriefText = BriefTextPtr; + BriefTextValid = true; + + return BriefTextPtr; +} + +comments::FullComment *RawComment::parse(const ASTContext &Context) const { + // Make sure that RawText is valid. + getRawText(Context.getSourceManager()); + + comments::CommandTraits Traits; + comments::Lexer L(Context.getAllocator(), Traits, + getSourceRange().getBegin(), comments::CommentOptions(), + RawText.begin(), RawText.end()); + comments::Sema S(Context.getAllocator(), Context.getSourceManager(), + Context.getDiagnostics(), Traits); + S.setDecl(getDecl()); + comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(), + Context.getDiagnostics(), Traits); + + comments::FullComment *FC = P.parseFullComment(); + DeclOrParsedComment = FC; + return FC; +} + +namespace { +bool containsOnlyWhitespace(StringRef Str) { + return Str.find_first_not_of(" \t\f\v\r\n") == StringRef::npos; +} + +bool onlyWhitespaceBetweenComments(SourceManager &SM, + const RawComment &C1, const RawComment &C2) { + std::pair<FileID, unsigned> C1EndLocInfo = SM.getDecomposedLoc( + C1.getSourceRange().getEnd()); + std::pair<FileID, unsigned> C2BeginLocInfo = SM.getDecomposedLoc( + C2.getSourceRange().getBegin()); + + // Question does not make sense if comments are located in different files. + if (C1EndLocInfo.first != C2BeginLocInfo.first) + return false; + + bool Invalid = false; + const char *Buffer = SM.getBufferData(C1EndLocInfo.first, &Invalid).data(); + if (Invalid) + return false; + + StringRef TextBetweenComments(Buffer + C1EndLocInfo.second, + C2BeginLocInfo.second - C1EndLocInfo.second); + + return containsOnlyWhitespace(TextBetweenComments); +} +} // unnamed namespace + +void RawCommentList::addComment(const RawComment &RC, + llvm::BumpPtrAllocator &Allocator) { + if (RC.isInvalid()) + return; + + // Check if the comments are not in source order. + while (!Comments.empty() && + !SourceMgr.isBeforeInTranslationUnit( + Comments.back()->getSourceRange().getBegin(), + RC.getSourceRange().getBegin())) { + // If they are, just pop a few last comments that don't fit. + // This happens if an \#include directive contains comments. + Comments.pop_back(); + } + + if (OnlyWhitespaceSeen) { + if (!onlyWhitespaceBetweenComments(SourceMgr, LastComment, RC)) + OnlyWhitespaceSeen = false; + } + + LastComment = RC; + + // Ordinary comments are not interesting for us. + if (RC.isOrdinary()) + return; + + // If this is the first Doxygen comment, save it (because there isn't + // anything to merge it with). + if (Comments.empty()) { + Comments.push_back(new (Allocator) RawComment(RC)); + OnlyWhitespaceSeen = true; + return; + } + + const RawComment &C1 = *Comments.back(); + const RawComment &C2 = RC; + + // Merge comments only if there is only whitespace between them. + // Can't merge trailing and non-trailing comments. + // Merge trailing comments if they are on same or consecutive lines. + if (OnlyWhitespaceSeen && + (C1.isTrailingComment() == C2.isTrailingComment()) && + (!C1.isTrailingComment() || + C1.getEndLine(SourceMgr) + 1 >= C2.getBeginLine(SourceMgr))) { + SourceRange MergedRange(C1.getSourceRange().getBegin(), + C2.getSourceRange().getEnd()); + *Comments.back() = RawComment(SourceMgr, MergedRange, true); + } else + Comments.push_back(new (Allocator) RawComment(RC)); + + OnlyWhitespaceSeen = true; +} + diff --git a/lib/AST/RecordLayout.cpp b/lib/AST/RecordLayout.cpp index 0114eba06db7..2ae0aab19f69 100644 --- a/lib/AST/RecordLayout.cpp +++ b/lib/AST/RecordLayout.cpp @@ -32,7 +32,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, CharUnits datasize, const uint64_t *fieldoffsets, unsigned fieldcount) - : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment), + : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0), FieldCount(fieldcount), CXXInfo(0) { if (FieldCount > 0) { FieldOffsets = new (Ctx) uint64_t[FieldCount]; @@ -43,7 +43,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, // Constructor for C++ records. ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, - CharUnits vfptroffset, CharUnits vbptroffset, + bool hasOwnVFPtr, CharUnits vbptroffset, CharUnits datasize, const uint64_t *fieldoffsets, unsigned fieldcount, @@ -53,8 +53,8 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const BaseOffsetsMapTy& BaseOffsets, - const BaseOffsetsMapTy& VBaseOffsets) - : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment), + const VBaseOffsetsMapTy& VBaseOffsets) + : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0), FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo) { if (FieldCount > 0) { @@ -69,7 +69,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject; CXXInfo->BaseOffsets = BaseOffsets; CXXInfo->VBaseOffsets = VBaseOffsets; - CXXInfo->VFPtrOffset = vfptroffset; + CXXInfo->HasOwnVFPtr = hasOwnVFPtr; CXXInfo->VBPtrOffset = vbptroffset; #ifndef NDEBUG @@ -81,7 +81,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, "Primary virtual base must be at offset 0!"); } } else { - assert(getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(getBaseClassOffset(PrimaryBase).isZero() && "Primary base must be at offset 0!"); } } diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index c2d9294a007e..d5df63f7d151 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" @@ -161,10 +162,9 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { // Check the fields. for (CXXRecordDecl::field_iterator I = Class->field_begin(), E = Class->field_end(); I != E; ++I) { - const FieldDecl *FD = *I; const RecordType *RT = - Context.getBaseElementType(FD->getType())->getAs<RecordType>(); + Context.getBaseElementType(I->getType())->getAs<RecordType>(); // We only care about record types. if (!RT) @@ -261,12 +261,11 @@ EmptySubobjectMap::CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -310,12 +309,11 @@ void EmptySubobjectMap::UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -380,13 +378,12 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -491,13 +488,12 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -538,6 +534,8 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD, } } +typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy; + class RecordLayoutBuilder { protected: // FIXME: Remove this and make the appropriate fields public. @@ -600,8 +598,9 @@ protected: /// out is virtual. bool PrimaryBaseIsVirtual; - /// VFPtrOffset - Virtual function table offset. Only for MS layout. - CharUnits VFPtrOffset; + /// HasOwnVFPtr - Whether the class provides its own vtable/vftbl + /// pointer, as opposed to inheriting one from a primary base class. + bool HasOwnVFPtr; /// VBPtrOffset - Virtual base table offset. Only for MS layout. CharUnits VBPtrOffset; @@ -612,7 +611,7 @@ protected: BaseOffsetsMapTy Bases; // VBases - virtual base classes and their offsets in the record. - BaseOffsetsMapTy VBases; + ASTRecordLayout::VBaseOffsetsMapTy VBases; /// IndirectPrimaryBases - Virtual base classes, direct or indirect, that are /// primary base classes for some other direct or indirect base class. @@ -652,7 +651,7 @@ protected: NonVirtualAlignment(CharUnits::One()), ZeroLengthBitfield(0), PrimaryBase(0), PrimaryBaseIsVirtual(false), - VFPtrOffset(CharUnits::fromQuantity(-1)), + HasOwnVFPtr(false), VBPtrOffset(CharUnits::fromQuantity(-1)), FirstNearlyEmptyVBase(0) { } @@ -725,15 +724,20 @@ protected: CharUnits Offset); bool needsVFTable(const CXXRecordDecl *RD) const; - bool hasNewVirtualFunction(const CXXRecordDecl *RD) const; + bool hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor = false) const; bool isPossiblePrimaryBase(const CXXRecordDecl *Base) const; + void computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &VtordispVBases); + /// LayoutVirtualBases - Lays out all the virtual bases. void LayoutVirtualBases(const CXXRecordDecl *RD, const CXXRecordDecl *MostDerivedClass); /// LayoutVirtualBase - Lays out a single virtual base. - void LayoutVirtualBase(const BaseSubobjectInfo *Base); + void LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed = false); /// LayoutBase - Will lay out a base and return the offset where it was /// placed, in chars. @@ -1044,8 +1048,7 @@ RecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD) { CharUnits PtrAlign = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); EnsureVTablePointerAlignment(PtrAlign); - if (isMicrosoftCXXABI()) - VFPtrOffset = getSize(); + HasOwnVFPtr = true; setSize(getSize() + PtrWidth); setDataSize(getSize()); } @@ -1142,7 +1145,7 @@ RecordLayoutBuilder::AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info, assert(!VBases.count(Info->PrimaryVirtualBaseInfo->Class) && "primary vbase offset already exists!"); VBases.insert(std::make_pair(Info->PrimaryVirtualBaseInfo->Class, - Offset)); + ASTRecordLayout::VBaseInfo(Offset, false))); // Traverse the primary virtual base. AddPrimaryVirtualBaseOffsets(Info->PrimaryVirtualBaseInfo, Offset); @@ -1193,19 +1196,177 @@ bool RecordLayoutBuilder::needsVFTable(const CXXRecordDecl *RD) const { return hasNewVirtualFunction(RD); } +/// Does the given class inherit non-virtually from any of the classes +/// in the given set? +static bool hasNonVirtualBaseInSet(const CXXRecordDecl *RD, + const ClassSetTy &set) { + for (CXXRecordDecl::base_class_const_iterator + I = RD->bases_begin(), E = RD->bases_end(); I != E; ++I) { + // Ignore virtual links. + if (I->isVirtual()) continue; + + // Check whether the set contains the base. + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + if (set.count(base)) + return true; + + // Otherwise, recurse and propagate. + if (hasNonVirtualBaseInSet(base, set)) + return true; + } + + return false; +} + +/// Does the given method (B::foo()) already override a method (A::foo()) +/// such that A requires a vtordisp in B? If so, we don't need to add a +/// new vtordisp for B in a yet-more-derived class C providing C::foo(). +static bool overridesMethodRequiringVtorDisp(const ASTContext &Context, + const CXXMethodDecl *M) { + CXXMethodDecl::method_iterator + I = M->begin_overridden_methods(), E = M->end_overridden_methods(); + if (I == E) return false; + + const ASTRecordLayout::VBaseOffsetsMapTy &offsets = + Context.getASTRecordLayout(M->getParent()).getVBaseOffsetsMap(); + do { + const CXXMethodDecl *overridden = *I; + + // If the overridden method's class isn't recognized as a virtual + // base in the derived class, ignore it. + ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + it = offsets.find(overridden->getParent()); + if (it == offsets.end()) continue; + + // Otherwise, check if the overridden method's class needs a vtordisp. + if (it->second.hasVtorDisp()) return true; + + } while (++I != E); + return false; +} + +/// In the Microsoft ABI, decide which of the virtual bases require a +/// vtordisp field. +void RecordLayoutBuilder::computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &vtordispVBases) { + // Bail out if we have no virtual bases. + assert(RD->getNumVBases()); + + // Build up the set of virtual bases that we haven't decided yet. + ClassSetTy undecidedVBases; + for (CXXRecordDecl::base_class_const_iterator + I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { + const CXXRecordDecl *vbase = I->getType()->getAsCXXRecordDecl(); + undecidedVBases.insert(vbase); + } + assert(!undecidedVBases.empty()); + + // A virtual base requires a vtordisp field in a derived class if it + // requires a vtordisp field in a base class. Walk all the direct + // bases and collect this information. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + const ASTRecordLayout &baseLayout = Context.getASTRecordLayout(base); + + // Iterate over the set of virtual bases provided by this class. + for (ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + VI = baseLayout.getVBaseOffsetsMap().begin(), + VE = baseLayout.getVBaseOffsetsMap().end(); VI != VE; ++VI) { + // If it doesn't need a vtordisp in this base, ignore it. + if (!VI->second.hasVtorDisp()) continue; + + // If we've already seen it and decided it needs a vtordisp, ignore it. + if (!undecidedVBases.erase(VI->first)) + continue; + + // Add it. + vtordispVBases.insert(VI->first); + + // Quit as soon as we've decided everything. + if (undecidedVBases.empty()) + return; + } + } + + // Okay, we have virtual bases that we haven't yet decided about. A + // virtual base requires a vtordisp if any the non-destructor + // virtual methods declared in this class directly override a method + // provided by that virtual base. (If so, we need to emit a thunk + // for that method, to be used in the construction vftable, which + // applies an additional 'vtordisp' this-adjustment.) + + // Collect the set of bases directly overridden by any method in this class. + // It's possible that some of these classes won't be virtual bases, or won't be + // provided by virtual bases, or won't be virtual bases in the overridden + // instance but are virtual bases elsewhere. Only the last matters for what + // we're doing, and we can ignore those: if we don't directly override + // a method provided by a virtual copy of a base class, but we do directly + // override a method provided by a non-virtual copy of that base class, + // then we must indirectly override the method provided by the virtual base, + // and so we should already have collected it in the loop above. + ClassSetTy overriddenBases; + for (CXXRecordDecl::method_iterator + M = RD->method_begin(), E = RD->method_end(); M != E; ++M) { + // Ignore non-virtual methods and destructors. + if (isa<CXXDestructorDecl>(*M) || !M->isVirtual()) + continue; + + for (CXXMethodDecl::method_iterator I = M->begin_overridden_methods(), + E = M->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *overriddenMethod = (*I); + + // Ignore methods that override methods from vbases that require + // require vtordisps. + if (overridesMethodRequiringVtorDisp(Context, overriddenMethod)) + continue; + + // As an optimization, check immediately whether we're overriding + // something from the undecided set. + const CXXRecordDecl *overriddenBase = overriddenMethod->getParent(); + if (undecidedVBases.erase(overriddenBase)) { + vtordispVBases.insert(overriddenBase); + if (undecidedVBases.empty()) return; + + // We can't 'continue;' here because one of our undecided + // vbases might non-virtually inherit from this base. + // Consider: + // struct A { virtual void foo(); }; + // struct B : A {}; + // struct C : virtual A, virtual B { virtual void foo(); }; + // We need a vtordisp for B here. + } + + // Otherwise, just collect it. + overriddenBases.insert(overriddenBase); + } + } + + // Walk the undecided v-bases and check whether they (non-virtually) + // provide any of the overridden bases. We don't need to consider + // virtual links because the vtordisp inheres to the layout + // subobject containing the base. + for (ClassSetTy::const_iterator + I = undecidedVBases.begin(), E = undecidedVBases.end(); I != E; ++I) { + if (hasNonVirtualBaseInSet(*I, overriddenBases)) + vtordispVBases.insert(*I); + } +} + /// hasNewVirtualFunction - Does the given polymorphic class declare a /// virtual function that does not override a method from any of its /// base classes? bool -RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { - assert(RD->isPolymorphic()); +RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor) const { if (!RD->getNumBases()) return true; for (CXXRecordDecl::method_iterator method = RD->method_begin(); method != RD->method_end(); ++method) { - if (method->isVirtual() && !method->size_overridden_methods()) { + if (method->isVirtual() && !method->size_overridden_methods() && + !(IgnoreDestructor && method->getKind() == Decl::CXXDestructor)) { return true; } } @@ -1215,11 +1376,11 @@ RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { /// isPossiblePrimaryBase - Is the given base class an acceptable /// primary base class? bool -RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { +RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *base) const { // In the Itanium ABI, a class can be a primary base class if it has // a vtable for any reason. if (!isMicrosoftCXXABI()) - return Base->isDynamicClass(); + return base->isDynamicClass(); // In the MS ABI, a class can only be a primary base class if it // provides a vf-table at a static offset. That means it has to be @@ -1228,14 +1389,22 @@ RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { // base, which we have to guard against. // First off, it has to have virtual functions. - if (!Base->isPolymorphic()) return false; + if (!base->isPolymorphic()) return false; + + // If it has no virtual bases, then the vfptr must be at a static offset. + if (!base->getNumVBases()) return true; + + // Otherwise, the necessary information is cached in the layout. + const ASTRecordLayout &layout = Context.getASTRecordLayout(base); + + // If the base has its own vfptr, it can be a primary base. + if (layout.hasOwnVFPtr()) return true; - // If it has no virtual bases, then everything is at a static offset. - if (!Base->getNumVBases()) return true; + // If the base has a primary base class, then it can be a primary base. + if (layout.getPrimaryBase()) return true; - // Okay, just ask the base class's layout. - return (Context.getASTRecordLayout(Base).getVFPtrOffset() - != CharUnits::fromQuantity(-1)); + // Otherwise it can't. + return false; } void @@ -1288,10 +1457,12 @@ RecordLayoutBuilder::LayoutVirtualBases(const CXXRecordDecl *RD, } void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { - if (!RD->getNumVBases()) return; + ClassSetTy VtordispVBases; + computeVtordisps(RD, VtordispVBases); + // This is substantially simplified because there are no virtual // primary bases. for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), @@ -1299,12 +1470,25 @@ void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl(); const BaseSubobjectInfo *BaseInfo = VirtualBaseInfo.lookup(BaseDecl); assert(BaseInfo && "Did not find virtual base info!"); - - LayoutVirtualBase(BaseInfo); + + // If this base requires a vtordisp, add enough space for an int field. + // This is apparently always 32-bits, even on x64. + bool vtordispNeeded = false; + if (VtordispVBases.count(BaseDecl)) { + CharUnits IntSize = + CharUnits::fromQuantity(Context.getTargetInfo().getIntWidth() / 8); + + setSize(getSize() + IntSize); + setDataSize(getSize()); + vtordispNeeded = true; + } + + LayoutVirtualBase(BaseInfo, vtordispNeeded); } } -void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { +void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed) { assert(!Base->Derived && "Trying to lay out a primary virtual base!"); // Layout the base. @@ -1312,9 +1496,11 @@ void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { // Add its base class offset. assert(!VBases.count(Base->Class) && "vbase offset already exists!"); - VBases.insert(std::make_pair(Base->Class, Offset)); - - AddPrimaryVirtualBaseOffsets(Base, Offset); + VBases.insert(std::make_pair(Base->Class, + ASTRecordLayout::VBaseInfo(Offset, IsVtordispNeed))); + + if (!isMicrosoftCXXABI()) + AddPrimaryVirtualBaseOffsets(Base, Offset); } CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { @@ -1461,8 +1647,8 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { Context.getTargetInfo().getCharAlign())); NonVirtualAlignment = Alignment; - if (isMicrosoftCXXABI() && - NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { + if (isMicrosoftCXXABI()) { + if (NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { CharUnits AlignMember = NonVirtualSize.RoundUpToAlignment(Alignment) - NonVirtualSize; @@ -1472,9 +1658,9 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { NonVirtualSize = Context.toCharUnitsFromBits( llvm::RoundUpToAlignment(getSizeInBits(), Context.getTargetInfo().getCharAlign())); + } MSLayoutVirtualBases(RD); - } else { // Lay out the virtual bases and add the primary virtual base offsets. LayoutVirtualBases(RD, RD); @@ -1540,7 +1726,7 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { for (RecordDecl::field_iterator Field = D->field_begin(), FieldEnd = D->field_end(); Field != FieldEnd; ++Field) { if (IsMsStruct) { - FieldDecl *FD = (*Field); + FieldDecl *FD = *Field; if (Context.ZeroBitfieldFollowsBitfield(FD, LastFD)) ZeroLengthBitfield = FD; // Zero-length bitfields following non-bitfield members are @@ -1635,9 +1821,8 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { } else if (!Context.getTargetInfo().useBitFieldTypeAlignment() && Context.getTargetInfo().useZeroLengthBitfieldAlignment()) { - FieldDecl *FD = (*Field); - if (FD->isBitField() && FD->getBitWidthValue(Context) == 0) - ZeroLengthBitfield = FD; + if (Field->isBitField() && Field->getBitWidthValue(Context) == 0) + ZeroLengthBitfield = *Field; } LayoutField(*Field); } @@ -2166,6 +2351,10 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { if (MD->hasInlineBody()) continue; + // Ignore inline deleted or defaulted functions. + if (!MD->isUserProvided()) + continue; + // We found it. return MD; } @@ -2238,7 +2427,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { NewEntry = new (*this) ASTRecordLayout(*this, Builder.getSize(), Builder.Alignment, - Builder.VFPtrOffset, + Builder.HasOwnVFPtr, Builder.VBPtrOffset, DataSize, Builder.FieldOffsets.data(), @@ -2375,7 +2564,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, IndentLevel++; const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); - bool HasVfptr = Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1); + bool HasVfptr = Layout.hasOwnVFPtr(); bool HasVbptr = Layout.getVBPtrOffset() != CharUnits::fromQuantity(-1); // Vtable pointer. @@ -2405,7 +2594,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, // vfptr and vbptr (for Microsoft C++ ABI) if (HasVfptr) { - PrintOffset(OS, Offset + Layout.getVFPtrOffset(), IndentLevel); + PrintOffset(OS, Offset, IndentLevel); OS << '(' << *RD << " vftable pointer)\n"; } if (HasVbptr) { @@ -2417,27 +2606,29 @@ static void DumpCXXRecordLayout(raw_ostream &OS, uint64_t FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *Field = *I; + const FieldDecl &Field = **I; CharUnits FieldOffset = Offset + C.toCharUnitsFromBits(Layout.getFieldOffset(FieldNo)); - if (const RecordType *RT = Field->getType()->getAs<RecordType>()) { + if (const RecordType *RT = Field.getType()->getAs<RecordType>()) { if (const CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(RT->getDecl())) { DumpCXXRecordLayout(OS, D, C, FieldOffset, IndentLevel, - Field->getName().data(), + Field.getName().data(), /*IncludeVirtualBases=*/true); continue; } } PrintOffset(OS, FieldOffset, IndentLevel); - OS << Field->getType().getAsString() << ' ' << *Field << '\n'; + OS << Field.getType().getAsString() << ' ' << Field << '\n'; } if (!IncludeVirtualBases) return; // Dump virtual bases. + const ASTRecordLayout::VBaseOffsetsMapTy &vtordisps = + Layout.getVBaseOffsetsMap(); for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { assert(I->isVirtual() && "Found non-virtual class!"); @@ -2445,6 +2636,12 @@ static void DumpCXXRecordLayout(raw_ostream &OS, cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBase); + + if (vtordisps.find(VBase)->second.hasVtorDisp()) { + PrintOffset(OS, VBaseOffset - CharUnits::fromQuantity(4), IndentLevel); + OS << "(vtordisp for vbase " << *VBase << ")\n"; + } + DumpCXXRecordLayout(OS, VBase, C, VBaseOffset, IndentLevel, VBase == PrimaryBase ? "(primary virtual base)" : "(virtual base)", diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index e4d9f0a1ef42..d877c3fab762 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -244,6 +244,22 @@ SourceLocation Stmt::getLocEnd() const { llvm_unreachable("unknown statement kind"); } +CompoundStmt::CompoundStmt(ASTContext &C, Stmt **StmtStart, unsigned NumStmts, + SourceLocation LB, SourceLocation RB) + : Stmt(CompoundStmtClass), LBracLoc(LB), RBracLoc(RB) { + CompoundStmtBits.NumStmts = NumStmts; + assert(CompoundStmtBits.NumStmts == NumStmts && + "NumStmts doesn't fit in bits of CompoundStmtBits.NumStmts!"); + + if (NumStmts == 0) { + Body = 0; + return; + } + + Body = new (C) Stmt*[NumStmts]; + memcpy(Body, StmtStart, NumStmts * sizeof(*Body)); +} + void CompoundStmt::setStmts(ASTContext &C, Stmt **Stmts, unsigned NumStmts) { if (this->Body) C.Deallocate(Body); @@ -257,6 +273,23 @@ const char *LabelStmt::getName() const { return getDecl()->getIdentifier()->getNameStart(); } +AttributedStmt *AttributedStmt::Create(ASTContext &C, SourceLocation Loc, + ArrayRef<const Attr*> Attrs, + Stmt *SubStmt) { + void *Mem = C.Allocate(sizeof(AttributedStmt) + + sizeof(Attr*) * (Attrs.size() - 1), + llvm::alignOf<AttributedStmt>()); + return new (Mem) AttributedStmt(Loc, Attrs, SubStmt); +} + +AttributedStmt *AttributedStmt::CreateEmpty(ASTContext &C, unsigned NumAttrs) { + assert(NumAttrs > 0 && "NumAttrs should be greater than zero"); + void *Mem = C.Allocate(sizeof(AttributedStmt) + + sizeof(Attr*) * (NumAttrs - 1), + llvm::alignOf<AttributedStmt>()); + return new (Mem) AttributedStmt(EmptyShell(), NumAttrs); +} + // This is defined here to avoid polluting Stmt.h with importing Expr.h SourceRange ReturnStmt::getSourceRange() const { if (RetExpr) @@ -328,7 +361,7 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C, StringLiteral **Constraints, Stmt **Exprs, unsigned NumOutputs, - unsigned NumInputs, + unsigned NumInputs, StringLiteral **Clobbers, unsigned NumClobbers) { this->NumOutputs = NumOutputs; @@ -336,19 +369,19 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C, this->NumClobbers = NumClobbers; unsigned NumExprs = NumOutputs + NumInputs; - + C.Deallocate(this->Names); this->Names = new (C) IdentifierInfo*[NumExprs]; std::copy(Names, Names + NumExprs, this->Names); - + C.Deallocate(this->Exprs); this->Exprs = new (C) Stmt*[NumExprs]; std::copy(Exprs, Exprs + NumExprs, this->Exprs); - + C.Deallocate(this->Constraints); this->Constraints = new (C) StringLiteral*[NumExprs]; std::copy(Constraints, Constraints + NumExprs, this->Constraints); - + C.Deallocate(this->Clobbers); this->Clobbers = new (C) StringLiteral*[NumClobbers]; std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); @@ -407,7 +440,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, std::string CurStringPiece; bool HasVariants = !C.getTargetInfo().hasNoAsmVariants(); - + while (1) { // Done with the string? if (CurPtr == StrEnd) { @@ -428,7 +461,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, CurStringPiece += CurChar; continue; } - + // Escaped "%" character in asm string. if (CurPtr == StrEnd) { // % at end of string is invalid (no escape). @@ -525,8 +558,8 @@ QualType CXXCatchStmt::getCaughtType() const { // Constructors //===----------------------------------------------------------------------===// -AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, - bool isvolatile, bool msasm, +AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, + bool isvolatile, bool msasm, unsigned numoutputs, unsigned numinputs, IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, StringLiteral *asmstr, unsigned numclobbers, @@ -535,8 +568,8 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, , IsSimple(issimple), IsVolatile(isvolatile), MSAsm(msasm) , NumOutputs(numoutputs), NumInputs(numinputs), NumClobbers(numclobbers) { - unsigned NumExprs = NumOutputs +NumInputs; - + unsigned NumExprs = NumOutputs + NumInputs; + Names = new (C) IdentifierInfo*[NumExprs]; std::copy(names, names + NumExprs, Names); @@ -550,6 +583,33 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, std::copy(clobbers, clobbers + NumClobbers, Clobbers); } +MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc, + bool issimple, bool isvolatile, ArrayRef<Token> asmtoks, + ArrayRef<unsigned> lineends, StringRef asmstr, + ArrayRef<StringRef> clobbers, SourceLocation endloc) + : Stmt(MSAsmStmtClass), AsmLoc(asmloc), EndLoc(endloc), + AsmStr(asmstr.str()), IsSimple(issimple), IsVolatile(isvolatile), + NumAsmToks(asmtoks.size()), NumLineEnds(lineends.size()), + NumClobbers(clobbers.size()) { + + AsmToks = new (C) Token[NumAsmToks]; + for (unsigned i = 0, e = NumAsmToks; i != e; ++i) + AsmToks[i] = asmtoks[i]; + + LineEnds = new (C) unsigned[NumLineEnds]; + for (unsigned i = 0, e = NumLineEnds; i != e; ++i) + LineEnds[i] = lineends[i]; + + Clobbers = new (C) StringRef[NumClobbers]; + for (unsigned i = 0, e = NumClobbers; i != e; ++i) { + // FIXME: Avoid the allocation/copy if at all possible. + size_t size = clobbers[i].size(); + char *dest = new (C) char[size]; + std::strncpy(dest, clobbers[i].data(), size); + Clobbers[i] = StringRef(dest, size); + } +} + ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect, Stmt *Body, SourceLocation FCL, SourceLocation RPL) @@ -571,31 +631,31 @@ ObjCAtTryStmt::ObjCAtTryStmt(SourceLocation atTryLoc, Stmt *atTryStmt, Stmts[0] = atTryStmt; for (unsigned I = 0; I != NumCatchStmts; ++I) Stmts[I + 1] = CatchStmts[I]; - + if (HasFinally) Stmts[NumCatchStmts + 1] = atFinallyStmt; } -ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context, - SourceLocation atTryLoc, +ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context, + SourceLocation atTryLoc, Stmt *atTryStmt, - Stmt **CatchStmts, + Stmt **CatchStmts, unsigned NumCatchStmts, Stmt *atFinallyStmt) { - unsigned Size = sizeof(ObjCAtTryStmt) + + unsigned Size = sizeof(ObjCAtTryStmt) + (1 + NumCatchStmts + (atFinallyStmt != 0)) * sizeof(Stmt *); void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>()); return new (Mem) ObjCAtTryStmt(atTryLoc, atTryStmt, CatchStmts, NumCatchStmts, atFinallyStmt); } -ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context, +ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context, unsigned NumCatchStmts, bool HasFinally) { - unsigned Size = sizeof(ObjCAtTryStmt) + + unsigned Size = sizeof(ObjCAtTryStmt) + (1 + NumCatchStmts + HasFinally) * sizeof(Stmt *); void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>()); - return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally); + return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally); } SourceRange ObjCAtTryStmt::getSourceRange() const { @@ -606,12 +666,12 @@ SourceRange ObjCAtTryStmt::getSourceRange() const { EndLoc = getCatchStmt(NumCatchStmts - 1)->getLocEnd(); else EndLoc = getTryBody()->getLocEnd(); - + return SourceRange(AtTryLoc, EndLoc); } CXXTryStmt *CXXTryStmt::Create(ASTContext &C, SourceLocation tryLoc, - Stmt *tryBlock, Stmt **handlers, + Stmt *tryBlock, Stmt **handlers, unsigned numHandlers) { std::size_t Size = sizeof(CXXTryStmt); Size += ((numHandlers + 1) * sizeof(Stmt)); @@ -671,20 +731,20 @@ const VarDecl *CXXForRangeStmt::getLoopVariable() const { return const_cast<CXXForRangeStmt*>(this)->getLoopVariable(); } -IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, +IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL, Stmt *elsev) : Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) { setConditionVariable(C, var); SubExprs[COND] = reinterpret_cast<Stmt*>(cond); SubExprs[THEN] = then; - SubExprs[ELSE] = elsev; + SubExprs[ELSE] = elsev; } VarDecl *IfStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -694,16 +754,16 @@ void IfStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[VAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } -ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, - Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, +ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, + Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP) - : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP) + : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP) { SubExprs[INIT] = Init; setConditionVariable(C, condVar); @@ -715,7 +775,7 @@ ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, VarDecl *ForStmt::getConditionVariable() const { if (!SubExprs[CONDVAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[CONDVAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -725,14 +785,14 @@ void ForStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[CONDVAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[CONDVAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } -SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) - : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0) +SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) + : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0) { setConditionVariable(C, Var); SubExprs[COND] = reinterpret_cast<Stmt*>(cond); @@ -742,7 +802,7 @@ SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) VarDecl *SwitchStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -752,7 +812,7 @@ void SwitchStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[VAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); @@ -764,7 +824,7 @@ Stmt *SwitchCase::getSubStmt() { return cast<DefaultStmt>(this)->getSubStmt(); } -WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, +WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, SourceLocation WL) : Stmt(WhileStmtClass) { setConditionVariable(C, Var); @@ -776,7 +836,7 @@ WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, VarDecl *WhileStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } diff --git a/lib/AST/StmtDumper.cpp b/lib/AST/StmtDumper.cpp index b5e298c0c94c..962e35269c18 100644 --- a/lib/AST/StmtDumper.cpp +++ b/lib/AST/StmtDumper.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtVisitor.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/PrettyPrinter.h" @@ -166,6 +167,7 @@ namespace { void VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node); void VisitObjCEncodeExpr(ObjCEncodeExpr *Node); void VisitObjCMessageExpr(ObjCMessageExpr* Node); + void VisitObjCBoxedExpr(ObjCBoxedExpr* Node); void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); void VisitObjCProtocolExpr(ObjCProtocolExpr *Node); void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node); @@ -423,6 +425,7 @@ void StmtDumper::VisitPredefinedExpr(PredefinedExpr *Node) { default: llvm_unreachable("unknown case"); case PredefinedExpr::Func: OS << " __func__"; break; case PredefinedExpr::Function: OS << " __FUNCTION__"; break; + case PredefinedExpr::LFunction: OS << " L__FUNCTION__"; break; case PredefinedExpr::PrettyFunction: OS << " __PRETTY_FUNCTION__";break; } } @@ -445,18 +448,8 @@ void StmtDumper::VisitFloatingLiteral(FloatingLiteral *Node) { void StmtDumper::VisitStringLiteral(StringLiteral *Str) { DumpExpr(Str); - // FIXME: this doesn't print wstrings right. OS << " "; - switch (Str->getKind()) { - case StringLiteral::Ascii: break; // No prefix - case StringLiteral::Wide: OS << 'L'; break; - case StringLiteral::UTF8: OS << "u8"; break; - case StringLiteral::UTF16: OS << 'u'; break; - case StringLiteral::UTF32: OS << 'U'; break; - } - OS << '"'; - OS.write_escaped(Str->getString()); - OS << '"'; + Str->outputString(OS); } void StmtDumper::VisitUnaryOperator(UnaryOperator *Node) { @@ -471,7 +464,7 @@ void StmtDumper::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { OS << " sizeof "; break; case UETT_AlignOf: - OS << " __alignof "; + OS << " alignof "; break; case UETT_VecStep: OS << " vec_step "; @@ -637,6 +630,11 @@ void StmtDumper::VisitObjCMessageExpr(ObjCMessageExpr* Node) { } } +void StmtDumper::VisitObjCBoxedExpr(ObjCBoxedExpr* Node) { + DumpExpr(Node); + OS << " selector=" << Node->getBoxingMethod()->getSelector().getAsString(); +} + void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) { DumpStmt(Node); if (VarDecl *CatchParam = Node->getCatchParamDecl()) { diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 0d1066b7e322..2f7cb55c7cc1 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -172,9 +173,9 @@ void StmtPrinter::VisitLabelStmt(LabelStmt *Node) { void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) { OS << "[["; bool first = true; - for (AttrVec::const_iterator it = Node->getAttrs().begin(), - end = Node->getAttrs().end(); - it != end; ++it) { + for (ArrayRef<const Attr*>::iterator it = Node->getAttrs().begin(), + end = Node->getAttrs().end(); + it != end; ++it) { if (!first) { OS << ", "; first = false; @@ -429,6 +430,11 @@ void StmtPrinter::VisitAsmStmt(AsmStmt *Node) { OS << ");\n"; } +void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) { + // FIXME: Implement MS style inline asm statement printer. + Indent() << "asm ()"; +} + void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { Indent() << "@try"; if (CompoundStmt *TS = dyn_cast<CompoundStmt>(Node->getTryBody())) { @@ -638,6 +644,9 @@ void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { case PredefinedExpr::Function: OS << "__FUNCTION__"; break; + case PredefinedExpr::LFunction: + OS << "L__FUNCTION__"; + break; case PredefinedExpr::PrettyFunction: OS << "__PRETTY_FUNCTION__"; break; @@ -734,93 +743,7 @@ void StmtPrinter::VisitImaginaryLiteral(ImaginaryLiteral *Node) { } void StmtPrinter::VisitStringLiteral(StringLiteral *Str) { - switch (Str->getKind()) { - case StringLiteral::Ascii: break; // no prefix. - case StringLiteral::Wide: OS << 'L'; break; - case StringLiteral::UTF8: OS << "u8"; break; - case StringLiteral::UTF16: OS << 'u'; break; - case StringLiteral::UTF32: OS << 'U'; break; - } - OS << '"'; - static char Hex[] = "0123456789ABCDEF"; - - unsigned LastSlashX = Str->getLength(); - for (unsigned I = 0, N = Str->getLength(); I != N; ++I) { - switch (uint32_t Char = Str->getCodeUnit(I)) { - default: - // FIXME: Convert UTF-8 back to codepoints before rendering. - - // Convert UTF-16 surrogate pairs back to codepoints before rendering. - // Leave invalid surrogates alone; we'll use \x for those. - if (Str->getKind() == StringLiteral::UTF16 && I != N - 1 && - Char >= 0xd800 && Char <= 0xdbff) { - uint32_t Trail = Str->getCodeUnit(I + 1); - if (Trail >= 0xdc00 && Trail <= 0xdfff) { - Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00); - ++I; - } - } - - if (Char > 0xff) { - // If this is a wide string, output characters over 0xff using \x - // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a - // codepoint: use \x escapes for invalid codepoints. - if (Str->getKind() == StringLiteral::Wide || - (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) { - // FIXME: Is this the best way to print wchar_t? - OS << "\\x"; - int Shift = 28; - while ((Char >> Shift) == 0) - Shift -= 4; - for (/**/; Shift >= 0; Shift -= 4) - OS << Hex[(Char >> Shift) & 15]; - LastSlashX = I; - break; - } - - if (Char > 0xffff) - OS << "\\U00" - << Hex[(Char >> 20) & 15] - << Hex[(Char >> 16) & 15]; - else - OS << "\\u"; - OS << Hex[(Char >> 12) & 15] - << Hex[(Char >> 8) & 15] - << Hex[(Char >> 4) & 15] - << Hex[(Char >> 0) & 15]; - break; - } - - // If we used \x... for the previous character, and this character is a - // hexadecimal digit, prevent it being slurped as part of the \x. - if (LastSlashX + 1 == I) { - switch (Char) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - OS << "\"\""; - } - } - - if (Char <= 0xff && isprint(Char)) - OS << (char)Char; - else // Output anything hard as an octal escape. - OS << '\\' - << (char)('0' + ((Char >> 6) & 7)) - << (char)('0' + ((Char >> 3) & 7)) - << (char)('0' + ((Char >> 0) & 7)); - break; - // Handle some common non-printable cases to make dumps prettier. - case '\\': OS << "\\\\"; break; - case '"': OS << "\\\""; break; - case '\n': OS << "\\n"; break; - case '\t': OS << "\\t"; break; - case '\a': OS << "\\a"; break; - case '\b': OS << "\\b"; break; - } - } - OS << '"'; + Str->outputString(OS); } void StmtPrinter::VisitParenExpr(ParenExpr *Node) { OS << "("; @@ -892,7 +815,12 @@ void StmtPrinter::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node){ OS << "sizeof"; break; case UETT_AlignOf: - OS << "__alignof"; + if (Policy.LangOpts.CPlusPlus) + OS << "alignof"; + else if (Policy.LangOpts.C11) + OS << "_Alignof"; + else + OS << "__alignof"; break; case UETT_VecStep: OS << "vec_step"; @@ -1275,7 +1203,7 @@ void StmtPrinter::VisitUserDefinedLiteral(UserDefinedLiteral *Node) { const TemplateArgument &Pack = Args->get(0); for (TemplateArgument::pack_iterator I = Pack.pack_begin(), E = Pack.pack_end(); I != E; ++I) { - char C = (char)I->getAsIntegral()->getZExtValue(); + char C = (char)I->getAsIntegral().getZExtValue(); OS << C; } break; @@ -1727,9 +1655,9 @@ void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { VisitStringLiteral(Node->getString()); } -void StmtPrinter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void StmtPrinter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { OS << "@"; - Visit(E->getNumber()); + Visit(E->getSubExpr()); } void StmtPrinter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index e50523ae860b..2168b64d5b23 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -178,6 +178,11 @@ void StmtProfiler::VisitAsmStmt(const AsmStmt *S) { VisitStringLiteral(S->getClobber(I)); } +void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) { + // FIXME: Implement MS style inline asm statement profiler. + VisitStmt(S); +} + void StmtProfiler::VisitCXXCatchStmt(const CXXCatchStmt *S) { VisitStmt(S); VisitType(S->getCaughtType()); @@ -981,7 +986,7 @@ void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } -void StmtProfiler::VisitObjCNumericLiteral(const ObjCNumericLiteral *E) { +void StmtProfiler::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { VisitExpr(E); } @@ -1161,7 +1166,7 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { break; case TemplateArgument::Integral: - Arg.getAsIntegral()->Profile(ID); + Arg.getAsIntegral().Profile(ID); VisitType(Arg.getIntegralType()); break; diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index 531e03e302b2..f8dd396d92df 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -36,17 +36,17 @@ using namespace clang; static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out) { const ::clang::Type *T = TemplArg.getIntegralType().getTypePtr(); - const llvm::APSInt *Val = TemplArg.getAsIntegral(); + const llvm::APSInt &Val = TemplArg.getAsIntegral(); if (T->isBooleanType()) { - Out << (Val->getBoolValue() ? "true" : "false"); + Out << (Val.getBoolValue() ? "true" : "false"); } else if (T->isCharType()) { - const char Ch = Val->getZExtValue(); + const char Ch = Val.getZExtValue(); Out << ((Ch == '\'') ? "'\\" : "'"); Out.write_escaped(StringRef(&Ch, 1), /*UseHexEscapes=*/ true); Out << "'"; } else { - Out << Val->toString(10); + Out << Val; } } @@ -54,6 +54,25 @@ static void printIntegral(const TemplateArgument &TemplArg, // TemplateArgument Implementation //===----------------------------------------------------------------------===// +TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, + QualType Type) + : Kind(Integral) { + // Copy the APSInt value into our decomposed form. + Integer.BitWidth = Value.getBitWidth(); + Integer.IsUnsigned = Value.isUnsigned(); + // If the value is large, we have to get additional memory from the ASTContext + unsigned NumWords = Value.getNumWords(); + if (NumWords > 1) { + void *Mem = Ctx.Allocate(NumWords * sizeof(uint64_t)); + std::memcpy(Mem, Value.getRawData(), NumWords * sizeof(uint64_t)); + Integer.pVal = static_cast<uint64_t *>(Mem); + } else { + Integer.VAL = Value.getZExtValue(); + } + + Integer.Type = Type.getAsOpaquePtr(); +} + TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context, const TemplateArgument *Args, unsigned NumArgs) { @@ -246,7 +265,7 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, } case Integral: - getAsIntegral()->Profile(ID); + getAsIntegral().Profile(ID); getIntegralType().Profile(ID); break; @@ -275,7 +294,7 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { case Integral: return getIntegralType() == Other.getIntegralType() && - *getAsIntegral() == *Other.getAsIntegral(); + getAsIntegral() == Other.getAsIntegral(); case Pack: if (Args.NumArgs != Other.Args.NumArgs) return false; @@ -498,7 +517,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, return DB << "nullptr"; case TemplateArgument::Integral: - return DB << Arg.getAsIntegral()->toString(10); + return DB << Arg.getAsIntegral().toString(10); case TemplateArgument::Template: return DB << Arg.getAsTemplate(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 3f6a09457d1e..abefae44b2ba 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -288,6 +288,28 @@ QualType QualType::IgnoreParens(QualType T) { return T; } +/// \brief This will check for a TypedefType by removing any existing sugar +/// until it reaches a TypedefType or a non-sugared type. +template <> const TypedefType *Type::getAs() const { + const Type *Cur = this; + + while (true) { + if (const TypedefType *TDT = dyn_cast<TypedefType>(Cur)) + return TDT; + switch (Cur->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Parent) +#define TYPE(Class, Parent) \ + case Class: { \ + const Class##Type *Ty = cast<Class##Type>(Cur); \ + if (!Ty->isSugared()) return 0; \ + Cur = Ty->desugar().getTypePtr(); \ + break; \ + } +#include "clang/AST/TypeNodes.def" + } + } +} + /// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic /// sugar off the given type. This should produce an object of the /// same dynamic type as the canonical type. @@ -895,6 +917,14 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } bool QualType::isPODType(ASTContext &Context) const { + // C++11 has a more relaxed definition of POD. + if (Context.getLangOpts().CPlusPlus0x) + return isCXX11PODType(Context); + + return isCXX98PODType(Context); +} + +bool QualType::isCXX98PODType(ASTContext &Context) const { // The compiler shouldn't query this for incomplete types, but the user might. // We return false for that case. Except for incomplete arrays of PODs, which // are PODs according to the standard. @@ -902,7 +932,7 @@ bool QualType::isPODType(ASTContext &Context) const { return 0; if ((*this)->isIncompleteArrayType()) - return Context.getBaseElementType(*this).isPODType(Context); + return Context.getBaseElementType(*this).isCXX98PODType(Context); if ((*this)->isIncompleteType()) return false; @@ -929,7 +959,7 @@ bool QualType::isPODType(ASTContext &Context) const { case Type::VariableArray: case Type::ConstantArray: // IncompleteArray is handled above. - return Context.getBaseElementType(*this).isPODType(Context); + return Context.getBaseElementType(*this).isCXX98PODType(Context); case Type::ObjCObjectPointer: case Type::BlockPointer: @@ -1417,7 +1447,7 @@ const char *Type::getTypeClassName() const { llvm_unreachable("Invalid type class."); } -const char *BuiltinType::getName(const PrintingPolicy &Policy) const { +StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { switch (getKind()) { case Void: return "void"; case Bool: return Policy.Bool ? "bool" : "_Bool"; @@ -1554,6 +1584,11 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, slot[1] = epi.ExceptionSpecTemplate; // This exception specification doesn't make the type dependent, because // it's not instantiated as part of instantiating the type. + } else if (getExceptionSpecType() == EST_Unevaluated) { + // Store the function decl from which we will resolve our + // exception specification. + FunctionDecl **slot = reinterpret_cast<FunctionDecl**>(argSlot + numArgs); + slot[0] = epi.ExceptionSpecDecl; } if (epi.ConsumedArguments) { @@ -1637,7 +1672,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr()); } else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){ epi.NoexceptExpr->Profile(ID, Context, false); - } else if (epi.ExceptionSpecType == EST_Uninstantiated) { + } else if (epi.ExceptionSpecType == EST_Uninstantiated || + epi.ExceptionSpecType == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpecDecl->getCanonicalDecl()); } if (epi.ConsumedArguments) { @@ -1832,8 +1868,7 @@ TemplateSpecializationType(TemplateName T, Canon.isNull()? T.isDependent() : Canon->isInstantiationDependentType(), false, - Canon.isNull()? T.containsUnexpandedParameterPack() - : Canon->containsUnexpandedParameterPack()), + T.containsUnexpandedParameterPack()), Template(T), NumArgs(NumArgs), TypeAlias(!AliasedType.isNull()) { assert(!T.getAsDependentTemplateName() && "Use DependentTemplateSpecializationType for dependent template-name"); @@ -1858,6 +1893,8 @@ TemplateSpecializationType(TemplateName T, // arguments is. Given: // template<typename T> using U = int; // U<T> is always non-dependent, irrespective of the type T. + // However, U<Ts> contains an unexpanded parameter pack, even though + // its expansion (and thus its desugared type) doesn't. if (Canon.isNull() && Args[Arg].isDependent()) setDependent(); else if (Args[Arg].isInstantiationDependent()) @@ -1866,7 +1903,7 @@ TemplateSpecializationType(TemplateName T, if (Args[Arg].getKind() == TemplateArgument::Type && Args[Arg].getAsType()->isVariablyModifiedType()) setVariablyModified(); - if (Canon.isNull() && Args[Arg].containsUnexpandedParameterPack()) + if (Args[Arg].containsUnexpandedParameterPack()) setContainsUnexpandedParameterPack(); new (&TemplateArgs[Arg]) TemplateArgument(Args[Arg]); diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp index caa19b19df54..c7bb7da4d17b 100644 --- a/lib/AST/TypeLoc.cpp +++ b/lib/AST/TypeLoc.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/raw_ostream.h" #include "clang/AST/TypeLocVisitor.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 3bf80e797244..c42117c82bf5 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -19,8 +20,10 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; namespace { @@ -40,62 +43,124 @@ namespace { Policy.SuppressStrongLifetime = Old; } }; + + class ParamPolicyRAII { + PrintingPolicy &Policy; + bool Old; + + public: + explicit ParamPolicyRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressSpecifiers) { + Policy.SuppressSpecifiers = false; + } + + ~ParamPolicyRAII() { + Policy.SuppressSpecifiers = Old; + } + }; + + class ElaboratedTypePolicyRAII { + PrintingPolicy &Policy; + bool SuppressTagKeyword; + bool SuppressScope; + + public: + explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { + SuppressTagKeyword = Policy.SuppressTagKeyword; + SuppressScope = Policy.SuppressScope; + Policy.SuppressTagKeyword = true; + Policy.SuppressScope = true; + } + + ~ElaboratedTypePolicyRAII() { + Policy.SuppressTagKeyword = SuppressTagKeyword; + Policy.SuppressScope = SuppressScope; + } + }; class TypePrinter { PrintingPolicy Policy; + bool HasEmptyPlaceHolder; public: - explicit TypePrinter(const PrintingPolicy &Policy) : Policy(Policy) { } - - void print(const Type *ty, Qualifiers qs, std::string &buffer); - void print(QualType T, std::string &S); - void AppendScope(DeclContext *DC, std::string &S); - void printTag(TagDecl *T, std::string &S); + explicit TypePrinter(const PrintingPolicy &Policy) + : Policy(Policy), HasEmptyPlaceHolder(false) { } + + void print(const Type *ty, Qualifiers qs, raw_ostream &OS, + StringRef PlaceHolder); + void print(QualType T, raw_ostream &OS, StringRef PlaceHolder); + + static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier); + void spaceBeforePlaceHolder(raw_ostream &OS); + void printTypeSpec(const NamedDecl *D, raw_ostream &OS); + + void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); + void printBefore(QualType T, raw_ostream &OS); + void printAfter(const Type *ty, Qualifiers qs, raw_ostream &OS); + void printAfter(QualType T, raw_ostream &OS); + void AppendScope(DeclContext *DC, raw_ostream &OS); + void printTag(TagDecl *T, raw_ostream &OS); #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) \ - void print##CLASS(const CLASS##Type *T, std::string &S); + void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \ + void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS); #include "clang/AST/TypeNodes.def" }; } -static void AppendTypeQualList(std::string &S, unsigned TypeQuals) { +static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals) { + bool appendSpace = false; if (TypeQuals & Qualifiers::Const) { - if (!S.empty()) S += ' '; - S += "const"; + OS << "const"; + appendSpace = true; } if (TypeQuals & Qualifiers::Volatile) { - if (!S.empty()) S += ' '; - S += "volatile"; + if (appendSpace) OS << ' '; + OS << "volatile"; + appendSpace = true; } if (TypeQuals & Qualifiers::Restrict) { - if (!S.empty()) S += ' '; - S += "restrict"; + if (appendSpace) OS << ' '; + OS << "restrict"; } } -void TypePrinter::print(QualType t, std::string &buffer) { +void TypePrinter::spaceBeforePlaceHolder(raw_ostream &OS) { + if (!HasEmptyPlaceHolder) + OS << ' '; +} + +void TypePrinter::print(QualType t, raw_ostream &OS, StringRef PlaceHolder) { SplitQualType split = t.split(); - print(split.Ty, split.Quals, buffer); + print(split.Ty, split.Quals, OS, PlaceHolder); } -void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { +void TypePrinter::print(const Type *T, Qualifiers Quals, raw_ostream &OS, + StringRef PlaceHolder) { if (!T) { - buffer += "NULL TYPE"; + OS << "NULL TYPE"; return; } if (Policy.SuppressSpecifiers && T->isSpecifierType()) return; - - // Print qualifiers as appropriate. - + + SaveAndRestore<bool> PHVal(HasEmptyPlaceHolder, PlaceHolder.empty()); + + printBefore(T, Quals, OS); + OS << PlaceHolder; + printAfter(T, Quals, OS); +} + +bool TypePrinter::canPrefixQualifiers(const Type *T, + bool &NeedARCStrongQualifier) { // CanPrefixQualifiers - We prefer to print type qualifiers before the type, // so that we get "const int" instead of "int const", but we can't do this if // the type is complex. For example if the type is "int*", we *must* print // "int * const", printing "const int *" is different. Only do this when the // type expands to a simple string. bool CanPrefixQualifiers = false; - bool NeedARCStrongQualifier = false; + NeedARCStrongQualifier = false; Type::TypeClass TC = T->getTypeClass(); if (const AutoType *AT = dyn_cast<AutoType>(T)) TC = AT->desugar()->getTypeClass(); @@ -157,493 +222,616 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { CanPrefixQualifiers = false; break; } - - if (!CanPrefixQualifiers && !Quals.empty()) { - std::string qualsBuffer; + + return CanPrefixQualifiers; +} + +void TypePrinter::printBefore(QualType T, raw_ostream &OS) { + SplitQualType Split = T.split(); + + // If we have cv1 T, where T is substituted for cv2 U, only print cv1 - cv2 + // at this level. + Qualifiers Quals = Split.Quals; + if (const SubstTemplateTypeParmType *Subst = + dyn_cast<SubstTemplateTypeParmType>(Split.Ty)) + Quals -= QualType(Subst, 0).getQualifiers(); + + printBefore(Split.Ty, Quals, OS); +} + +/// \brief Prints the part of the type string before an identifier, e.g. for +/// "int foo[10]" it prints "int ". +void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) { + if (Policy.SuppressSpecifiers && T->isSpecifierType()) + return; + + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder); + + // Print qualifiers as appropriate. + + bool CanPrefixQualifiers = false; + bool NeedARCStrongQualifier = false; + CanPrefixQualifiers = canPrefixQualifiers(T, NeedARCStrongQualifier); + + if (CanPrefixQualifiers && !Quals.empty()) { if (NeedARCStrongQualifier) { IncludeStrongLifetimeRAII Strong(Policy); - Quals.getAsStringInternal(qualsBuffer, Policy); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true); } else { - Quals.getAsStringInternal(qualsBuffer, Policy); - } - - if (!qualsBuffer.empty()) { - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; - } - std::swap(buffer, qualsBuffer); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true); } } - + + bool hasAfterQuals = false; + if (!CanPrefixQualifiers && !Quals.empty()) { + hasAfterQuals = !Quals.isEmptyWhenPrinted(Policy); + if (hasAfterQuals) + HasEmptyPlaceHolder = false; + } + switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) case Type::CLASS: \ - print##CLASS(cast<CLASS##Type>(T), buffer); \ + print##CLASS##Before(cast<CLASS##Type>(T), OS); \ break; #include "clang/AST/TypeNodes.def" } - - // If we're adding the qualifiers as a prefix, do it now. - if (CanPrefixQualifiers && !Quals.empty()) { - std::string qualsBuffer; + + if (hasAfterQuals) { if (NeedARCStrongQualifier) { IncludeStrongLifetimeRAII Strong(Policy); - Quals.getAsStringInternal(qualsBuffer, Policy); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get()); } else { - Quals.getAsStringInternal(qualsBuffer, Policy); - } - - if (!qualsBuffer.empty()) { - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; - } - std::swap(buffer, qualsBuffer); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get()); } } } -void TypePrinter::printBuiltin(const BuiltinType *T, std::string &S) { - if (S.empty()) { - S = T->getName(Policy); - } else { - // Prefix the basic type, e.g. 'int X'. - S = ' ' + S; - S = T->getName(Policy) + S; +void TypePrinter::printAfter(QualType t, raw_ostream &OS) { + SplitQualType split = t.split(); + printAfter(split.Ty, split.Quals, OS); +} + +/// \brief Prints the part of the type string after an identifier, e.g. for +/// "int foo[10]" it prints "[10]". +void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) { + switch (T->getTypeClass()) { +#define ABSTRACT_TYPE(CLASS, PARENT) +#define TYPE(CLASS, PARENT) case Type::CLASS: \ + print##CLASS##After(cast<CLASS##Type>(T), OS); \ + break; +#include "clang/AST/TypeNodes.def" } } -void TypePrinter::printComplex(const ComplexType *T, std::string &S) { - print(T->getElementType(), S); - S = "_Complex " + S; +void TypePrinter::printBuiltinBefore(const BuiltinType *T, raw_ostream &OS) { + OS << T->getName(Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printBuiltinAfter(const BuiltinType *T, raw_ostream &OS) { } -void TypePrinter::printPointer(const PointerType *T, std::string &S) { - S = '*' + S; - +void TypePrinter::printComplexBefore(const ComplexType *T, raw_ostream &OS) { + OS << "_Complex "; + printBefore(T->getElementType(), OS); +} +void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); +} + +void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) - S = '(' + S + ')'; - + OS << '('; + OS << '*'; +} +void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (*A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeType())) + OS << ')'; + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printBlockPointer(const BlockPointerType *T, std::string &S) { - S = '^' + S; - print(T->getPointeeType(), S); +void TypePrinter::printBlockPointerBefore(const BlockPointerType *T, + raw_ostream &OS) { + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); + OS << '^'; +} +void TypePrinter::printBlockPointerAfter(const BlockPointerType *T, + raw_ostream &OS) { + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printLValueReference(const LValueReferenceType *T, - std::string &S) { - S = '&' + S; - +void TypePrinter::printLValueReferenceBefore(const LValueReferenceType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeTypeAsWritten(), OS); // Handle things like 'int (&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeTypeAsWritten())) - S = '(' + S + ')'; - + OS << '('; + OS << '&'; +} +void TypePrinter::printLValueReferenceAfter(const LValueReferenceType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeTypeAsWritten(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (&A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeTypeAsWritten())) + OS << ')'; + printAfter(T->getPointeeTypeAsWritten(), OS); } -void TypePrinter::printRValueReference(const RValueReferenceType *T, - std::string &S) { - S = "&&" + S; - +void TypePrinter::printRValueReferenceBefore(const RValueReferenceType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeTypeAsWritten(), OS); // Handle things like 'int (&&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeTypeAsWritten())) - S = '(' + S + ')'; - + OS << '('; + OS << "&&"; +} +void TypePrinter::printRValueReferenceAfter(const RValueReferenceType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeTypeAsWritten(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (&&A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeTypeAsWritten())) + OS << ')'; + printAfter(T->getPointeeTypeAsWritten(), OS); } -void TypePrinter::printMemberPointer(const MemberPointerType *T, - std::string &S) { - PrintingPolicy InnerPolicy(Policy); - Policy.SuppressTag = true; - std::string C = QualType(T->getClass(), 0).getAsString(InnerPolicy); - C += "::*"; - S = C + S; - +void TypePrinter::printMemberPointerBefore(const MemberPointerType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); // Handle things like 'int (Cls::*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) - S = '(' + S + ')'; - + OS << '('; + + PrintingPolicy InnerPolicy(Policy); + InnerPolicy.SuppressTag = false; + TypePrinter(InnerPolicy).print(QualType(T->getClass(), 0), OS, StringRef()); + + OS << "::*"; +} +void TypePrinter::printMemberPointerAfter(const MemberPointerType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (Cls::*A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeType())) + OS << ')'; + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printConstantArray(const ConstantArrayType *T, - std::string &S) { - S += '['; - S += llvm::utostr(T->getSize().getZExtValue()); - S += ']'; - +void TypePrinter::printConstantArrayBefore(const ConstantArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printConstantArrayAfter(const ConstantArrayType *T, + raw_ostream &OS) { + OS << '[' << T->getSize().getZExtValue() << ']'; + printAfter(T->getElementType(), OS); } -void TypePrinter::printIncompleteArray(const IncompleteArrayType *T, - std::string &S) { - S += "[]"; +void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T, + raw_ostream &OS) { + OS << "[]"; + printAfter(T->getElementType(), OS); } -void TypePrinter::printVariableArray(const VariableArrayType *T, - std::string &S) { - S += '['; - +void TypePrinter::printVariableArrayBefore(const VariableArrayType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printVariableArrayAfter(const VariableArrayType *T, + raw_ostream &OS) { + OS << '['; if (T->getIndexTypeQualifiers().hasQualifiers()) { - AppendTypeQualList(S, T->getIndexTypeCVRQualifiers()); - S += ' '; + AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers()); + OS << ' '; } - + if (T->getSizeModifier() == VariableArrayType::Static) - S += "static"; + OS << "static"; else if (T->getSizeModifier() == VariableArrayType::Star) - S += '*'; - - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ']'; - - IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + OS << '*'; + + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ']'; + + printAfter(T->getElementType(), OS); } -void TypePrinter::printDependentSizedArray(const DependentSizedArrayType *T, - std::string &S) { - S += '['; - - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ']'; - +void TypePrinter::printDependentSizedArrayBefore( + const DependentSizedArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printDependentSizedArrayAfter( + const DependentSizedArrayType *T, + raw_ostream &OS) { + OS << '['; + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ']'; + printAfter(T->getElementType(), OS); } -void TypePrinter::printDependentSizedExtVector( +void TypePrinter::printDependentSizedExtVectorBefore( const DependentSizedExtVectorType *T, - std::string &S) { - print(T->getElementType(), S); - - S += " __attribute__((ext_vector_type("; - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ")))"; + raw_ostream &OS) { + printBefore(T->getElementType(), OS); +} +void TypePrinter::printDependentSizedExtVectorAfter( + const DependentSizedExtVectorType *T, + raw_ostream &OS) { + OS << " __attribute__((ext_vector_type("; + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ")))"; + printAfter(T->getElementType(), OS); } -void TypePrinter::printVector(const VectorType *T, std::string &S) { +void TypePrinter::printVectorBefore(const VectorType *T, raw_ostream &OS) { switch (T->getVectorKind()) { case VectorType::AltiVecPixel: - S = "__vector __pixel " + S; + OS << "__vector __pixel "; break; case VectorType::AltiVecBool: - print(T->getElementType(), S); - S = "__vector __bool " + S; + OS << "__vector __bool "; + printBefore(T->getElementType(), OS); break; case VectorType::AltiVecVector: - print(T->getElementType(), S); - S = "__vector " + S; + OS << "__vector "; + printBefore(T->getElementType(), OS); break; case VectorType::NeonVector: - print(T->getElementType(), S); - S = ("__attribute__((neon_vector_type(" + - llvm::utostr_32(T->getNumElements()) + "))) " + S); + OS << "__attribute__((neon_vector_type(" + << T->getNumElements() << "))) "; + printBefore(T->getElementType(), OS); break; case VectorType::NeonPolyVector: - print(T->getElementType(), S); - S = ("__attribute__((neon_polyvector_type(" + - llvm::utostr_32(T->getNumElements()) + "))) " + S); + OS << "__attribute__((neon_polyvector_type(" << + T->getNumElements() << "))) "; + printBefore(T->getElementType(), OS); break; case VectorType::GenericVector: { // FIXME: We prefer to print the size directly here, but have no way // to get the size of the type. - print(T->getElementType(), S); - std::string V = "__attribute__((__vector_size__("; - V += llvm::utostr_32(T->getNumElements()); // convert back to bytes. - std::string ET; - print(T->getElementType(), ET); - V += " * sizeof(" + ET + ")))) "; - S = V + S; + OS << "__attribute__((__vector_size__(" + << T->getNumElements() + << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + OS << ")))) "; + printBefore(T->getElementType(), OS); break; } } } +void TypePrinter::printVectorAfter(const VectorType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); +} -void TypePrinter::printExtVector(const ExtVectorType *T, std::string &S) { - S += " __attribute__((ext_vector_type("; - S += llvm::utostr_32(T->getNumElements()); - S += ")))"; - print(T->getElementType(), S); +void TypePrinter::printExtVectorBefore(const ExtVectorType *T, + raw_ostream &OS) { + printBefore(T->getElementType(), OS); +} +void TypePrinter::printExtVectorAfter(const ExtVectorType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); + OS << " __attribute__((ext_vector_type("; + OS << T->getNumElements(); + OS << ")))"; } void -FunctionProtoType::printExceptionSpecification(std::string &S, +FunctionProtoType::printExceptionSpecification(raw_ostream &OS, PrintingPolicy Policy) const { if (hasDynamicExceptionSpec()) { - S += " throw("; + OS << " throw("; if (getExceptionSpecType() == EST_MSAny) - S += "..."; + OS << "..."; else for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) { if (I) - S += ", "; + OS << ", "; - S += getExceptionType(I).getAsString(Policy); + OS << getExceptionType(I).stream(Policy); } - S += ")"; + OS << ')'; } else if (isNoexceptExceptionSpec(getExceptionSpecType())) { - S += " noexcept"; + OS << " noexcept"; if (getExceptionSpecType() == EST_ComputedNoexcept) { - S += "("; - llvm::raw_string_ostream EOut(S); - getNoexceptExpr()->printPretty(EOut, 0, Policy); - EOut.flush(); - S += EOut.str(); - S += ")"; + OS << '('; + getNoexceptExpr()->printPretty(OS, 0, Policy); + OS << ')'; } } } -void TypePrinter::printFunctionProto(const FunctionProtoType *T, - std::string &S) { +void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T, + raw_ostream &OS) { + if (T->hasTrailingReturn()) { + OS << "auto "; + if (!HasEmptyPlaceHolder) + OS << '('; + } else { + // If needed for precedence reasons, wrap the inner part in grouping parens. + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + printBefore(T->getResultType(), OS); + if (!PrevPHIsEmpty.get()) + OS << '('; + } +} + +void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, + raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. - if (!S.empty()) - S = "(" + S + ")"; - - S += "("; - std::string Tmp; - PrintingPolicy ParamPolicy(Policy); - ParamPolicy.SuppressSpecifiers = false; - for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) { - if (i) S += ", "; - print(T->getArgType(i), Tmp); - S += Tmp; - Tmp.clear(); + if (!HasEmptyPlaceHolder) + OS << ')'; + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + + OS << '('; + { + ParamPolicyRAII ParamPolicy(Policy); + for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) { + if (i) OS << ", "; + print(T->getArgType(i), OS, StringRef()); + } } if (T->isVariadic()) { if (T->getNumArgs()) - S += ", "; - S += "..."; + OS << ", "; + OS << "..."; } else if (T->getNumArgs() == 0 && !Policy.LangOpts.CPlusPlus) { // Do not emit int() if we have a proto, emit 'int(void)'. - S += "void"; + OS << "void"; } - S += ")"; + OS << ')'; FunctionType::ExtInfo Info = T->getExtInfo(); switch(Info.getCC()) { case CC_Default: break; case CC_C: - S += " __attribute__((cdecl))"; + OS << " __attribute__((cdecl))"; break; case CC_X86StdCall: - S += " __attribute__((stdcall))"; + OS << " __attribute__((stdcall))"; break; case CC_X86FastCall: - S += " __attribute__((fastcall))"; + OS << " __attribute__((fastcall))"; break; case CC_X86ThisCall: - S += " __attribute__((thiscall))"; + OS << " __attribute__((thiscall))"; break; case CC_X86Pascal: - S += " __attribute__((pascal))"; + OS << " __attribute__((pascal))"; break; case CC_AAPCS: - S += " __attribute__((pcs(\"aapcs\")))"; + OS << " __attribute__((pcs(\"aapcs\")))"; break; case CC_AAPCS_VFP: - S += " __attribute__((pcs(\"aapcs-vfp\")))"; + OS << " __attribute__((pcs(\"aapcs-vfp\")))"; break; } if (Info.getNoReturn()) - S += " __attribute__((noreturn))"; + OS << " __attribute__((noreturn))"; if (Info.getRegParm()) - S += " __attribute__((regparm (" + - llvm::utostr_32(Info.getRegParm()) + ")))"; - - AppendTypeQualList(S, T->getTypeQuals()); + OS << " __attribute__((regparm (" + << Info.getRegParm() << ")))"; + + if (unsigned quals = T->getTypeQuals()) { + OS << ' '; + AppendTypeQualList(OS, quals); + } switch (T->getRefQualifier()) { case RQ_None: break; case RQ_LValue: - S += " &"; + OS << " &"; break; case RQ_RValue: - S += " &&"; + OS << " &&"; break; } - T->printExceptionSpecification(S, Policy); + T->printExceptionSpecification(OS, Policy); + if (T->hasTrailingReturn()) { - std::string ResultS; - print(T->getResultType(), ResultS); - S = "auto " + S + " -> " + ResultS; + OS << " -> "; + print(T->getResultType(), OS, StringRef()); } else - print(T->getResultType(), S); + printAfter(T->getResultType(), OS); } -void TypePrinter::printFunctionNoProto(const FunctionNoProtoType *T, - std::string &S) { +void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, + raw_ostream &OS) { + // If needed for precedence reasons, wrap the inner part in grouping parens. + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + printBefore(T->getResultType(), OS); + if (!PrevPHIsEmpty.get()) + OS << '('; +} +void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T, + raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. - if (!S.empty()) - S = "(" + S + ")"; + if (!HasEmptyPlaceHolder) + OS << ')'; + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); - S += "()"; + OS << "()"; if (T->getNoReturnAttr()) - S += " __attribute__((noreturn))"; - print(T->getResultType(), S); + OS << " __attribute__((noreturn))"; + printAfter(T->getResultType(), OS); } -static void printTypeSpec(const NamedDecl *D, std::string &S) { +void TypePrinter::printTypeSpec(const NamedDecl *D, raw_ostream &OS) { IdentifierInfo *II = D->getIdentifier(); - if (S.empty()) - S = II->getName().str(); - else - S = II->getName().str() + ' ' + S; + OS << II->getName(); + spaceBeforePlaceHolder(OS); } -void TypePrinter::printUnresolvedUsing(const UnresolvedUsingType *T, - std::string &S) { - printTypeSpec(T->getDecl(), S); +void TypePrinter::printUnresolvedUsingBefore(const UnresolvedUsingType *T, + raw_ostream &OS) { + printTypeSpec(T->getDecl(), OS); } +void TypePrinter::printUnresolvedUsingAfter(const UnresolvedUsingType *T, + raw_ostream &OS) { } -void TypePrinter::printTypedef(const TypedefType *T, std::string &S) { - printTypeSpec(T->getDecl(), S); +void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { + printTypeSpec(T->getDecl(), OS); } +void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) { } -void TypePrinter::printTypeOfExpr(const TypeOfExprType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typeof(e) X'. - S = ' ' + S; - std::string Str; - llvm::raw_string_ostream s(Str); - T->getUnderlyingExpr()->printPretty(s, 0, Policy); - S = "typeof " + s.str() + S; +void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T, + raw_ostream &OS) { + OS << "typeof "; + T->getUnderlyingExpr()->printPretty(OS, 0, Policy); + spaceBeforePlaceHolder(OS); } - -void TypePrinter::printTypeOf(const TypeOfType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typeof(t) X'. - S = ' ' + S; - std::string Tmp; - print(T->getUnderlyingType(), Tmp); - S = "typeof(" + Tmp + ")" + S; +void TypePrinter::printTypeOfExprAfter(const TypeOfExprType *T, + raw_ostream &OS) { } + +void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) { + OS << "typeof("; + print(T->getUnderlyingType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTypeOfAfter(const TypeOfType *T, raw_ostream &OS) { } -void TypePrinter::printDecltype(const DecltypeType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'decltype(t) X'. - S = ' ' + S; - std::string Str; - llvm::raw_string_ostream s(Str); - T->getUnderlyingExpr()->printPretty(s, 0, Policy); - S = "decltype(" + s.str() + ")" + S; +void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) { + OS << "decltype("; + T->getUnderlyingExpr()->printPretty(OS, 0, Policy); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDecltypeAfter(const DecltypeType *T, raw_ostream &OS) { } -void TypePrinter::printUnaryTransform(const UnaryTransformType *T, - std::string &S) { - if (!S.empty()) - S = ' ' + S; - std::string Str; +void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getBaseType(), Str); switch (T->getUTTKind()) { case UnaryTransformType::EnumUnderlyingType: - S = "__underlying_type(" + Str + ")" + S; - break; + OS << "__underlying_type("; + print(T->getBaseType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); + return; } + + printBefore(T->getBaseType(), OS); +} +void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + + switch (T->getUTTKind()) { + case UnaryTransformType::EnumUnderlyingType: + return; + } + + printAfter(T->getBaseType(), OS); } -void TypePrinter::printAuto(const AutoType *T, std::string &S) { +void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { // If the type has been deduced, do not print 'auto'. if (T->isDeduced()) { - print(T->getDeducedType(), S); + printBefore(T->getDeducedType(), OS); } else { - if (!S.empty()) // Prefix the basic type, e.g. 'auto X'. - S = ' ' + S; - S = "auto" + S; + OS << "auto"; + spaceBeforePlaceHolder(OS); } } +void TypePrinter::printAutoAfter(const AutoType *T, raw_ostream &OS) { + // If the type has been deduced, do not print 'auto'. + if (T->isDeduced()) + printAfter(T->getDeducedType(), OS); +} -void TypePrinter::printAtomic(const AtomicType *T, std::string &S) { - if (!S.empty()) - S = ' ' + S; - std::string Str; +void TypePrinter::printAtomicBefore(const AtomicType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getValueType(), Str); - S = "_Atomic(" + Str + ")" + S; + OS << "_Atomic("; + print(T->getValueType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printAtomicAfter(const AtomicType *T, raw_ostream &OS) { } /// Appends the given scope to the end of a string. -void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) { +void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) { if (DC->isTranslationUnit()) return; - AppendScope(DC->getParent(), Buffer); - - unsigned OldSize = Buffer.size(); + AppendScope(DC->getParent(), OS); if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) { if (Policy.SuppressUnwrittenScope && (NS->isAnonymousNamespace() || NS->isInline())) return; if (NS->getIdentifier()) - Buffer += NS->getNameAsString(); + OS << NS->getName() << "::"; else - Buffer += "<anonymous>"; + OS << "<anonymous>::"; } else if (ClassTemplateSpecializationDecl *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { IncludeStrongLifetimeRAII Strong(Policy); + OS << Spec->getIdentifier()->getName(); const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); - std::string TemplateArgsStr - = TemplateSpecializationType::PrintTemplateArgumentList( + TemplateSpecializationType::PrintTemplateArgumentList(OS, TemplateArgs.data(), TemplateArgs.size(), Policy); - Buffer += Spec->getIdentifier()->getName(); - Buffer += TemplateArgsStr; + OS << "::"; } else if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) { if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl()) - Buffer += Typedef->getIdentifier()->getName(); + OS << Typedef->getIdentifier()->getName() << "::"; else if (Tag->getIdentifier()) - Buffer += Tag->getIdentifier()->getName(); + OS << Tag->getIdentifier()->getName() << "::"; else return; } - - if (Buffer.size() != OldSize) - Buffer += "::"; } -void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { +void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { if (Policy.SuppressTag) return; - std::string Buffer; bool HasKindDecoration = false; // bool SuppressTagKeyword @@ -654,25 +842,24 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { if (!(Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword || D->getTypedefNameForAnonDecl())) { HasKindDecoration = true; - Buffer += D->getKindName(); - Buffer += ' '; + OS << D->getKindName(); + OS << ' '; } // Compute the full nested-name-specifier for this type. // In C, this will always be empty except when the type // being printed is anonymous within other Record. if (!Policy.SuppressScope) - AppendScope(D->getDeclContext(), Buffer); + AppendScope(D->getDeclContext(), OS); if (const IdentifierInfo *II = D->getIdentifier()) - Buffer += II->getNameStart(); + OS << II->getName(); else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); - Buffer += Typedef->getIdentifier()->getNameStart(); + OS << Typedef->getIdentifier()->getName(); } else { // Make an unambiguous representation for anonymous types, e.g. // <anonymous enum at /usr/include/string.h:120:9> - llvm::raw_string_ostream OS(Buffer); if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) { OS << "<lambda"; @@ -717,219 +904,223 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { NumArgs = TemplateArgs.size(); } IncludeStrongLifetimeRAII Strong(Policy); - Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args, - NumArgs, - Policy); - } - - if (!InnerString.empty()) { - Buffer += ' '; - Buffer += InnerString; + TemplateSpecializationType::PrintTemplateArgumentList(OS, + Args, NumArgs, + Policy); } - std::swap(Buffer, InnerString); + spaceBeforePlaceHolder(OS); } -void TypePrinter::printRecord(const RecordType *T, std::string &S) { - printTag(T->getDecl(), S); +void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) { + printTag(T->getDecl(), OS); } +void TypePrinter::printRecordAfter(const RecordType *T, raw_ostream &OS) { } -void TypePrinter::printEnum(const EnumType *T, std::string &S) { - printTag(T->getDecl(), S); +void TypePrinter::printEnumBefore(const EnumType *T, raw_ostream &OS) { + printTag(T->getDecl(), OS); } +void TypePrinter::printEnumAfter(const EnumType *T, raw_ostream &OS) { } -void TypePrinter::printTemplateTypeParm(const TemplateTypeParmType *T, - std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'parmname X'. - S = ' ' + S; - +void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T, + raw_ostream &OS) { if (IdentifierInfo *Id = T->getIdentifier()) - S = Id->getName().str() + S; + OS << Id->getName(); else - S = "type-parameter-" + llvm::utostr_32(T->getDepth()) + '-' + - llvm::utostr_32(T->getIndex()) + S; + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTemplateTypeParmAfter(const TemplateTypeParmType *T, + raw_ostream &OS) { } -void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T, - std::string &S) { +void TypePrinter::printSubstTemplateTypeParmBefore( + const SubstTemplateTypeParmType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getReplacementType(), S); + printBefore(T->getReplacementType(), OS); +} +void TypePrinter::printSubstTemplateTypeParmAfter( + const SubstTemplateTypeParmType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + printAfter(T->getReplacementType(), OS); } -void TypePrinter::printSubstTemplateTypeParmPack( +void TypePrinter::printSubstTemplateTypeParmPackBefore( const SubstTemplateTypeParmPackType *T, - std::string &S) { + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - printTemplateTypeParm(T->getReplacedParameter(), S); + printTemplateTypeParmBefore(T->getReplacedParameter(), OS); +} +void TypePrinter::printSubstTemplateTypeParmPackAfter( + const SubstTemplateTypeParmPackType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + printTemplateTypeParmAfter(T->getReplacedParameter(), OS); } -void TypePrinter::printTemplateSpecialization( +void TypePrinter::printTemplateSpecializationBefore( const TemplateSpecializationType *T, - std::string &S) { + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - std::string SpecString; + T->getTemplateName().print(OS, Policy); - { - llvm::raw_string_ostream OS(SpecString); - T->getTemplateName().print(OS, Policy); - } - - SpecString += TemplateSpecializationType::PrintTemplateArgumentList( - T->getArgs(), - T->getNumArgs(), - Policy); - if (S.empty()) - S.swap(SpecString); - else - S = SpecString + ' ' + S; + TemplateSpecializationType::PrintTemplateArgumentList(OS, + T->getArgs(), + T->getNumArgs(), + Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTemplateSpecializationAfter( + const TemplateSpecializationType *T, + raw_ostream &OS) { } -void TypePrinter::printInjectedClassName(const InjectedClassNameType *T, - std::string &S) { - printTemplateSpecialization(T->getInjectedTST(), S); +void TypePrinter::printInjectedClassNameBefore(const InjectedClassNameType *T, + raw_ostream &OS) { + printTemplateSpecializationBefore(T->getInjectedTST(), OS); } - -void TypePrinter::printElaborated(const ElaboratedType *T, std::string &S) { - std::string MyString; - - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - NestedNameSpecifier* Qualifier = T->getQualifier(); - if (Qualifier) - Qualifier->print(OS, Policy); - } - - std::string TypeStr; - PrintingPolicy InnerPolicy(Policy); - InnerPolicy.SuppressTagKeyword = true; - InnerPolicy.SuppressScope = true; - TypePrinter(InnerPolicy).print(T->getNamedType(), TypeStr); +void TypePrinter::printInjectedClassNameAfter(const InjectedClassNameType *T, + raw_ostream &OS) { } + +void TypePrinter::printElaboratedBefore(const ElaboratedType *T, + raw_ostream &OS) { + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; + NestedNameSpecifier* Qualifier = T->getQualifier(); + if (Qualifier) + Qualifier->print(OS, Policy); - MyString += TypeStr; - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + ElaboratedTypePolicyRAII PolicyRAII(Policy); + printBefore(T->getNamedType(), OS); +} +void TypePrinter::printElaboratedAfter(const ElaboratedType *T, + raw_ostream &OS) { + ElaboratedTypePolicyRAII PolicyRAII(Policy); + printAfter(T->getNamedType(), OS); } -void TypePrinter::printParen(const ParenType *T, std::string &S) { - if (!S.empty() && !isa<FunctionType>(T->getInnerType())) - S = '(' + S + ')'; - print(T->getInnerType(), S); +void TypePrinter::printParenBefore(const ParenType *T, raw_ostream &OS) { + if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) { + printBefore(T->getInnerType(), OS); + OS << '('; + } else + printBefore(T->getInnerType(), OS); +} +void TypePrinter::printParenAfter(const ParenType *T, raw_ostream &OS) { + if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) { + OS << ')'; + printAfter(T->getInnerType(), OS); + } else + printAfter(T->getInnerType(), OS); } -void TypePrinter::printDependentName(const DependentNameType *T, std::string &S) { - std::string MyString; +void TypePrinter::printDependentNameBefore(const DependentNameType *T, + raw_ostream &OS) { + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - - T->getQualifier()->print(OS, Policy); - - OS << T->getIdentifier()->getName(); - } + T->getQualifier()->print(OS, Policy); - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + OS << T->getIdentifier()->getName(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDependentNameAfter(const DependentNameType *T, + raw_ostream &OS) { } -void TypePrinter::printDependentTemplateSpecialization( - const DependentTemplateSpecializationType *T, std::string &S) { +void TypePrinter::printDependentTemplateSpecializationBefore( + const DependentTemplateSpecializationType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - std::string MyString; - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - - if (T->getQualifier()) - T->getQualifier()->print(OS, Policy); - OS << T->getIdentifier()->getName(); - OS << TemplateSpecializationType::PrintTemplateArgumentList( - T->getArgs(), - T->getNumArgs(), - Policy); - } + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + if (T->getQualifier()) + T->getQualifier()->print(OS, Policy); + OS << T->getIdentifier()->getName(); + TemplateSpecializationType::PrintTemplateArgumentList(OS, + T->getArgs(), + T->getNumArgs(), + Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDependentTemplateSpecializationAfter( + const DependentTemplateSpecializationType *T, raw_ostream &OS) { } -void TypePrinter::printPackExpansion(const PackExpansionType *T, - std::string &S) { - print(T->getPattern(), S); - S += "..."; +void TypePrinter::printPackExpansionBefore(const PackExpansionType *T, + raw_ostream &OS) { + printBefore(T->getPattern(), OS); +} +void TypePrinter::printPackExpansionAfter(const PackExpansionType *T, + raw_ostream &OS) { + printAfter(T->getPattern(), OS); + OS << "..."; } -void TypePrinter::printAttributed(const AttributedType *T, - std::string &S) { +void TypePrinter::printAttributedBefore(const AttributedType *T, + raw_ostream &OS) { // Prefer the macro forms of the GC and ownership qualifiers. if (T->getAttrKind() == AttributedType::attr_objc_gc || T->getAttrKind() == AttributedType::attr_objc_ownership) - return print(T->getEquivalentType(), S); + return printBefore(T->getEquivalentType(), OS); + + printBefore(T->getModifiedType(), OS); +} - print(T->getModifiedType(), S); +void TypePrinter::printAttributedAfter(const AttributedType *T, + raw_ostream &OS) { + // Prefer the macro forms of the GC and ownership qualifiers. + if (T->getAttrKind() == AttributedType::attr_objc_gc || + T->getAttrKind() == AttributedType::attr_objc_ownership) + return printAfter(T->getEquivalentType(), OS); // TODO: not all attributes are GCC-style attributes. - S += " __attribute__(("; + OS << " __attribute__(("; switch (T->getAttrKind()) { case AttributedType::attr_address_space: - S += "address_space("; - S += T->getEquivalentType().getAddressSpace(); - S += ")"; + OS << "address_space("; + OS << T->getEquivalentType().getAddressSpace(); + OS << ')'; break; case AttributedType::attr_vector_size: { - S += "__vector_size__("; + OS << "__vector_size__("; if (const VectorType *vector =T->getEquivalentType()->getAs<VectorType>()) { - S += vector->getNumElements(); - S += " * sizeof("; - - std::string tmp; - print(vector->getElementType(), tmp); - S += tmp; - S += ")"; + OS << vector->getNumElements(); + OS << " * sizeof("; + print(vector->getElementType(), OS, StringRef()); + OS << ')'; } - S += ")"; + OS << ')'; break; } case AttributedType::attr_neon_vector_type: case AttributedType::attr_neon_polyvector_type: { if (T->getAttrKind() == AttributedType::attr_neon_vector_type) - S += "neon_vector_type("; + OS << "neon_vector_type("; else - S += "neon_polyvector_type("; + OS << "neon_polyvector_type("; const VectorType *vector = T->getEquivalentType()->getAs<VectorType>(); - S += llvm::utostr_32(vector->getNumElements()); - S += ")"; + OS << vector->getNumElements(); + OS << ')'; break; } case AttributedType::attr_regparm: { - S += "regparm("; + OS << "regparm("; QualType t = T->getEquivalentType(); while (!t->isFunctionType()) t = t->getPointeeType(); - S += t->getAs<FunctionType>()->getRegParmType(); - S += ")"; + OS << t->getAs<FunctionType>()->getRegParmType(); + OS << ')'; break; } case AttributedType::attr_objc_gc: { - S += "objc_gc("; + OS << "objc_gc("; QualType tmp = T->getEquivalentType(); while (tmp.getObjCGCAttr() == Qualifiers::GCNone) { @@ -939,116 +1130,244 @@ void TypePrinter::printAttributed(const AttributedType *T, } if (tmp.isObjCGCWeak()) - S += "weak"; + OS << "weak"; else - S += "strong"; - S += ")"; + OS << "strong"; + OS << ')'; break; } case AttributedType::attr_objc_ownership: - S += "objc_ownership("; + OS << "objc_ownership("; switch (T->getEquivalentType().getObjCLifetime()) { case Qualifiers::OCL_None: llvm_unreachable("no ownership!"); - case Qualifiers::OCL_ExplicitNone: S += "none"; break; - case Qualifiers::OCL_Strong: S += "strong"; break; - case Qualifiers::OCL_Weak: S += "weak"; break; - case Qualifiers::OCL_Autoreleasing: S += "autoreleasing"; break; + case Qualifiers::OCL_ExplicitNone: OS << "none"; break; + case Qualifiers::OCL_Strong: OS << "strong"; break; + case Qualifiers::OCL_Weak: OS << "weak"; break; + case Qualifiers::OCL_Autoreleasing: OS << "autoreleasing"; break; } - S += ")"; + OS << ')'; break; - case AttributedType::attr_noreturn: S += "noreturn"; break; - case AttributedType::attr_cdecl: S += "cdecl"; break; - case AttributedType::attr_fastcall: S += "fastcall"; break; - case AttributedType::attr_stdcall: S += "stdcall"; break; - case AttributedType::attr_thiscall: S += "thiscall"; break; - case AttributedType::attr_pascal: S += "pascal"; break; + case AttributedType::attr_noreturn: OS << "noreturn"; break; + case AttributedType::attr_cdecl: OS << "cdecl"; break; + case AttributedType::attr_fastcall: OS << "fastcall"; break; + case AttributedType::attr_stdcall: OS << "stdcall"; break; + case AttributedType::attr_thiscall: OS << "thiscall"; break; + case AttributedType::attr_pascal: OS << "pascal"; break; case AttributedType::attr_pcs: { - S += "pcs("; + OS << "pcs("; QualType t = T->getEquivalentType(); while (!t->isFunctionType()) t = t->getPointeeType(); - S += (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ? + OS << (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ? "\"aapcs\"" : "\"aapcs-vfp\""); - S += ")"; + OS << ')'; break; } } - S += "))"; + OS << "))"; } -void TypePrinter::printObjCInterface(const ObjCInterfaceType *T, - std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'. - S = ' ' + S; - - std::string ObjCQIString = T->getDecl()->getNameAsString(); - S = ObjCQIString + S; +void TypePrinter::printObjCInterfaceBefore(const ObjCInterfaceType *T, + raw_ostream &OS) { + OS << T->getDecl()->getName(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printObjCInterfaceAfter(const ObjCInterfaceType *T, + raw_ostream &OS) { } -void TypePrinter::printObjCObject(const ObjCObjectType *T, - std::string &S) { +void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T, + raw_ostream &OS) { if (T->qual_empty()) - return print(T->getBaseType(), S); + return printBefore(T->getBaseType(), OS); - std::string tmp; - print(T->getBaseType(), tmp); - tmp += '<'; + print(T->getBaseType(), OS, StringRef()); + OS << '<'; bool isFirst = true; for (ObjCObjectType::qual_iterator I = T->qual_begin(), E = T->qual_end(); I != E; ++I) { if (isFirst) isFirst = false; else - tmp += ','; - tmp += (*I)->getNameAsString(); + OS << ','; + OS << (*I)->getName(); } - tmp += '>'; - - if (!S.empty()) { - tmp += ' '; - tmp += S; - } - std::swap(tmp, S); + OS << '>'; + spaceBeforePlaceHolder(OS); +} +void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T, + raw_ostream &OS) { + if (T->qual_empty()) + return printAfter(T->getBaseType(), OS); } -void TypePrinter::printObjCObjectPointer(const ObjCObjectPointerType *T, - std::string &S) { - std::string ObjCQIString; - - T->getPointeeType().getLocalQualifiers().getAsStringInternal(ObjCQIString, - Policy); - if (!ObjCQIString.empty()) - ObjCQIString += ' '; - +void TypePrinter::printObjCObjectPointerBefore(const ObjCObjectPointerType *T, + raw_ostream &OS) { + T->getPointeeType().getLocalQualifiers().print(OS, Policy, + /*appendSpaceIfNonEmpty=*/true); + if (T->isObjCIdType() || T->isObjCQualifiedIdType()) - ObjCQIString += "id"; + OS << "id"; else if (T->isObjCClassType() || T->isObjCQualifiedClassType()) - ObjCQIString += "Class"; + OS << "Class"; else if (T->isObjCSelType()) - ObjCQIString += "SEL"; + OS << "SEL"; else - ObjCQIString += T->getInterfaceDecl()->getNameAsString(); + OS << T->getInterfaceDecl()->getName(); if (!T->qual_empty()) { - ObjCQIString += '<'; + OS << '<'; for (ObjCObjectPointerType::qual_iterator I = T->qual_begin(), E = T->qual_end(); I != E; ++I) { - ObjCQIString += (*I)->getNameAsString(); + OS << (*I)->getName(); if (I+1 != E) - ObjCQIString += ','; + OS << ','; + } + OS << '>'; + } + + if (!T->isObjCIdType() && !T->isObjCQualifiedIdType()) { + OS << " *"; // Don't forget the implicit pointer. + } else { + spaceBeforePlaceHolder(OS); + } +} +void TypePrinter::printObjCObjectPointerAfter(const ObjCObjectPointerType *T, + raw_ostream &OS) { } + +void TemplateSpecializationType:: + PrintTemplateArgumentList(raw_ostream &OS, + const TemplateArgumentListInfo &Args, + const PrintingPolicy &Policy) { + return PrintTemplateArgumentList(OS, + Args.getArgumentArray(), + Args.size(), + Policy); +} + +void +TemplateSpecializationType::PrintTemplateArgumentList( + raw_ostream &OS, + const TemplateArgument *Args, + unsigned NumArgs, + const PrintingPolicy &Policy, + bool SkipBrackets) { + if (!SkipBrackets) + OS << '<'; + + bool needSpace = false; + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Arg > 0) + OS << ", "; + + // Print the argument into a string. + SmallString<128> Buf; + llvm::raw_svector_ostream ArgOS(Buf); + if (Args[Arg].getKind() == TemplateArgument::Pack) { + PrintTemplateArgumentList(ArgOS, + Args[Arg].pack_begin(), + Args[Arg].pack_size(), + Policy, true); + } else { + Args[Arg].print(Policy, ArgOS); + } + StringRef ArgString = ArgOS.str(); + + // If this is the first argument and its string representation + // begins with the global scope specifier ('::foo'), add a space + // to avoid printing the diagraph '<:'. + if (!Arg && !ArgString.empty() && ArgString[0] == ':') + OS << ' '; + + OS << ArgString; + + needSpace = (!ArgString.empty() && ArgString.back() == '>'); + } + + // If the last character of our string is '>', add another space to + // keep the two '>''s separate tokens. We don't *have* to do this in + // C++0x, but it's still good hygiene. + if (needSpace) + OS << ' '; + + if (!SkipBrackets) + OS << '>'; +} + +// Sadly, repeat all that with TemplateArgLoc. +void TemplateSpecializationType:: +PrintTemplateArgumentList(raw_ostream &OS, + const TemplateArgumentLoc *Args, unsigned NumArgs, + const PrintingPolicy &Policy) { + OS << '<'; + + bool needSpace = false; + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Arg > 0) + OS << ", "; + + // Print the argument into a string. + SmallString<128> Buf; + llvm::raw_svector_ostream ArgOS(Buf); + if (Args[Arg].getArgument().getKind() == TemplateArgument::Pack) { + PrintTemplateArgumentList(ArgOS, + Args[Arg].getArgument().pack_begin(), + Args[Arg].getArgument().pack_size(), + Policy, true); + } else { + Args[Arg].getArgument().print(Policy, ArgOS); } - ObjCQIString += '>'; + StringRef ArgString = ArgOS.str(); + + // If this is the first argument and its string representation + // begins with the global scope specifier ('::foo'), add a space + // to avoid printing the diagraph '<:'. + if (!Arg && !ArgString.empty() && ArgString[0] == ':') + OS << ' '; + + OS << ArgString; + + needSpace = (!ArgString.empty() && ArgString.back() == '>'); } - if (!T->isObjCIdType() && !T->isObjCQualifiedIdType()) - ObjCQIString += " *"; // Don't forget the implicit pointer. - else if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'. - S = ' ' + S; + // If the last character of our string is '>', add another space to + // keep the two '>''s separate tokens. We don't *have* to do this in + // C++0x, but it's still good hygiene. + if (needSpace) + OS << ' '; + + OS << '>'; +} + +void +FunctionProtoType::printExceptionSpecification(std::string &S, + PrintingPolicy Policy) const { - S = ObjCQIString + S; + if (hasDynamicExceptionSpec()) { + S += " throw("; + if (getExceptionSpecType() == EST_MSAny) + S += "..."; + else + for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) { + if (I) + S += ", "; + + S += getExceptionType(I).getAsString(Policy); + } + S += ")"; + } else if (isNoexceptExceptionSpec(getExceptionSpecType())) { + S += " noexcept"; + if (getExceptionSpecType() == EST_ComputedNoexcept) { + S += "("; + llvm::raw_string_ostream EOut(S); + getNoexceptExpr()->printPretty(EOut, 0, Policy); + EOut.flush(); + S += EOut.str(); + S += ")"; + } + } } std::string TemplateSpecializationType:: @@ -1148,15 +1467,14 @@ PrintTemplateArgumentList(const TemplateArgumentLoc *Args, unsigned NumArgs, } void QualType::dump(const char *msg) const { - std::string R = "identifier"; - LangOptions LO; - getAsStringInternal(R, PrintingPolicy(LO)); if (msg) llvm::errs() << msg << ": "; - llvm::errs() << R << "\n"; + LangOptions LO; + print(llvm::errs(), PrintingPolicy(LO), "identifier"); + llvm::errs() << '\n'; } void QualType::dump() const { - dump(""); + dump(0); } void Type::dump() const { @@ -1171,51 +1489,99 @@ std::string Qualifiers::getAsString() const { // Appends qualifiers to the given string, separated by spaces. Will // prefix a space if the string is non-empty. Will not append a final // space. -void Qualifiers::getAsStringInternal(std::string &S, - const PrintingPolicy& Policy) const { - AppendTypeQualList(S, getCVRQualifiers()); +std::string Qualifiers::getAsString(const PrintingPolicy &Policy) const { + SmallString<64> Buf; + llvm::raw_svector_ostream StrOS(Buf); + print(StrOS, Policy); + return StrOS.str(); +} + +bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const { + if (getCVRQualifiers()) + return false; + + if (getAddressSpace()) + return false; + + if (getObjCGCAttr()) + return false; + + if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) + if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) + return false; + + return true; +} + +// Appends qualifiers to the given string, separated by spaces. Will +// prefix a space if the string is non-empty. Will not append a final +// space. +void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy, + bool appendSpaceIfNonEmpty) const { + bool addSpace = false; + + unsigned quals = getCVRQualifiers(); + if (quals) { + AppendTypeQualList(OS, quals); + addSpace = true; + } if (unsigned addrspace = getAddressSpace()) { - if (!S.empty()) S += ' '; + if (addSpace) + OS << ' '; + addSpace = true; switch (addrspace) { case LangAS::opencl_global: - S += "__global"; + OS << "__global"; break; case LangAS::opencl_local: - S += "__local"; + OS << "__local"; break; case LangAS::opencl_constant: - S += "__constant"; + OS << "__constant"; break; default: - S += "__attribute__((address_space("; - S += llvm::utostr_32(addrspace); - S += ")))"; + OS << "__attribute__((address_space("; + OS << addrspace; + OS << ")))"; } } if (Qualifiers::GC gc = getObjCGCAttr()) { - if (!S.empty()) S += ' '; + if (addSpace) + OS << ' '; + addSpace = true; if (gc == Qualifiers::Weak) - S += "__weak"; + OS << "__weak"; else - S += "__strong"; + OS << "__strong"; } if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) { - if (!S.empty() && - !(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) - S += ' '; - + if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)){ + if (addSpace) + OS << ' '; + addSpace = true; + } + switch (lifetime) { case Qualifiers::OCL_None: llvm_unreachable("none but true"); - case Qualifiers::OCL_ExplicitNone: S += "__unsafe_unretained"; break; + case Qualifiers::OCL_ExplicitNone: OS << "__unsafe_unretained"; break; case Qualifiers::OCL_Strong: if (!Policy.SuppressStrongLifetime) - S += "__strong"; + OS << "__strong"; break; - case Qualifiers::OCL_Weak: S += "__weak"; break; - case Qualifiers::OCL_Autoreleasing: S += "__autoreleasing"; break; + case Qualifiers::OCL_Weak: OS << "__weak"; break; + case Qualifiers::OCL_Autoreleasing: OS << "__autoreleasing"; break; } } + + if (appendSpaceIfNonEmpty && addSpace) + OS << ' '; +} + +std::string QualType::getAsString(const PrintingPolicy &Policy) const { + std::string S; + getAsStringInternal(S, Policy); + return S; } std::string QualType::getAsString(const Type *ty, Qualifiers qs) { @@ -1225,8 +1591,25 @@ std::string QualType::getAsString(const Type *ty, Qualifiers qs) { return buffer; } +void QualType::print(const Type *ty, Qualifiers qs, + raw_ostream &OS, const PrintingPolicy &policy, + const Twine &PlaceHolder) { + SmallString<128> PHBuf; + StringRef PH; + if (PlaceHolder.isSingleStringRef()) + PH = PlaceHolder.getSingleStringRef(); + else + PH = PlaceHolder.toStringRef(PHBuf); + + TypePrinter(policy).print(ty, qs, OS, PH); +} + void QualType::getAsStringInternal(const Type *ty, Qualifiers qs, std::string &buffer, const PrintingPolicy &policy) { - TypePrinter(policy).print(ty, qs, buffer); + SmallString<256> Buf; + llvm::raw_svector_ostream StrOS(Buf); + TypePrinter(policy).print(ty, qs, StrOS, buffer); + std::string str = StrOS.str(); + buffer.swap(str); } diff --git a/lib/AST/VTTBuilder.cpp b/lib/AST/VTTBuilder.cpp index f5ff624cf069..5ca4e862ef7b 100644 --- a/lib/AST/VTTBuilder.cpp +++ b/lib/AST/VTTBuilder.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/VTTBuilder.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 107d9fb78c50..104530fc1305 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/VTableBuilder.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" @@ -164,7 +165,7 @@ FinalOverriders::FinalOverriders(const CXXRecordDecl *MostDerivedClass, SubobjectOffsets, SubobjectLayoutClassOffsets, SubobjectCounts); - // Get the the final overriders. + // Get the final overriders. CXXFinalOverriderMap FinalOverriders; MostDerivedClass->getFinalOverriders(FinalOverriders); @@ -630,7 +631,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base, // Get the base offset of the primary base. if (PrimaryBaseIsVirtual) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = @@ -639,7 +640,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base, PrimaryBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(PrimaryBase); } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); @@ -682,7 +683,7 @@ void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base, // primary base will have its vcall and vbase offsets emitted already. if (PrimaryBase && !Layout.isPrimaryBaseVirtual()) { // Get the base offset of the primary base. - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); AddVCallOffsets(BaseSubobject(PrimaryBase, Base.getBaseOffset()), @@ -1370,7 +1371,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider, break; if (Layout.isPrimaryBaseVirtual()) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); const ASTRecordLayout &LayoutClassLayout = @@ -1384,7 +1385,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider, break; } } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); } @@ -1436,7 +1437,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, CharUnits PrimaryBaseOffset; CharUnits PrimaryBaseOffsetInLayoutClass; if (Layout.isPrimaryBaseVirtual()) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = @@ -1451,7 +1452,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, PrimaryBaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(PrimaryBase); } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp new file mode 100644 index 000000000000..085049debdbe --- /dev/null +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -0,0 +1,547 @@ +//===--- ASTMatchFinder.cpp - Structural query framework ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements an algorithm to efficiently search for matches on AST nodes. +// Uses memoization to support recursive matches like HasDescendant. +// +// The general idea is to visit all AST nodes with a RecursiveASTVisitor, +// calling the Matches(...) method of each matcher we are running on each +// AST node. The matcher can recurse via the ASTMatchFinder interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include <set> + +namespace clang { +namespace ast_matchers { +namespace internal { +namespace { + +// We use memoization to avoid running the same matcher on the same +// AST node twice. This pair is the key for looking up match +// result. It consists of an ID of the MatcherInterface (for +// identifying the matcher) and a pointer to the AST node. +typedef std::pair<uint64_t, const void*> UntypedMatchInput; + +// Used to store the result of a match and possibly bound nodes. +struct MemoizedMatchResult { + bool ResultOfMatch; + BoundNodesTree Nodes; +}; + +// A RecursiveASTVisitor that traverses all children or all descendants of +// a node. +class MatchChildASTVisitor + : public RecursiveASTVisitor<MatchChildASTVisitor> { +public: + typedef RecursiveASTVisitor<MatchChildASTVisitor> VisitorBase; + + // Creates an AST visitor that matches 'matcher' on all children or + // descendants of a traversed node. max_depth is the maximum depth + // to traverse: use 1 for matching the children and INT_MAX for + // matching the descendants. + MatchChildASTVisitor(const UntypedBaseMatcher *BaseMatcher, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder, + int MaxDepth, + ASTMatchFinder::TraversalKind Traversal, + ASTMatchFinder::BindKind Bind) + : BaseMatcher(BaseMatcher), + Finder(Finder), + Builder(Builder), + CurrentDepth(-1), + MaxDepth(MaxDepth), + Traversal(Traversal), + Bind(Bind), + Matches(false) {} + + // Returns true if a match is found in the subtree rooted at the + // given AST node. This is done via a set of mutually recursive + // functions. Here's how the recursion is done (the *wildcard can + // actually be Decl, Stmt, or Type): + // + // - Traverse(node) calls BaseTraverse(node) when it needs + // to visit the descendants of node. + // - BaseTraverse(node) then calls (via VisitorBase::Traverse*(node)) + // Traverse*(c) for each child c of 'node'. + // - Traverse*(c) in turn calls Traverse(c), completing the + // recursion. + template <typename T> + bool findMatch(const T &Node) { + reset(); + traverse(Node); + return Matches; + } + + // The following are overriding methods from the base visitor class. + // They are public only to allow CRTP to work. They are *not *part + // of the public API of this class. + bool TraverseDecl(Decl *DeclNode) { + return (DeclNode == NULL) || traverse(*DeclNode); + } + bool TraverseStmt(Stmt *StmtNode) { + const Stmt *StmtToTraverse = StmtNode; + if (Traversal == + ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) { + const Expr *ExprNode = dyn_cast_or_null<Expr>(StmtNode); + if (ExprNode != NULL) { + StmtToTraverse = ExprNode->IgnoreParenImpCasts(); + } + } + return (StmtToTraverse == NULL) || traverse(*StmtToTraverse); + } + bool TraverseType(QualType TypeNode) { + return traverse(TypeNode); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + +private: + // Used for updating the depth during traversal. + struct ScopedIncrement { + explicit ScopedIncrement(int *Depth) : Depth(Depth) { ++(*Depth); } + ~ScopedIncrement() { --(*Depth); } + + private: + int *Depth; + }; + + // Resets the state of this object. + void reset() { + Matches = false; + CurrentDepth = -1; + } + + // Forwards the call to the corresponding Traverse*() method in the + // base visitor class. + bool baseTraverse(const Decl &DeclNode) { + return VisitorBase::TraverseDecl(const_cast<Decl*>(&DeclNode)); + } + bool baseTraverse(const Stmt &StmtNode) { + return VisitorBase::TraverseStmt(const_cast<Stmt*>(&StmtNode)); + } + bool baseTraverse(QualType TypeNode) { + return VisitorBase::TraverseType(TypeNode); + } + + // Traverses the subtree rooted at 'node'; returns true if the + // traversal should continue after this function returns; also sets + // matched_ to true if a match is found during the traversal. + template <typename T> + bool traverse(const T &Node) { + TOOLING_COMPILE_ASSERT(IsBaseType<T>::value, + traverse_can_only_be_instantiated_with_base_type); + ScopedIncrement ScopedDepth(&CurrentDepth); + if (CurrentDepth == 0) { + // We don't want to match the root node, so just recurse. + return baseTraverse(Node); + } + if (Bind != ASTMatchFinder::BK_All) { + if (BaseMatcher->matches(Node, Finder, Builder)) { + Matches = true; + return false; // Abort as soon as a match is found. + } + if (CurrentDepth < MaxDepth) { + // The current node doesn't match, and we haven't reached the + // maximum depth yet, so recurse. + return baseTraverse(Node); + } + // The current node doesn't match, and we have reached the + // maximum depth, so don't recurse (but continue the traversal + // such that other nodes at the current level can be visited). + return true; + } else { + BoundNodesTreeBuilder RecursiveBuilder; + if (BaseMatcher->matches(Node, Finder, &RecursiveBuilder)) { + // After the first match the matcher succeeds. + Matches = true; + Builder->addMatch(RecursiveBuilder.build()); + } + if (CurrentDepth < MaxDepth) { + baseTraverse(Node); + } + // In kBindAll mode we always search for more matches. + return true; + } + } + + const UntypedBaseMatcher *const BaseMatcher; + ASTMatchFinder *const Finder; + BoundNodesTreeBuilder *const Builder; + int CurrentDepth; + const int MaxDepth; + const ASTMatchFinder::TraversalKind Traversal; + const ASTMatchFinder::BindKind Bind; + bool Matches; +}; + +// Controls the outermost traversal of the AST and allows to match multiple +// matchers. +class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>, + public ASTMatchFinder { +public: + MatchASTVisitor(std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *Triggers) + : Triggers(Triggers), + ActiveASTContext(NULL) { + } + + void set_active_ast_context(ASTContext *NewActiveASTContext) { + ActiveASTContext = NewActiveASTContext; + } + + // The following Visit*() and Traverse*() functions "override" + // methods in RecursiveASTVisitor. + + bool VisitTypedefDecl(TypedefDecl *DeclNode) { + // When we see 'typedef A B', we add name 'B' to the set of names + // A's canonical type maps to. This is necessary for implementing + // IsDerivedFrom(x) properly, where x can be the name of the base + // class or any of its aliases. + // + // In general, the is-alias-of (as defined by typedefs) relation + // is tree-shaped, as you can typedef a type more than once. For + // example, + // + // typedef A B; + // typedef A C; + // typedef C D; + // typedef C E; + // + // gives you + // + // A + // |- B + // `- C + // |- D + // `- E + // + // It is wrong to assume that the relation is a chain. A correct + // implementation of IsDerivedFrom() needs to recognize that B and + // E are aliases, even though neither is a typedef of the other. + // Therefore, we cannot simply walk through one typedef chain to + // find out whether the type name matches. + const Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr(); + const Type *CanonicalType = // root of the typedef tree + ActiveASTContext->getCanonicalType(TypeNode); + TypeAliases[CanonicalType].insert(DeclNode); + return true; + } + + bool TraverseDecl(Decl *DeclNode); + bool TraverseStmt(Stmt *StmtNode); + bool TraverseType(QualType TypeNode); + bool TraverseTypeLoc(TypeLoc TypeNode); + + // Matches children or descendants of 'Node' with 'BaseMatcher'. + template <typename T> + bool memoizedMatchesRecursively(const T &Node, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, int MaxDepth, + TraversalKind Traversal, BindKind Bind) { + TOOLING_COMPILE_ASSERT((llvm::is_same<T, Decl>::value) || + (llvm::is_same<T, Stmt>::value), + type_does_not_support_memoization); + const UntypedMatchInput input(BaseMatcher.getID(), &Node); + std::pair<MemoizationMap::iterator, bool> InsertResult + = ResultCache.insert(std::make_pair(input, MemoizedMatchResult())); + if (InsertResult.second) { + BoundNodesTreeBuilder DescendantBoundNodesBuilder; + InsertResult.first->second.ResultOfMatch = + matchesRecursively(Node, BaseMatcher, &DescendantBoundNodesBuilder, + MaxDepth, Traversal, Bind); + InsertResult.first->second.Nodes = + DescendantBoundNodesBuilder.build(); + } + InsertResult.first->second.Nodes.copyTo(Builder); + return InsertResult.first->second.ResultOfMatch; + } + + // Matches children or descendants of 'Node' with 'BaseMatcher'. + template <typename T> + bool matchesRecursively(const T &Node, const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, int MaxDepth, + TraversalKind Traversal, BindKind Bind) { + MatchChildASTVisitor Visitor( + &BaseMatcher, this, Builder, MaxDepth, Traversal, Bind); + return Visitor.findMatch(Node); + } + + virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, + const Matcher<NamedDecl> &Base, + BoundNodesTreeBuilder *Builder); + + // Implements ASTMatchFinder::MatchesChildOf. + virtual bool matchesChildOf(const Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traversal, + BindKind Bind) { + return matchesRecursively(DeclNode, BaseMatcher, Builder, 1, Traversal, + Bind); + } + virtual bool matchesChildOf(const Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traversal, + BindKind Bind) { + return matchesRecursively(StmtNode, BaseMatcher, Builder, 1, Traversal, + Bind); + } + + // Implements ASTMatchFinder::MatchesDescendantOf. + virtual bool matchesDescendantOf(const Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + return memoizedMatchesRecursively(DeclNode, BaseMatcher, Builder, INT_MAX, + TK_AsIs, Bind); + } + virtual bool matchesDescendantOf(const Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + return memoizedMatchesRecursively(StmtNode, BaseMatcher, Builder, INT_MAX, + TK_AsIs, Bind); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + +private: + // Implements a BoundNodesTree::Visitor that calls a MatchCallback with + // the aggregated bound nodes for each match. + class MatchVisitor : public BoundNodesTree::Visitor { + public: + MatchVisitor(ASTContext* Context, + MatchFinder::MatchCallback* Callback) + : Context(Context), + Callback(Callback) {} + + virtual void visitMatch(const BoundNodes& BoundNodesView) { + Callback->run(MatchFinder::MatchResult(BoundNodesView, Context)); + } + + private: + ASTContext* Context; + MatchFinder::MatchCallback* Callback; + }; + + // Returns true if 'TypeNode' has an alias that matches the given matcher. + bool typeHasMatchingAlias(const Type *TypeNode, + const Matcher<NamedDecl> Matcher, + BoundNodesTreeBuilder *Builder) { + const Type *const CanonicalType = + ActiveASTContext->getCanonicalType(TypeNode); + const std::set<const TypedefDecl*> &Aliases = TypeAliases[CanonicalType]; + for (std::set<const TypedefDecl*>::const_iterator + It = Aliases.begin(), End = Aliases.end(); + It != End; ++It) { + if (Matcher.matches(**It, this, Builder)) + return true; + } + return false; + } + + // Matches all registered matchers on the given node and calls the + // result callback for every node that matches. + template <typename T> + void match(const T &node) { + for (std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> >::const_iterator + It = Triggers->begin(), End = Triggers->end(); + It != End; ++It) { + BoundNodesTreeBuilder Builder; + if (It->first->matches(node, this, &Builder)) { + BoundNodesTree BoundNodes = Builder.build(); + MatchVisitor Visitor(ActiveASTContext, It->second); + BoundNodes.visitMatches(&Visitor); + } + } + } + + std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *const Triggers; + ASTContext *ActiveASTContext; + + // Maps a canonical type to its TypedefDecls. + llvm::DenseMap<const Type*, std::set<const TypedefDecl*> > TypeAliases; + + // Maps (matcher, node) -> the match result for memoization. + typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap; + MemoizationMap ResultCache; +}; + +// Returns true if the given class is directly or indirectly derived +// from a base type with the given name. A class is considered to be +// also derived from itself. +bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, + const Matcher<NamedDecl> &Base, + BoundNodesTreeBuilder *Builder) { + if (Base.matches(*Declaration, this, Builder)) + return true; + if (!Declaration->hasDefinition()) + return false; + typedef CXXRecordDecl::base_class_const_iterator BaseIterator; + for (BaseIterator It = Declaration->bases_begin(), + End = Declaration->bases_end(); It != End; ++It) { + const Type *TypeNode = It->getType().getTypePtr(); + + if (typeHasMatchingAlias(TypeNode, Base, Builder)) + return true; + + // Type::getAs<...>() drills through typedefs. + if (TypeNode->getAs<DependentNameType>() != NULL || + TypeNode->getAs<TemplateTypeParmType>() != NULL) + // Dependent names and template TypeNode parameters will be matched when + // the template is instantiated. + continue; + CXXRecordDecl *ClassDecl = NULL; + TemplateSpecializationType const *TemplateType = + TypeNode->getAs<TemplateSpecializationType>(); + if (TemplateType != NULL) { + if (TemplateType->getTemplateName().isDependent()) + // Dependent template specializations will be matched when the + // template is instantiated. + continue; + + // For template specialization types which are specializing a template + // declaration which is an explicit or partial specialization of another + // template declaration, getAsCXXRecordDecl() returns the corresponding + // ClassTemplateSpecializationDecl. + // + // For template specialization types which are specializing a template + // declaration which is neither an explicit nor partial specialization of + // another template declaration, getAsCXXRecordDecl() returns NULL and + // we get the CXXRecordDecl of the templated declaration. + CXXRecordDecl *SpecializationDecl = + TemplateType->getAsCXXRecordDecl(); + if (SpecializationDecl != NULL) { + ClassDecl = SpecializationDecl; + } else { + ClassDecl = llvm::dyn_cast<CXXRecordDecl>( + TemplateType->getTemplateName() + .getAsTemplateDecl()->getTemplatedDecl()); + } + } else { + ClassDecl = TypeNode->getAsCXXRecordDecl(); + } + assert(ClassDecl != NULL); + assert(ClassDecl != Declaration); + if (classIsDerivedFrom(ClassDecl, Base, Builder)) + return true; + } + return false; +} + +bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) { + if (DeclNode == NULL) { + return true; + } + match(*DeclNode); + return RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode); +} + +bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode) { + if (StmtNode == NULL) { + return true; + } + match(*StmtNode); + return RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode); +} + +bool MatchASTVisitor::TraverseType(QualType TypeNode) { + match(TypeNode); + return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode); +} + +bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) { + match(TypeLoc.getType()); + return RecursiveASTVisitor<MatchASTVisitor>:: + TraverseTypeLoc(TypeLoc); +} + +class MatchASTConsumer : public ASTConsumer { +public: + MatchASTConsumer(std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *Triggers, + MatchFinder::ParsingDoneTestCallback *ParsingDone) + : Visitor(Triggers), + ParsingDone(ParsingDone) {} + +private: + virtual void HandleTranslationUnit(ASTContext &Context) { + if (ParsingDone != NULL) { + ParsingDone->run(); + } + Visitor.set_active_ast_context(&Context); + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + Visitor.set_active_ast_context(NULL); + } + + MatchASTVisitor Visitor; + MatchFinder::ParsingDoneTestCallback *ParsingDone; +}; + +} // end namespace +} // end namespace internal + +MatchFinder::MatchResult::MatchResult(const BoundNodes &Nodes, + ASTContext *Context) + : Nodes(Nodes), Context(Context), + SourceManager(&Context->getSourceManager()) {} + +MatchFinder::MatchCallback::~MatchCallback() {} +MatchFinder::ParsingDoneTestCallback::~ParsingDoneTestCallback() {} + +MatchFinder::MatchFinder() : ParsingDone(NULL) {} + +MatchFinder::~MatchFinder() { + for (std::vector< std::pair<const internal::UntypedBaseMatcher*, + MatchFinder::MatchCallback*> >::const_iterator + It = Triggers.begin(), End = Triggers.end(); + It != End; ++It) { + delete It->first; + } +} + +void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<Decl>(NodeMatch), Action)); +} + +void MatchFinder::addMatcher(const TypeMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<QualType>(NodeMatch), Action)); +} + +void MatchFinder::addMatcher(const StatementMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<Stmt>(NodeMatch), Action)); +} + +ASTConsumer *MatchFinder::newASTConsumer() { + return new internal::MatchASTConsumer(&Triggers, ParsingDone); +} + +void MatchFinder::registerTestCallbackAfterParsing( + MatchFinder::ParsingDoneTestCallback *NewParsingDone) { + ParsingDone = NewParsingDone; +} + +} // end namespace ast_matchers +} // end namespace clang diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp new file mode 100644 index 000000000000..69c51905fe8f --- /dev/null +++ b/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -0,0 +1,102 @@ +//===--- ASTMatchersInternal.cpp - Structural query framework -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the base layer of the matcher framework. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" + +namespace clang { +namespace ast_matchers { +namespace internal { + +BoundNodesTree::BoundNodesTree() {} + +BoundNodesTree::BoundNodesTree( + const std::map<std::string, const Decl*>& DeclBindings, + const std::map<std::string, const Stmt*>& StmtBindings, + const std::vector<BoundNodesTree> RecursiveBindings) + : DeclBindings(DeclBindings), StmtBindings(StmtBindings), + RecursiveBindings(RecursiveBindings) {} + +void BoundNodesTree::copyTo(BoundNodesTreeBuilder* Builder) const { + copyBindingsTo(DeclBindings, Builder); + copyBindingsTo(StmtBindings, Builder); + for (std::vector<BoundNodesTree>::const_iterator + I = RecursiveBindings.begin(), + E = RecursiveBindings.end(); + I != E; ++I) { + Builder->addMatch(*I); + } +} + +template <typename T> +void BoundNodesTree::copyBindingsTo( + const T& Bindings, BoundNodesTreeBuilder* Builder) const { + for (typename T::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) { + Builder->setBinding(I->first, I->second); + } +} + +void BoundNodesTree::visitMatches(Visitor* ResultVisitor) { + std::map<std::string, const Decl*> AggregatedDeclBindings; + std::map<std::string, const Stmt*> AggregatedStmtBindings; + visitMatchesRecursively(ResultVisitor, AggregatedDeclBindings, + AggregatedStmtBindings); +} + +void BoundNodesTree:: +visitMatchesRecursively(Visitor* ResultVisitor, + std::map<std::string, const Decl*> + AggregatedDeclBindings, + std::map<std::string, const Stmt*> + AggregatedStmtBindings) { + copy(DeclBindings.begin(), DeclBindings.end(), + inserter(AggregatedDeclBindings, AggregatedDeclBindings.begin())); + copy(StmtBindings.begin(), StmtBindings.end(), + inserter(AggregatedStmtBindings, AggregatedStmtBindings.begin())); + if (RecursiveBindings.empty()) { + ResultVisitor->visitMatch(BoundNodes(AggregatedDeclBindings, + AggregatedStmtBindings)); + } else { + for (unsigned I = 0; I < RecursiveBindings.size(); ++I) { + RecursiveBindings[I].visitMatchesRecursively(ResultVisitor, + AggregatedDeclBindings, + AggregatedStmtBindings); + } + } +} + +BoundNodesTreeBuilder::BoundNodesTreeBuilder() {} + +void BoundNodesTreeBuilder::setBinding(const std::string &Id, + const Decl *Node) { + DeclBindings[Id] = Node; +} + +void BoundNodesTreeBuilder::setBinding(const std::string &Id, + const Stmt *Node) { + StmtBindings[Id] = Node; +} + +void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) { + RecursiveBindings.push_back(Bindings); +} + +BoundNodesTree BoundNodesTreeBuilder::build() const { + return BoundNodesTree(DeclBindings, StmtBindings, RecursiveBindings); +} + +} // end namespace internal +} // end namespace ast_matchers +} // end namespace clang diff --git a/lib/ASTMatchers/CMakeLists.txt b/lib/ASTMatchers/CMakeLists.txt new file mode 100644 index 000000000000..8fc7d4b208e8 --- /dev/null +++ b/lib/ASTMatchers/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS support) +set(LLVM_USED_LIBS clangBasic clangAST) + +add_clang_library(clangASTMatchers + ASTMatchFinder.cpp + ASTMatchersInternal.cpp + ) + +add_dependencies(clangASTMatchers + ClangAttrClasses + ClangAttrImpl + ClangAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangStmtNodes + ) diff --git a/lib/ASTMatchers/Makefile b/lib/ASTMatchers/Makefile new file mode 100644 index 000000000000..76d82719a103 --- /dev/null +++ b/lib/ASTMatchers/Makefile @@ -0,0 +1,13 @@ +##===- clang/lib/ASTMatchers/Makefile ----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangASTMatchers + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index 659cc6d3f1de..7de7f395e8a7 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -34,11 +35,9 @@ typedef llvm::DenseMap<const void *, ManagedAnalysis *> ManagedAnalysisMap; AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *d, - idx::TranslationUnit *tu, const CFG::BuildOptions &buildOptions) : Manager(Mgr), D(d), - TU(tu), cfgBuildOptions(buildOptions), forcedBlkExprs(0), builtCFG(false), @@ -50,11 +49,9 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, } AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, - const Decl *d, - idx::TranslationUnit *tu) + const Decl *d) : Manager(Mgr), D(d), - TU(tu), forcedBlkExprs(0), builtCFG(false), builtCompleteCFG(false), @@ -184,8 +181,16 @@ void AnalysisDeclContext::dumpCFG(bool ShowColors) { } ParentMap &AnalysisDeclContext::getParentMap() { - if (!PM) + if (!PM) { PM.reset(new ParentMap(getBody())); + if (const CXXConstructorDecl *C = dyn_cast<CXXConstructorDecl>(getDecl())) { + for (CXXConstructorDecl::init_const_iterator I = C->init_begin(), + E = C->init_end(); + I != E; ++I) { + PM->addStmt((*I)->getInit()); + } + } + } return *PM; } @@ -195,11 +200,10 @@ PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() { return PCA.get(); } -AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D, - idx::TranslationUnit *TU) { +AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) { AnalysisDeclContext *&AC = Contexts[D]; if (!AC) - AC = new AnalysisDeclContext(this, D, TU, cfgBuildOptions); + AC = new AnalysisDeclContext(this, D, cfgBuildOptions); return AC; } @@ -209,6 +213,14 @@ AnalysisDeclContext::getStackFrame(LocationContext const *Parent, const Stmt *S, return getLocationContextManager().getStackFrame(this, Parent, S, Blk, Idx); } +const BlockInvocationContext * +AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, + const clang::BlockDecl *BD, + const void *ContextData) { + return getLocationContextManager().getBlockInvocationContext(this, parent, + BD, ContextData); +} + LocationContextManager & AnalysisDeclContext::getLocationContextManager() { assert(Manager && "Cannot create LocationContexts without an AnalysisDeclContextManager!"); @@ -239,7 +251,7 @@ void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { } void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAnalysisDeclContext(), getParent(), BD); + Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData); } //===----------------------------------------------------------------------===// @@ -288,6 +300,24 @@ LocationContextManager::getScope(AnalysisDeclContext *ctx, return getLocationContext<ScopeContext, Stmt>(ctx, parent, s); } +const BlockInvocationContext * +LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx, + const LocationContext *parent, + const BlockDecl *BD, + const void *ContextData) { + llvm::FoldingSetNodeID ID; + BlockInvocationContext::Profile(ID, ctx, parent, BD, ContextData); + void *InsertPos; + BlockInvocationContext *L = + cast_or_null<BlockInvocationContext>(Contexts.FindNodeOrInsertPos(ID, + InsertPos)); + if (!L) { + L = new BlockInvocationContext(ctx, parent, BD, ContextData); + Contexts.InsertNode(L, InsertPos); + } + return L; +} + //===----------------------------------------------------------------------===// // LocationContext methods. //===----------------------------------------------------------------------===// @@ -302,19 +332,6 @@ const StackFrameContext *LocationContext::getCurrentStackFrame() const { return NULL; } -const StackFrameContext * -LocationContext::getStackFrameForDeclContext(const DeclContext *DC) const { - const LocationContext *LC = this; - while (LC) { - if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { - if (cast<DeclContext>(SFC->getDecl()) == DC) - return SFC; - } - LC = LC->getParent(); - } - return NULL; -} - bool LocationContext::isParentOf(const LocationContext *LC) const { do { const LocationContext *Parent = LC->getParent(); diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 2f1f1cb4e4da..05c53850f568 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1,4 +1,4 @@ -//===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===// + //===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,6 +14,7 @@ #include "llvm/Support/SaveAndRestore.h" #include "clang/Analysis/CFG.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/PrettyPrinter.h" @@ -312,19 +313,6 @@ private: CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc); CFGBlock *VisitBinaryOperator(BinaryOperator *B, AddStmtChoice asc); CFGBlock *VisitBreakStmt(BreakStmt *B); - CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); - CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, - AddStmtChoice asc); - CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); - CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); - CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); - CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, - AddStmtChoice asc); - CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); - CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, - AddStmtChoice asc); - CFGBlock *VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, - AddStmtChoice asc); CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc); CFGBlock *VisitCaseStmt(CaseStmt *C); CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc); @@ -332,31 +320,47 @@ private: CFGBlock *VisitConditionalOperator(AbstractConditionalOperator *C, AddStmtChoice asc); CFGBlock *VisitContinueStmt(ContinueStmt *C); + CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, + AddStmtChoice asc); + CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); + CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); + CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); + CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, + AddStmtChoice asc); + CFGBlock *VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, + AddStmtChoice asc); + CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); + CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); CFGBlock *VisitDeclStmt(DeclStmt *DS); CFGBlock *VisitDeclSubExpr(DeclStmt *DS); CFGBlock *VisitDefaultStmt(DefaultStmt *D); CFGBlock *VisitDoStmt(DoStmt *D); - CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc); + CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc); CFGBlock *VisitForStmt(ForStmt *F); CFGBlock *VisitGotoStmt(GotoStmt *G); CFGBlock *VisitIfStmt(IfStmt *I); CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc); CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I); CFGBlock *VisitLabelStmt(LabelStmt *L); - CFGBlock *VisitLambdaExpr(LambdaExpr *L); + CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc); + CFGBlock *VisitLogicalOperator(BinaryOperator *B); + std::pair<CFGBlock *, CFGBlock *> VisitLogicalOperator(BinaryOperator *B, + Stmt *Term, + CFGBlock *TrueBlock, + CFGBlock *FalseBlock); CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc); CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S); - CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S); CFGBlock *VisitObjCAtThrowStmt(ObjCAtThrowStmt *S); CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S); + CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); - CFGBlock *VisitReturnStmt(ReturnStmt *R); CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); - CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, - AddStmtChoice asc); + CFGBlock *VisitReturnStmt(ReturnStmt *R); CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc); CFGBlock *VisitSwitchStmt(SwitchStmt *S); + CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, + AddStmtChoice asc); CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); CFGBlock *VisitWhileStmt(WhileStmt *W); @@ -772,13 +776,12 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, // If this destructor is marked as a no-return destructor, we need to // create a new block for the destructor which does not have as a successor // anything built thus far: control won't flow out of this block. - QualType Ty; - if ((*I)->getType()->isReferenceType()) { + QualType Ty = (*I)->getType(); + if (Ty->isReferenceType()) { Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit()); - } else { - Ty = Context->getBaseElementType((*I)->getType()); } - + Ty = Context->getBaseElementType(Ty); + const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor(); if (cast<FunctionType>(Dtor->getType())->getNoReturnAttr()) Block = createNoReturnBlock(); @@ -1070,9 +1073,6 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::LambdaExprClass: return VisitLambdaExpr(cast<LambdaExpr>(S), asc); - case Stmt::AttributedStmtClass: - return Visit(cast<AttributedStmt>(S)->getSubStmt(), asc); - case Stmt::MemberExprClass: return VisitMemberExpr(cast<MemberExpr>(S), asc); @@ -1166,55 +1166,111 @@ CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U, return Visit(U->getSubExpr(), AddStmtChoice()); } -CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, - AddStmtChoice asc) { - if (B->isLogicalOp()) { // && or || - CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); - appendStmt(ConfluenceBlock, B); +CFGBlock *CFGBuilder::VisitLogicalOperator(BinaryOperator *B) { + CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); + appendStmt(ConfluenceBlock, B); - if (badCFG) - return 0; + if (badCFG) + return 0; - // create the block evaluating the LHS - CFGBlock *LHSBlock = createBlock(false); - LHSBlock->setTerminator(B); + return VisitLogicalOperator(B, 0, ConfluenceBlock, ConfluenceBlock).first; +} - // create the block evaluating the RHS - Succ = ConfluenceBlock; - Block = NULL; - CFGBlock *RHSBlock = addStmt(B->getRHS()); +std::pair<CFGBlock*, CFGBlock*> +CFGBuilder::VisitLogicalOperator(BinaryOperator *B, + Stmt *Term, + CFGBlock *TrueBlock, + CFGBlock *FalseBlock) { - if (RHSBlock) { - if (badCFG) - return 0; - } else { - // Create an empty block for cases where the RHS doesn't require - // any explicit statements in the CFG. - RHSBlock = createBlock(); + // Introspect the RHS. If it is a nested logical operation, we recursively + // build the CFG using this function. Otherwise, resort to default + // CFG construction behavior. + Expr *RHS = B->getRHS()->IgnoreParens(); + CFGBlock *RHSBlock, *ExitBlock; + + do { + if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS)) + if (B_RHS->isLogicalOp()) { + llvm::tie(RHSBlock, ExitBlock) = + VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock); + break; + } + + // The RHS is not a nested logical operation. Don't push the terminator + // down further, but instead visit RHS and construct the respective + // pieces of the CFG, and link up the RHSBlock with the terminator + // we have been provided. + ExitBlock = RHSBlock = createBlock(false); + + if (!Term) { + assert(TrueBlock == FalseBlock); + addSuccessor(RHSBlock, TrueBlock); + } + else { + RHSBlock->setTerminator(Term); + TryResult KnownVal = tryEvaluateBool(RHS); + addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); + addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); } - // Generate the blocks for evaluating the LHS. - Block = LHSBlock; - CFGBlock *EntryLHSBlock = addStmt(B->getLHS()); + Block = RHSBlock; + RHSBlock = addStmt(RHS); + } + while (false); - // See if this is a known constant. - TryResult KnownVal = tryEvaluateBool(B->getLHS()); - if (KnownVal.isKnown() && (B->getOpcode() == BO_LOr)) - KnownVal.negate(); + if (badCFG) + return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + + // Generate the blocks for evaluating the LHS. + Expr *LHS = B->getLHS()->IgnoreParens(); + + if (BinaryOperator *B_LHS = dyn_cast<BinaryOperator>(LHS)) + if (B_LHS->isLogicalOp()) { + if (B->getOpcode() == BO_LOr) + FalseBlock = RHSBlock; + else + TrueBlock = RHSBlock; - // Now link the LHSBlock with RHSBlock. - if (B->getOpcode() == BO_LOr) { - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); - } else { - assert(B->getOpcode() == BO_LAnd); - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + // For the LHS, treat 'B' as the terminator that we want to sink + // into the nested branch. The RHS always gets the top-most + // terminator. + return VisitLogicalOperator(B_LHS, B, TrueBlock, FalseBlock); } - return EntryLHSBlock; + // Create the block evaluating the LHS. + // This contains the '&&' or '||' as the terminator. + CFGBlock *LHSBlock = createBlock(false); + LHSBlock->setTerminator(B); + + Block = LHSBlock; + CFGBlock *EntryLHSBlock = addStmt(LHS); + + if (badCFG) + return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + + // See if this is a known constant. + TryResult KnownVal = tryEvaluateBool(LHS); + + // Now link the LHSBlock with RHSBlock. + if (B->getOpcode() == BO_LOr) { + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock); + } else { + assert(B->getOpcode() == BO_LAnd); + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); } + return std::make_pair(EntryLHSBlock, ExitBlock); +} + + +CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, + AddStmtChoice asc) { + // && or || + if (B->isLogicalOp()) + return VisitLogicalOperator(B); + if (B->getOpcode() == BO_Comma) { // , autoCreateBlock(); appendStmt(Block, B); @@ -1284,7 +1340,7 @@ static bool CanThrow(Expr *E, ASTContext &Ctx) { const FunctionType *FT = Ty->getAs<FunctionType>(); if (FT) { if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT)) - if (Proto->getExceptionSpecType() != EST_Uninstantiated && + if (!isUnresolvedExceptionSpec(Proto->getExceptionSpecType()) && Proto->isNothrow(Ctx)) return false; } @@ -1435,6 +1491,12 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, if (badCFG) return 0; + // If the condition is a logical '&&' or '||', build a more accurate CFG. + if (BinaryOperator *Cond = + dyn_cast<BinaryOperator>(C->getCond()->IgnoreParens())) + if (Cond->isLogicalOp()) + return VisitLogicalOperator(Cond, C, LHSBlock, RHSBlock).first; + // Create the block that will contain the condition. Block = createBlock(false); @@ -1471,11 +1533,10 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { CFGBlock *B = 0; - // FIXME: Add a reverse iterator for DeclStmt to avoid this extra copy. - typedef SmallVector<Decl*,10> BufTy; - BufTy Buf(DS->decl_begin(), DS->decl_end()); - - for (BufTy::reverse_iterator I = Buf.rbegin(), E = Buf.rend(); I != E; ++I) { + // Build an individual DeclStmt for each decl. + for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(), + E = DS->decl_rend(); + I != E; ++I) { // Get the alignment of the new DeclStmt, padding out to >=8 bytes. unsigned A = llvm::AlignOf<DeclStmt>::Alignment < 8 ? 8 : llvm::AlignOf<DeclStmt>::Alignment; @@ -1645,6 +1706,19 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { } } + // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by + // having these handle the actual control-flow jump. Note that + // if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)" + // we resort to the old control-flow behavior. This special handling + // removes infeasible paths from the control-flow graph by having the + // control-flow transfer of '&&' or '||' go directly into the then/else + // blocks directly. + if (!I->getConditionVariable()) + if (BinaryOperator *Cond = + dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens())) + if (Cond->isLogicalOp()) + return VisitLogicalOperator(Cond, I, ThenBlock, ElseBlock).first; + // Now create a new block containing the if statement. Block = createBlock(false); @@ -1795,75 +1869,26 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); - // Because of short-circuit evaluation, the condition of the loop can span - // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that - // evaluate the condition. - CFGBlock *ExitConditionBlock = createBlock(false); - CFGBlock *EntryConditionBlock = ExitConditionBlock; - - // Set the terminator for the "exit" condition block. - ExitConditionBlock->setTerminator(F); - - // Now add the actual condition to the condition block. Because the condition - // itself may contain control-flow, new blocks may be created. - if (Stmt *C = F->getCond()) { - Block = ExitConditionBlock; - EntryConditionBlock = addStmt(C); - if (badCFG) - return 0; - assert(Block == EntryConditionBlock || - (Block == 0 && EntryConditionBlock == Succ)); - - // If this block contains a condition variable, add both the condition - // variable and initializer to the CFG. - if (VarDecl *VD = F->getConditionVariable()) { - if (Expr *Init = VD->getInit()) { - autoCreateBlock(); - appendStmt(Block, F->getConditionVariableDeclStmt()); - EntryConditionBlock = addStmt(Init); - assert(Block == EntryConditionBlock); - } - } - - if (Block) { - if (badCFG) - return 0; - } - } - - // The condition block is the implicit successor for the loop body as well as - // any code above the loop. - Succ = EntryConditionBlock; - - // See if this is a known constant. - TryResult KnownVal(true); - - if (F->getCond()) - KnownVal = tryEvaluateBool(F->getCond()); + CFGBlock *BodyBlock = 0, *TransitionBlock = 0; // Now create the loop body. { assert(F->getBody()); - // Save the current values for Block, Succ, and continue targets. - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); + // Save the current values for Block, Succ, continue and break targets. + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); - // Create a new block to contain the (bottom) of the loop body. - Block = NULL; - - // Loop body should end with destructor of Condition variable (if any). - addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F); + // Create an empty block to represent the transition block for looping back + // to the head of the loop. If we have increment code, it will + // go in this block as well. + Block = Succ = TransitionBlock = createBlock(false); + TransitionBlock->setLoopTarget(F); if (Stmt *I = F->getInc()) { // Generate increment code in its own basic block. This is the target of // continue statements. Succ = addStmt(I); - } else { - // No increment code. Create a special, empty, block that is used as the - // target block for "looping back" to the start of the loop. - assert(Succ == EntryConditionBlock); - Succ = Block ? Block : createBlock(); } // Finish up the increment (or empty) block if it hasn't been already. @@ -1874,11 +1899,13 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { Block = 0; } - ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); + // The starting block for the loop increment is the block that should + // represent the 'loop target' for looping back to the start of the loop. + ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); + ContinueJumpTarget.block->setLoopTarget(F); - // The starting block for the loop increment is the block that should - // represent the 'loop target' for looping back to the start of the loop. - ContinueJumpTarget.block->setLoopTarget(F); + // Loop body should end with destructor of Condition variable (if any). + addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F); // If body is not a compound statement create implicit scope // and add destructors. @@ -1887,20 +1914,79 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // Now populate the body block, and in the process create new blocks as we // walk the body of the loop. - CFGBlock *BodyBlock = addStmt(F->getBody()); + BodyBlock = addStmt(F->getBody()); - if (!BodyBlock) - BodyBlock = ContinueJumpTarget.block;//can happen for "for (...;...;...);" + if (!BodyBlock) { + // In the case of "for (...;...;...);" we can have a null BodyBlock. + // Use the continue jump target as the proxy for the body. + BodyBlock = ContinueJumpTarget.block; + } else if (badCFG) return 0; + } + + // Because of short-circuit evaluation, the condition of the loop can span + // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that + // evaluate the condition. + CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + + do { + Expr *C = F->getCond(); + + // Specially handle logical operators, which have a slightly + // more optimal CFG representation. + if (BinaryOperator *Cond = + dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : 0)) + if (Cond->isLogicalOp()) { + llvm::tie(EntryConditionBlock, ExitConditionBlock) = + VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor); + break; + } - // This new body block is a successor to our "exit" condition block. + // The default case when not handling logical operators. + EntryConditionBlock = ExitConditionBlock = createBlock(false); + ExitConditionBlock->setTerminator(F); + + // See if this is a known constant. + TryResult KnownVal(true); + + if (C) { + // Now add the actual condition to the condition block. + // Because the condition itself may contain control-flow, new blocks may + // be created. Thus we update "Succ" after adding the condition. + Block = ExitConditionBlock; + EntryConditionBlock = addStmt(C); + + // If this block contains a condition variable, add both the condition + // variable and initializer to the CFG. + if (VarDecl *VD = F->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, F->getConditionVariableDeclStmt()); + EntryConditionBlock = addStmt(Init); + assert(Block == EntryConditionBlock); + } + } + + if (Block && badCFG) + return 0; + + KnownVal = tryEvaluateBool(C); + } + + // Add the loop body entry as a successor to the condition. addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); - } + // Link up the condition block with the code that follows the loop. (the + // false branch). + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); - // Link up the condition block with the code that follows the loop. (the - // false branch). - addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + } while (false); + + // Link up the loop-back block to the entry condition block. + addSuccessor(TransitionBlock, EntryConditionBlock); + + // The condition block is the implicit successor for any code above the loop. + Succ = EntryConditionBlock; // If the loop contains initialization, create a new block for those // statements. This block can also contain statements that precede the loop. @@ -2108,74 +2194,30 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { return 0; LoopSuccessor = Block; Block = 0; - } else + } else { LoopSuccessor = Succ; - - // Because of short-circuit evaluation, the condition of the loop can span - // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that - // evaluate the condition. - CFGBlock *ExitConditionBlock = createBlock(false); - CFGBlock *EntryConditionBlock = ExitConditionBlock; - - // Set the terminator for the "exit" condition block. - ExitConditionBlock->setTerminator(W); - - // Now add the actual condition to the condition block. Because the condition - // itself may contain control-flow, new blocks may be created. Thus we update - // "Succ" after adding the condition. - if (Stmt *C = W->getCond()) { - Block = ExitConditionBlock; - EntryConditionBlock = addStmt(C); - // The condition might finish the current 'Block'. - Block = EntryConditionBlock; - - // If this block contains a condition variable, add both the condition - // variable and initializer to the CFG. - if (VarDecl *VD = W->getConditionVariable()) { - if (Expr *Init = VD->getInit()) { - autoCreateBlock(); - appendStmt(Block, W->getConditionVariableDeclStmt()); - EntryConditionBlock = addStmt(Init); - assert(Block == EntryConditionBlock); - } - } - - if (Block) { - if (badCFG) - return 0; - } } - // The condition block is the implicit successor for the loop body as well as - // any code above the loop. - Succ = EntryConditionBlock; - - // See if this is a known constant. - const TryResult& KnownVal = tryEvaluateBool(W->getCond()); + CFGBlock *BodyBlock = 0, *TransitionBlock = 0; // Process the loop body. { assert(W->getBody()); - // Save the current values for Block, Succ, and continue and break targets + // Save the current values for Block, Succ, continue and break targets. SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), - save_break(BreakJumpTarget); + save_break(BreakJumpTarget); // Create an empty block to represent the transition block for looping back // to the head of the loop. - Block = 0; - assert(Succ == EntryConditionBlock); - Succ = createBlock(); - Succ->setLoopTarget(W); + Succ = TransitionBlock = createBlock(false); + TransitionBlock->setLoopTarget(W); ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos); // All breaks should go to the code following the loop. BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); - // NULL out Block to force lazy instantiation of blocks for the body. - Block = NULL; - // Loop body should end with destructor of Condition variable (if any). addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); @@ -2185,22 +2227,69 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { addLocalScopeAndDtors(W->getBody()); // Create the body. The returned block is the entry to the loop body. - CFGBlock *BodyBlock = addStmt(W->getBody()); + BodyBlock = addStmt(W->getBody()); if (!BodyBlock) BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" - else if (Block) { - if (badCFG) - return 0; + else if (Block && badCFG) + return 0; + } + + // Because of short-circuit evaluation, the condition of the loop can span + // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that + // evaluate the condition. + CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + + do { + Expr *C = W->getCond(); + + // Specially handle logical operators, which have a slightly + // more optimal CFG representation. + if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C->IgnoreParens())) + if (Cond->isLogicalOp()) { + llvm::tie(EntryConditionBlock, ExitConditionBlock) = + VisitLogicalOperator(Cond, W, BodyBlock, + LoopSuccessor); + break; + } + + // The default case when not handling logical operators. + EntryConditionBlock = ExitConditionBlock = createBlock(false); + ExitConditionBlock->setTerminator(W); + + // Now add the actual condition to the condition block. + // Because the condition itself may contain control-flow, new blocks may + // be created. Thus we update "Succ" after adding the condition. + Block = ExitConditionBlock; + Block = EntryConditionBlock = addStmt(C); + + // If this block contains a condition variable, add both the condition + // variable and initializer to the CFG. + if (VarDecl *VD = W->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, W->getConditionVariableDeclStmt()); + EntryConditionBlock = addStmt(Init); + assert(Block == EntryConditionBlock); + } } + if (Block && badCFG) + return 0; + + // See if this is a known constant. + const TryResult& KnownVal = tryEvaluateBool(C); + // Add the loop body entry as a successor to the condition. addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); - } + // Link up the condition block with the code that follows the loop. (the + // false branch). + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); - // Link up the condition block with the code that follows the loop. (the - // false branch). - addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + } while(false); + + // Link up the loop-back block to the entry condition block. + addSuccessor(TransitionBlock, EntryConditionBlock); // There can be no more statements in the condition block since we loop back // to this block. NULL out Block to force lazy creation of another block. @@ -3203,8 +3292,8 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { } bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const { - if (const CXXDestructorDecl *cdecl = getDestructorDecl(astContext)) { - QualType ty = cdecl->getType(); + if (const CXXDestructorDecl *decl = getDestructorDecl(astContext)) { + QualType ty = decl->getType(); return cast<FunctionType>(ty)->getNoReturnAttr(); } return false; @@ -3631,8 +3720,7 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper, const Type* T = VD->getType().getTypePtr(); if (const ReferenceType* RT = T->getAs<ReferenceType>()) T = RT->getPointeeType().getTypePtr(); - else if (const Type *ET = T->getArrayElementTypeNoTypeQual()) - T = ET; + T = T->getBaseElementTypeUnsafe(); OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; @@ -3644,11 +3732,7 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper, } else if (const CFGMemberDtor *ME = E.getAs<CFGMemberDtor>()) { const FieldDecl *FD = ME->getFieldDecl(); - - const Type *T = FD->getType().getTypePtr(); - if (const Type *ET = T->getArrayElementTypeNoTypeQual()) - T = ET; - + const Type *T = FD->getType()->getBaseElementTypeUnsafe(); OS << "this->" << FD->getName(); OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()"; OS << " (Member object destructor)\n"; diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index ca2392b91588..d57e4813ca02 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -1,5 +1,3 @@ -set(LLVM_USED_LIBS clangBasic clangAST) - add_clang_library(clangAnalysis AnalysisDeclContext.cpp CallGraph.cpp @@ -20,5 +18,17 @@ add_clang_library(clangAnalysis UninitializedValues.cpp ) -add_dependencies(clangAnalysis ClangAttrClasses ClangAttrList - ClangDiagnosticAnalysis ClangDeclNodes ClangStmtNodes) +add_dependencies(clangAnalysis + ClangAttrClasses + ClangAttrList + ClangCommentNodes + ClangDiagnosticCommon + ClangDeclNodes + ClangDiagnosticAnalysis + ClangStmtNodes + ) + +target_link_libraries(clangAnalysis + clangBasic + clangAST + ) diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index 96a16c3afe05..6b759567889e 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -25,12 +25,11 @@ namespace { /// given function body. class CGBuilder : public StmtVisitor<CGBuilder> { CallGraph *G; - const Decl *FD; CallGraphNode *CallerNode; public: - CGBuilder(CallGraph *g, const Decl *D, CallGraphNode *N) - : G(g), FD(D), CallerNode(N) {} + CGBuilder(CallGraph *g, CallGraphNode *N) + : G(g), CallerNode(N) {} void VisitStmt(Stmt *S) { VisitChildren(S); } @@ -99,7 +98,7 @@ void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) { Root->addCallee(Node, this); // Process all the calls by this function as well. - CGBuilder builder(this, D, Node); + CGBuilder builder(this, Node); if (Stmt *Body = D->getBody()) builder.Visit(Body); } diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp index 7e9e38fd1eae..ce973af6d431 100644 --- a/lib/Analysis/CocoaConventions.cpp +++ b/lib/Analysis/CocoaConventions.cpp @@ -17,6 +17,8 @@ #include "clang/AST/DeclObjC.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" +#include <cctype> + using namespace clang; using namespace ento; diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp index ba45865af875..e7ea48688dae 100644 --- a/lib/Analysis/FormatString.cpp +++ b/lib/Analysis/FormatString.cpp @@ -15,7 +15,7 @@ #include "FormatStringParsing.h" #include "clang/Basic/LangOptions.h" -using clang::analyze_format_string::ArgTypeResult; +using clang::analyze_format_string::ArgType; using clang::analyze_format_string::FormatStringHandler; using clang::analyze_format_string::FormatSpecifier; using clang::analyze_format_string::LengthModifier; @@ -229,18 +229,34 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, } //===----------------------------------------------------------------------===// -// Methods on ArgTypeResult. +// Methods on ArgType. //===----------------------------------------------------------------------===// -bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { +bool ArgType::matchesType(ASTContext &C, QualType argTy) const { + if (Ptr) { + // It has to be a pointer. + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + // We cannot write through a const qualified pointer. + if (PT->getPointeeType().isConstQualified()) + return false; + + argTy = PT->getPointeeType(); + } + switch (K) { case InvalidTy: - llvm_unreachable("ArgTypeResult must be valid"); + llvm_unreachable("ArgType must be valid"); case UnknownTy: return true; case AnyCharTy: { + if (const EnumType *ETy = argTy->getAs<EnumType>()) + argTy = ETy->getDecl()->getIntegerType(); + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) switch (BT->getKind()) { default: @@ -255,7 +271,10 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { } case SpecificTy: { + if (const EnumType *ETy = argTy->getAs<EnumType>()) + argTy = ETy->getDecl()->getIntegerType(); argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + if (T == argTy) return true; // Check for "compatible types". @@ -265,10 +284,9 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { break; case BuiltinType::Char_S: case BuiltinType::SChar: - return T == C.UnsignedCharTy; case BuiltinType::Char_U: case BuiltinType::UChar: - return T == C.SignedCharTy; + return T == C.UnsignedCharTy || T == C.SignedCharTy; case BuiltinType::Short: return T == C.UnsignedShortTy; case BuiltinType::UShort: @@ -319,20 +337,21 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { } case WIntTy: { - // Instead of doing a lookup for the definition of 'wint_t' (which - // is defined by the system headers) instead see if wchar_t and - // the argument type promote to the same type. - QualType PromoWChar = - C.getWCharType()->isPromotableIntegerType() - ? C.getPromotedIntegerType(C.getWCharType()) : C.getWCharType(); + QualType PromoArg = argTy->isPromotableIntegerType() ? C.getPromotedIntegerType(argTy) : argTy; - PromoWChar = C.getCanonicalType(PromoWChar).getUnqualifiedType(); + QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType(); PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType(); - return PromoWChar == PromoArg; + // If the promoted argument is the corresponding signed type of the + // wint_t type, then it should match. + if (PromoArg->hasSignedIntegerRepresentation() && + C.getCorrespondingUnsignedType(PromoArg) == WInt) + return true; + + return WInt == PromoArg; } case CPointerTy: @@ -358,40 +377,63 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { } } - llvm_unreachable("Invalid ArgTypeResult Kind!"); + llvm_unreachable("Invalid ArgType Kind!"); } -QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { +QualType ArgType::getRepresentativeType(ASTContext &C) const { + QualType Res; switch (K) { case InvalidTy: - llvm_unreachable("No representative type for Invalid ArgTypeResult"); + llvm_unreachable("No representative type for Invalid ArgType"); case UnknownTy: - return QualType(); + llvm_unreachable("No representative type for Unknown ArgType"); case AnyCharTy: - return C.CharTy; + Res = C.CharTy; + break; case SpecificTy: - return T; + Res = T; + break; case CStrTy: - return C.getPointerType(C.CharTy); + Res = C.getPointerType(C.CharTy); + break; case WCStrTy: - return C.getPointerType(C.getWCharType()); + Res = C.getPointerType(C.getWCharType()); + break; case ObjCPointerTy: - return C.ObjCBuiltinIdTy; + Res = C.ObjCBuiltinIdTy; + break; case CPointerTy: - return C.VoidPtrTy; + Res = C.VoidPtrTy; + break; case WIntTy: { - QualType WC = C.getWCharType(); - return WC->isPromotableIntegerType() ? C.getPromotedIntegerType(WC) : WC; + Res = C.getWIntType(); + break; } } - llvm_unreachable("Invalid ArgTypeResult Kind!"); + if (Ptr) + Res = C.getPointerType(Res); + return Res; } -std::string ArgTypeResult::getRepresentativeTypeName(ASTContext &C) const { +std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { std::string S = getRepresentativeType(C).getAsString(); - if (Name && S != Name) - return std::string("'") + Name + "' (aka '" + S + "')"; + + std::string Alias; + if (Name) { + // Use a specific name for this type, e.g. "size_t". + Alias = Name; + if (Ptr) { + // If ArgType is actually a pointer to T, append an asterisk. + Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *"; + } + // If Alias is the same as the underlying type, e.g. wchar_t, then drop it. + if (S == Alias) + Alias.clear(); + } + + if (!Alias.empty()) + return std::string("'") + Alias + "' (aka '" + S + "')"; return std::string("'") + S + "'"; } @@ -400,7 +442,7 @@ std::string ArgTypeResult::getRepresentativeTypeName(ASTContext &C) const { // Methods on OptionalAmount. //===----------------------------------------------------------------------===// -ArgTypeResult +ArgType analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const { return Ctx.IntTy; } @@ -676,3 +718,37 @@ bool FormatSpecifier::hasStandardLengthConversionCombination() const { } return true; } + +bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, + LengthModifier &LM) { + assert(isa<TypedefType>(QT) && "Expected a TypedefType"); + const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl(); + + for (;;) { + const IdentifierInfo *Identifier = Typedef->getIdentifier(); + if (Identifier->getName() == "size_t") { + LM.setKind(LengthModifier::AsSizeT); + return true; + } else if (Identifier->getName() == "ssize_t") { + // Not C99, but common in Unix. + LM.setKind(LengthModifier::AsSizeT); + return true; + } else if (Identifier->getName() == "intmax_t") { + LM.setKind(LengthModifier::AsIntMax); + return true; + } else if (Identifier->getName() == "uintmax_t") { + LM.setKind(LengthModifier::AsIntMax); + return true; + } else if (Identifier->getName() == "ptrdiff_t") { + LM.setKind(LengthModifier::AsPtrDiff); + return true; + } + + QualType T = Typedef->getUnderlyingType(); + if (!isa<TypedefType>(T)) + break; + + Typedef = cast<TypedefType>(T)->getDecl(); + } + return false; +} diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index ff6607d51aa9..38f8199bffce 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -284,6 +284,14 @@ void TransferFunctions::Visit(Stmt *S) { } break; } + case Stmt::ObjCMessageExprClass: { + // In calls to super, include the implicit "self" pointer as being live. + ObjCMessageExpr *CE = cast<ObjCMessageExpr>(S); + if (CE->getReceiverKind() == ObjCMessageExpr::SuperInstance) + val.liveDecls = LV.DSetFact.add(val.liveDecls, + LV.analysisContext.getSelfDecl()); + break; + } case Stmt::DeclStmtClass: { const DeclStmt *DS = cast<DeclStmt>(S); if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) { @@ -455,6 +463,12 @@ LiveVariablesImpl::runOnBlock(const CFGBlock *block, for (CFGBlock::const_reverse_iterator it = block->rbegin(), ei = block->rend(); it != ei; ++it) { const CFGElement &elem = *it; + + if (const CFGAutomaticObjDtor *Dtor = dyn_cast<CFGAutomaticObjDtor>(&elem)){ + val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl()); + continue; + } + if (!isa<CFGStmt>(elem)) continue; @@ -486,6 +500,11 @@ LiveVariables::computeLiveness(AnalysisDeclContext &AC, if (!cfg) return 0; + // The analysis currently has scalability issues for very large CFGs. + // Bail out if it looks too large. + if (cfg->getNumBlockIDs() > 300000) + return 0; + LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign); // Construct the dataflow worklist. Enqueue the exit block as the diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp index e1049b3c685b..9e4c0fec4c04 100644 --- a/lib/Analysis/PrintfFormatString.cpp +++ b/lib/Analysis/PrintfFormatString.cpp @@ -15,7 +15,7 @@ #include "clang/Analysis/Analyses/FormatString.h" #include "FormatStringParsing.h" -using clang::analyze_format_string::ArgTypeResult; +using clang::analyze_format_string::ArgType; using clang::analyze_format_string::FormatStringHandler; using clang::analyze_format_string::LengthModifier; using clang::analyze_format_string::OptionalAmount; @@ -241,20 +241,20 @@ bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, // Methods on PrintfSpecifier. //===----------------------------------------------------------------------===// -ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx, - bool IsObjCLiteral) const { +ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, + bool IsObjCLiteral) const { const PrintfConversionSpecifier &CS = getConversionSpecifier(); if (!CS.consumesDataArgument()) - return ArgTypeResult::Invalid(); + return ArgType::Invalid(); if (CS.getKind() == ConversionSpecifier::cArg) switch (LM.getKind()) { case LengthModifier::None: return Ctx.IntTy; case LengthModifier::AsLong: - return ArgTypeResult(ArgTypeResult::WIntTy, "wint_t"); + return ArgType(ArgType::WIntTy, "wint_t"); default: - return ArgTypeResult::Invalid(); + return ArgType::Invalid(); } if (CS.isIntArg()) @@ -263,22 +263,22 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx, // GNU extension. return Ctx.LongLongTy; case LengthModifier::None: return Ctx.IntTy; - case LengthModifier::AsChar: return ArgTypeResult::AnyCharTy; + case LengthModifier::AsChar: return ArgType::AnyCharTy; case LengthModifier::AsShort: return Ctx.ShortTy; case LengthModifier::AsLong: return Ctx.LongTy; case LengthModifier::AsLongLong: case LengthModifier::AsQuad: return Ctx.LongLongTy; case LengthModifier::AsIntMax: - return ArgTypeResult(Ctx.getIntMaxType(), "intmax_t"); + return ArgType(Ctx.getIntMaxType(), "intmax_t"); case LengthModifier::AsSizeT: // FIXME: How to get the corresponding signed version of size_t? - return ArgTypeResult(); + return ArgType(); case LengthModifier::AsPtrDiff: - return ArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t"); + return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: - return ArgTypeResult::Invalid(); + return ArgType::Invalid(); } if (CS.isUIntArg()) @@ -294,16 +294,16 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx, case LengthModifier::AsQuad: return Ctx.UnsignedLongLongTy; case LengthModifier::AsIntMax: - return ArgTypeResult(Ctx.getUIntMaxType(), "uintmax_t"); + return ArgType(Ctx.getUIntMaxType(), "uintmax_t"); case LengthModifier::AsSizeT: - return ArgTypeResult(Ctx.getSizeType(), "size_t"); + return ArgType(Ctx.getSizeType(), "size_t"); case LengthModifier::AsPtrDiff: // FIXME: How to get the corresponding unsigned // version of ptrdiff_t? - return ArgTypeResult(); + return ArgType(); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: - return ArgTypeResult::Invalid(); + return ArgType::Invalid(); } if (CS.isDoubleArg()) { @@ -312,37 +312,90 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx, return Ctx.DoubleTy; } + if (CS.getKind() == ConversionSpecifier::nArg) { + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.SignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); + case LengthModifier::AsSizeT: + return ArgType(); // FIXME: ssize_t + case LengthModifier::AsPtrDiff: + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); + case LengthModifier::AsLongDouble: + return ArgType(); // FIXME: Is this a known extension? + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); + } + } + switch (CS.getKind()) { case ConversionSpecifier::sArg: if (LM.getKind() == LengthModifier::AsWideChar) { if (IsObjCLiteral) return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()); - return ArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t *"); + return ArgType(ArgType::WCStrTy, "wchar_t *"); } - return ArgTypeResult::CStrTy; + return ArgType::CStrTy; case ConversionSpecifier::SArg: if (IsObjCLiteral) return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()); - return ArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t *"); + return ArgType(ArgType::WCStrTy, "wchar_t *"); case ConversionSpecifier::CArg: if (IsObjCLiteral) return Ctx.UnsignedShortTy; - return ArgTypeResult(Ctx.WCharTy, "wchar_t"); + return ArgType(Ctx.WCharTy, "wchar_t"); case ConversionSpecifier::pArg: - return ArgTypeResult::CPointerTy; + return ArgType::CPointerTy; case ConversionSpecifier::ObjCObjArg: - return ArgTypeResult::ObjCPointerTy; + return ArgType::ObjCPointerTy; default: break; } // FIXME: Handle other cases. - return ArgTypeResult(); + return ArgType(); } bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, ASTContext &Ctx, bool IsObjCLiteral) { - // Handle strings first (char *, wchar_t *) + // %n is different from other conversion specifiers; don't try to fix it. + if (CS.getKind() == ConversionSpecifier::nArg) + return false; + + // Handle Objective-C objects first. Note that while the '%@' specifier will + // not warn for structure pointer or void pointer arguments (because that's + // how CoreFoundation objects are implemented), we only show a fixit for '%@' + // if we know it's an object (block, id, class, or __attribute__((NSObject))). + if (QT->isObjCRetainableType()) { + if (!IsObjCLiteral) + return false; + + CS.setKind(ConversionSpecifier::ObjCObjArg); + + // Disable irrelevant flags + HasThousandsGrouping = false; + HasPlusPrefix = false; + HasSpacePrefix = false; + HasAlternativeForm = false; + HasLeadingZeroes = false; + Precision.setHowSpecified(OptionalAmount::NotSpecified); + LM.setKind(LengthModifier::None); + + return true; + } + + // Handle strings next (char *, wchar_t *) if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) { CS.setKind(ConversionSpecifier::sArg); @@ -359,6 +412,10 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, return true; } + // If it's an enum, get its underlying type. + if (const EnumType *ETy = QT->getAs<EnumType>()) + QT = ETy->getDecl()->getIntegerType(); + // We can only work with builtin types. const BuiltinType *BT = QT->getAs<BuiltinType>(); if (!BT) @@ -421,24 +478,11 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, } // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) { - const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier(); - if (Identifier->getName() == "size_t") { - LM.setKind(LengthModifier::AsSizeT); - } else if (Identifier->getName() == "ssize_t") { - // Not C99, but common in Unix. - LM.setKind(LengthModifier::AsSizeT); - } else if (Identifier->getName() == "intmax_t") { - LM.setKind(LengthModifier::AsIntMax); - } else if (Identifier->getName() == "uintmax_t") { - LM.setKind(LengthModifier::AsIntMax); - } else if (Identifier->getName() == "ptrdiff_t") { - LM.setKind(LengthModifier::AsPtrDiff); - } - } + if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) + namedTypeToLengthModifier(QT, LM); // If fixing the length modifier was enough, we are done. - const analyze_printf::ArgTypeResult &ATR = getArgType(Ctx, IsObjCLiteral); + const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral); if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT)) return true; diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp index 3f711b447ae9..7d67e8a91c8a 100644 --- a/lib/Analysis/ProgramPoint.cpp +++ b/lib/Analysis/ProgramPoint.cpp @@ -36,8 +36,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, return PreStore(S, LC, tag); case ProgramPoint::PostLValueKind: return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + return PostStmtPurgeDeadSymbols(S, LC, tag); + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + return PreStmtPurgeDeadSymbols(S, LC, tag); } } diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp index c8b491a21682..5d659ce5851f 100644 --- a/lib/Analysis/PseudoConstantAnalysis.cpp +++ b/lib/Analysis/PseudoConstantAnalysis.cpp @@ -17,6 +17,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" +#include "llvm/ADT/SmallPtrSet.h" #include <deque> using namespace clang; diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp index 6bc4adb4f3c6..2942400621d1 100644 --- a/lib/Analysis/ScanfFormatString.cpp +++ b/lib/Analysis/ScanfFormatString.cpp @@ -15,12 +15,11 @@ #include "clang/Analysis/Analyses/FormatString.h" #include "FormatStringParsing.h" -using clang::analyze_format_string::ArgTypeResult; +using clang::analyze_format_string::ArgType; using clang::analyze_format_string::FormatStringHandler; using clang::analyze_format_string::LengthModifier; using clang::analyze_format_string::OptionalAmount; using clang::analyze_format_string::ConversionSpecifier; -using clang::analyze_scanf::ScanfArgTypeResult; using clang::analyze_scanf::ScanfConversionSpecifier; using clang::analyze_scanf::ScanfSpecifier; using clang::UpdateOnReturn; @@ -194,37 +193,42 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, return ScanfSpecifierResult(Start, FS); } -ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const { +ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { const ScanfConversionSpecifier &CS = getConversionSpecifier(); if (!CS.consumesDataArgument()) - return ScanfArgTypeResult::Invalid(); + return ArgType::Invalid(); switch(CS.getKind()) { // Signed int. case ConversionSpecifier::dArg: case ConversionSpecifier::iArg: switch (LM.getKind()) { - case LengthModifier::None: return ArgTypeResult(Ctx.IntTy); + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); case LengthModifier::AsChar: - return ArgTypeResult(ArgTypeResult::AnyCharTy); - case LengthModifier::AsShort: return ArgTypeResult(Ctx.ShortTy); - case LengthModifier::AsLong: return ArgTypeResult(Ctx.LongTy); + return ArgType::PtrTo(ArgType::AnyCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); case LengthModifier::AsLongLong: case LengthModifier::AsQuad: - return ArgTypeResult(Ctx.LongLongTy); + return ArgType::PtrTo(Ctx.LongLongTy); case LengthModifier::AsIntMax: - return ScanfArgTypeResult(Ctx.getIntMaxType(), "intmax_t *"); + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); case LengthModifier::AsSizeT: // FIXME: ssize_t. - return ScanfArgTypeResult(); + return ArgType(); case LengthModifier::AsPtrDiff: - return ScanfArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t *"); + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); case LengthModifier::AsLongDouble: // GNU extension. - return ArgTypeResult(Ctx.LongLongTy); - case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid(); - case LengthModifier::AsMAllocate: return ScanfArgTypeResult::Invalid(); + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsAllocate: + return ArgType::Invalid(); + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); } // Unsigned int. @@ -233,25 +237,31 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const { case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: switch (LM.getKind()) { - case LengthModifier::None: return ArgTypeResult(Ctx.UnsignedIntTy); - case LengthModifier::AsChar: return ArgTypeResult(Ctx.UnsignedCharTy); - case LengthModifier::AsShort: return ArgTypeResult(Ctx.UnsignedShortTy); - case LengthModifier::AsLong: return ArgTypeResult(Ctx.UnsignedLongTy); + case LengthModifier::None: + return ArgType::PtrTo(Ctx.UnsignedIntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.UnsignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.UnsignedShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.UnsignedLongTy); case LengthModifier::AsLongLong: case LengthModifier::AsQuad: - return ArgTypeResult(Ctx.UnsignedLongLongTy); + return ArgType::PtrTo(Ctx.UnsignedLongLongTy); case LengthModifier::AsIntMax: - return ScanfArgTypeResult(Ctx.getUIntMaxType(), "uintmax_t *"); + return ArgType::PtrTo(ArgType(Ctx.getUIntMaxType(), "uintmax_t")); case LengthModifier::AsSizeT: - return ScanfArgTypeResult(Ctx.getSizeType(), "size_t *"); + return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t")); case LengthModifier::AsPtrDiff: // FIXME: Unsigned version of ptrdiff_t? - return ScanfArgTypeResult(); + return ArgType(); case LengthModifier::AsLongDouble: // GNU extension. - return ArgTypeResult(Ctx.UnsignedLongLongTy); - case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid(); - case LengthModifier::AsMAllocate: return ScanfArgTypeResult::Invalid(); + return ArgType::PtrTo(Ctx.UnsignedLongLongTy); + case LengthModifier::AsAllocate: + return ArgType::Invalid(); + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); } // Float. @@ -264,12 +274,14 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const { case ConversionSpecifier::gArg: case ConversionSpecifier::GArg: switch (LM.getKind()) { - case LengthModifier::None: return ArgTypeResult(Ctx.FloatTy); - case LengthModifier::AsLong: return ArgTypeResult(Ctx.DoubleTy); + case LengthModifier::None: + return ArgType::PtrTo(Ctx.FloatTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.DoubleTy); case LengthModifier::AsLongDouble: - return ArgTypeResult(Ctx.LongDoubleTy); + return ArgType::PtrTo(Ctx.LongDoubleTy); default: - return ScanfArgTypeResult::Invalid(); + return ArgType::Invalid(); } // Char, string and scanlist. @@ -277,37 +289,65 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const { case ConversionSpecifier::sArg: case ConversionSpecifier::ScanListArg: switch (LM.getKind()) { - case LengthModifier::None: return ScanfArgTypeResult::CStrTy; + case LengthModifier::None: + return ArgType::PtrTo(ArgType::AnyCharTy); case LengthModifier::AsLong: - return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *"); + return ArgType::PtrTo(ArgType(Ctx.getWCharType(), "wchar_t")); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: - return ScanfArgTypeResult(ArgTypeResult::CStrTy); + return ArgType::PtrTo(ArgType::CStrTy); default: - return ScanfArgTypeResult::Invalid(); + return ArgType::Invalid(); } case ConversionSpecifier::CArg: case ConversionSpecifier::SArg: // FIXME: Mac OS X specific? switch (LM.getKind()) { case LengthModifier::None: - return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *"); + return ArgType::PtrTo(ArgType(Ctx.getWCharType(), "wchar_t")); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: - return ScanfArgTypeResult(ArgTypeResult::WCStrTy, "wchar_t **"); + return ArgType::PtrTo(ArgType(ArgType::WCStrTy, "wchar_t *")); default: - return ScanfArgTypeResult::Invalid(); + return ArgType::Invalid(); } // Pointer. case ConversionSpecifier::pArg: - return ScanfArgTypeResult(ArgTypeResult(ArgTypeResult::CPointerTy)); + return ArgType::PtrTo(ArgType::CPointerTy); + + // Write-back. + case ConversionSpecifier::nArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.SignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); + case LengthModifier::AsSizeT: + return ArgType(); // FIXME: ssize_t + case LengthModifier::AsPtrDiff: + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); + case LengthModifier::AsLongDouble: + return ArgType(); // FIXME: Is this a known extension? + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); + } default: break; } - return ScanfArgTypeResult(); + return ArgType(); } bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, @@ -315,7 +355,16 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, if (!QT->isPointerType()) return false; + // %n is different from other conversion specifiers; don't try to fix it. + if (CS.getKind() == ConversionSpecifier::nArg) + return false; + QualType PT = QT->getPointeeType(); + + // If it's an enum, get its underlying type. + if (const EnumType *ETy = QT->getAs<EnumType>()) + QT = ETy->getDecl()->getIntegerType(); + const BuiltinType *BT = PT->getAs<BuiltinType>(); if (!BT) return false; @@ -377,25 +426,12 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, } // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) { - const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier(); - if (Identifier->getName() == "size_t") { - LM.setKind(LengthModifier::AsSizeT); - } else if (Identifier->getName() == "ssize_t") { - // Not C99, but common in Unix. - LM.setKind(LengthModifier::AsSizeT); - } else if (Identifier->getName() == "intmax_t") { - LM.setKind(LengthModifier::AsIntMax); - } else if (Identifier->getName() == "uintmax_t") { - LM.setKind(LengthModifier::AsIntMax); - } else if (Identifier->getName() == "ptrdiff_t") { - LM.setKind(LengthModifier::AsPtrDiff); - } - } + if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) + namedTypeToLengthModifier(PT, LM); // If fixing the length modifier was enough, we are done. - const analyze_scanf::ScanfArgTypeResult &ATR = getArgType(Ctx); - if (hasValidLengthModifier() && ATR.isValid() && ATR.matchesType(Ctx, QT)) + const analyze_scanf::ArgType &AT = getArgType(Ctx); + if (hasValidLengthModifier() && AT.isValid() && AT.matchesType(Ctx, QT)) return true; // Figure out the conversion specifier. @@ -452,48 +488,3 @@ bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, assert(I == E && "Format string not exhausted"); return false; } - -bool ScanfArgTypeResult::matchesType(ASTContext& C, QualType argTy) const { - switch (K) { - case InvalidTy: - llvm_unreachable("ArgTypeResult must be valid"); - case UnknownTy: - return true; - case CStrTy: - return ArgTypeResult(ArgTypeResult::CStrTy).matchesType(C, argTy); - case WCStrTy: - return ArgTypeResult(ArgTypeResult::WCStrTy).matchesType(C, argTy); - case PtrToArgTypeResultTy: { - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return false; - return A.matchesType(C, PT->getPointeeType()); - } - } - - llvm_unreachable("Invalid ScanfArgTypeResult Kind!"); -} - -QualType ScanfArgTypeResult::getRepresentativeType(ASTContext &C) const { - switch (K) { - case InvalidTy: - llvm_unreachable("No representative type for Invalid ArgTypeResult"); - case UnknownTy: - return QualType(); - case CStrTy: - return C.getPointerType(C.CharTy); - case WCStrTy: - return C.getPointerType(C.getWCharType()); - case PtrToArgTypeResultTy: - return C.getPointerType(A.getRepresentativeType(C)); - } - - llvm_unreachable("Invalid ScanfArgTypeResult Kind!"); -} - -std::string ScanfArgTypeResult::getRepresentativeTypeName(ASTContext& C) const { - std::string S = getRepresentativeType(C).getAsString(); - if (!Name) - return std::string("'") + S + "'"; - return std::string("'") + Name + "' (aka '" + S + "')"; -} diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 2f7e794c2b38..5954682579d8 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -26,6 +26,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/OperatorKinds.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" @@ -45,8 +46,15 @@ ThreadSafetyHandler::~ThreadSafetyHandler() {} namespace { -/// \brief A MutexID object uniquely identifies a particular mutex, and -/// is built from an Expr* (i.e. calling a lock function). +/// SExpr implements a simple expression language that is used to store, +/// compare, and pretty-print C++ expressions. Unlike a clang Expr, a SExpr +/// does not capture surface syntax, and it does not distinguish between +/// C++ concepts, like pointers and references, that have no real semantic +/// differences. This simplicity allows SExprs to be meaningfully compared, +/// e.g. +/// (x) = x +/// (*this).foo = this->foo +/// *&a = a /// /// Thread-safety analysis works by comparing lock expressions. Within the /// body of a function, an expression such as "x->foo->bar.mu" will resolve to @@ -59,41 +67,194 @@ namespace { /// /// The current implementation assumes, but does not verify, that multiple uses /// of the same lock expression satisfies these criteria. -/// -/// Clang introduces an additional wrinkle, which is that it is difficult to -/// derive canonical expressions, or compare expressions directly for equality. -/// Thus, we identify a mutex not by an Expr, but by the list of named -/// declarations that are referenced by the Expr. In other words, -/// x->foo->bar.mu will be a four element vector with the Decls for -/// mu, bar, and foo, and x. The vector will uniquely identify the expression -/// for all practical purposes. Null is used to denote 'this'. -/// -/// Note we will need to perform substitution on "this" and function parameter -/// names when constructing a lock expression. -/// -/// For example: -/// class C { Mutex Mu; void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); }; -/// void myFunc(C *X) { ... X->lock() ... } -/// The original expression for the mutex acquired by myFunc is "this->Mu", but -/// "X" is substituted for "this" so we get X->Mu(); -/// -/// For another example: -/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... } -/// MyList *MyL; -/// foo(MyL); // requires lock MyL->Mu to be held -class MutexID { - SmallVector<NamedDecl*, 2> DeclSeq; - - /// Build a Decl sequence representing the lock from the given expression. +class SExpr { +private: + enum ExprOp { + EOP_Nop, //< No-op + EOP_Wildcard, //< Matches anything. + EOP_This, //< This keyword. + EOP_NVar, //< Named variable. + EOP_LVar, //< Local variable. + EOP_Dot, //< Field access + EOP_Call, //< Function call + EOP_MCall, //< Method call + EOP_Index, //< Array index + EOP_Unary, //< Unary operation + EOP_Binary, //< Binary operation + EOP_Unknown //< Catchall for everything else + }; + + + class SExprNode { + private: + unsigned char Op; //< Opcode of the root node + unsigned char Flags; //< Additional opcode-specific data + unsigned short Sz; //< Number of child nodes + const void* Data; //< Additional opcode-specific data + + public: + SExprNode(ExprOp O, unsigned F, const void* D) + : Op(static_cast<unsigned char>(O)), + Flags(static_cast<unsigned char>(F)), Sz(1), Data(D) + { } + + unsigned size() const { return Sz; } + void setSize(unsigned S) { Sz = S; } + + ExprOp kind() const { return static_cast<ExprOp>(Op); } + + const NamedDecl* getNamedDecl() const { + assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot); + return reinterpret_cast<const NamedDecl*>(Data); + } + + const NamedDecl* getFunctionDecl() const { + assert(Op == EOP_Call || Op == EOP_MCall); + return reinterpret_cast<const NamedDecl*>(Data); + } + + bool isArrow() const { return Op == EOP_Dot && Flags == 1; } + void setArrow(bool A) { Flags = A ? 1 : 0; } + + unsigned arity() const { + switch (Op) { + case EOP_Nop: return 0; + case EOP_Wildcard: return 0; + case EOP_NVar: return 0; + case EOP_LVar: return 0; + case EOP_This: return 0; + case EOP_Dot: return 1; + case EOP_Call: return Flags+1; // First arg is function. + case EOP_MCall: return Flags+1; // First arg is implicit obj. + case EOP_Index: return 2; + case EOP_Unary: return 1; + case EOP_Binary: return 2; + case EOP_Unknown: return Flags; + } + return 0; + } + + bool operator==(const SExprNode& Other) const { + // Ignore flags and size -- they don't matter. + return (Op == Other.Op && + Data == Other.Data); + } + + bool operator!=(const SExprNode& Other) const { + return !(*this == Other); + } + + bool matches(const SExprNode& Other) const { + return (*this == Other) || + (Op == EOP_Wildcard) || + (Other.Op == EOP_Wildcard); + } + }; + + + /// \brief Encapsulates the lexical context of a function call. The lexical + /// context includes the arguments to the call, including the implicit object + /// argument. When an attribute containing a mutex expression is attached to + /// a method, the expression may refer to formal parameters of the method. + /// Actual arguments must be substituted for formal parameters to derive + /// the appropriate mutex expression in the lexical context where the function + /// is called. PrevCtx holds the context in which the arguments themselves + /// should be evaluated; multiple calling contexts can be chained together + /// by the lock_returned attribute. + struct CallingContext { + const NamedDecl* AttrDecl; // The decl to which the attribute is attached. + Expr* SelfArg; // Implicit object argument -- e.g. 'this' + bool SelfArrow; // is Self referred to with -> or .? + unsigned NumArgs; // Number of funArgs + Expr** FunArgs; // Function arguments + CallingContext* PrevCtx; // The previous context; or 0 if none. + + CallingContext(const NamedDecl *D = 0, Expr *S = 0, + unsigned N = 0, Expr **A = 0, CallingContext *P = 0) + : AttrDecl(D), SelfArg(S), SelfArrow(false), + NumArgs(N), FunArgs(A), PrevCtx(P) + { } + }; + + typedef SmallVector<SExprNode, 4> NodeVector; + +private: + // A SExpr is a list of SExprNodes in prefix order. The Size field allows + // the list to be traversed as a tree. + NodeVector NodeVec; + +private: + unsigned makeNop() { + NodeVec.push_back(SExprNode(EOP_Nop, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeWildcard() { + NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeNamedVar(const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_NVar, 0, D)); + return NodeVec.size()-1; + } + + unsigned makeLocalVar(const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_LVar, 0, D)); + return NodeVec.size()-1; + } + + unsigned makeThis() { + NodeVec.push_back(SExprNode(EOP_This, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeDot(const NamedDecl *D, bool Arrow) { + NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D)); + return NodeVec.size()-1; + } + + unsigned makeCall(unsigned NumArgs, const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D)); + return NodeVec.size()-1; + } + + unsigned makeMCall(unsigned NumArgs, const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, D)); + return NodeVec.size()-1; + } + + unsigned makeIndex() { + NodeVec.push_back(SExprNode(EOP_Index, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeUnary() { + NodeVec.push_back(SExprNode(EOP_Unary, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeBinary() { + NodeVec.push_back(SExprNode(EOP_Binary, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeUnknown(unsigned Arity) { + NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0)); + return NodeVec.size()-1; + } + + /// Build an SExpr from the given C++ expression. /// Recursive function that terminates on DeclRefExpr. - /// Note: this function merely creates a MutexID; it does not check to + /// Note: this function merely creates a SExpr; it does not check to /// ensure that the original expression is a valid mutex expression. - void buildMutexID(Expr *Exp, const NamedDecl *D, Expr *Parent, - unsigned NumArgs, Expr **FunArgs) { - if (!Exp) { - DeclSeq.clear(); - return; - } + /// + /// NDeref returns the number of Derefence and AddressOf operations + /// preceeding the Expr; this is used to decide whether to pretty-print + /// SExprs with . or ->. + unsigned buildSExpr(Expr *Exp, CallingContext* CallCtx, int* NDeref = 0) { + if (!Exp) + return 0; if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) { NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl()); @@ -103,144 +264,246 @@ class MutexID { cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl(); unsigned i = PV->getFunctionScopeIndex(); - if (FunArgs && FD == D->getCanonicalDecl()) { + if (CallCtx && CallCtx->FunArgs && + FD == CallCtx->AttrDecl->getCanonicalDecl()) { // Substitute call arguments for references to function parameters - assert(i < NumArgs); - buildMutexID(FunArgs[i], D, 0, 0, 0); - return; + assert(i < CallCtx->NumArgs); + return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref); } // Map the param back to the param of the original function declaration. - DeclSeq.push_back(FD->getParamDecl(i)); - return; + makeNamedVar(FD->getParamDecl(i)); + return 1; } // Not a function parameter -- just store the reference. - DeclSeq.push_back(ND); - } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { - NamedDecl *ND = ME->getMemberDecl(); - DeclSeq.push_back(ND); - buildMutexID(ME->getBase(), D, Parent, NumArgs, FunArgs); + makeNamedVar(ND); + return 1; } else if (isa<CXXThisExpr>(Exp)) { - if (Parent) - buildMutexID(Parent, D, 0, 0, 0); + // Substitute parent for 'this' + if (CallCtx && CallCtx->SelfArg) { + if (!CallCtx->SelfArrow && NDeref) + // 'this' is a pointer, but self is not, so need to take address. + --(*NDeref); + return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref); + } else { - DeclSeq.push_back(0); // Use 0 to represent 'this'. - return; // mutexID is still valid in this case + makeThis(); + return 1; } + } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { + NamedDecl *ND = ME->getMemberDecl(); + int ImplicitDeref = ME->isArrow() ? 1 : 0; + unsigned Root = makeDot(ND, false); + unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref); + NodeVec[Root].setArrow(ImplicitDeref > 0); + NodeVec[Root].setSize(Sz + 1); + return Sz + 1; } else if (CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) { - DeclSeq.push_back(CMCE->getMethodDecl()->getCanonicalDecl()); - buildMutexID(CMCE->getImplicitObjectArgument(), - D, Parent, NumArgs, FunArgs); + // When calling a function with a lock_returned attribute, replace + // the function call with the expression in lock_returned. + if (LockReturnedAttr* At = + CMCE->getMethodDecl()->getAttr<LockReturnedAttr>()) { + CallingContext LRCallCtx(CMCE->getMethodDecl()); + LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument(); + LRCallCtx.SelfArrow = + dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow(); + LRCallCtx.NumArgs = CMCE->getNumArgs(); + LRCallCtx.FunArgs = CMCE->getArgs(); + LRCallCtx.PrevCtx = CallCtx; + return buildSExpr(At->getArg(), &LRCallCtx); + } + // Hack to treat smart pointers and iterators as pointers; + // ignore any method named get(). + if (CMCE->getMethodDecl()->getNameAsString() == "get" && + CMCE->getNumArgs() == 0) { + if (NDeref && dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow()) + ++(*NDeref); + return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref); + } unsigned NumCallArgs = CMCE->getNumArgs(); + unsigned Root = + makeMCall(NumCallArgs, CMCE->getMethodDecl()->getCanonicalDecl()); + unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx); Expr** CallArgs = CMCE->getArgs(); for (unsigned i = 0; i < NumCallArgs; ++i) { - buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs); + Sz += buildSExpr(CallArgs[i], CallCtx); } + NodeVec[Root].setSize(Sz + 1); + return Sz + 1; } else if (CallExpr *CE = dyn_cast<CallExpr>(Exp)) { - buildMutexID(CE->getCallee(), D, Parent, NumArgs, FunArgs); + if (LockReturnedAttr* At = + CE->getDirectCallee()->getAttr<LockReturnedAttr>()) { + CallingContext LRCallCtx(CE->getDirectCallee()); + LRCallCtx.NumArgs = CE->getNumArgs(); + LRCallCtx.FunArgs = CE->getArgs(); + LRCallCtx.PrevCtx = CallCtx; + return buildSExpr(At->getArg(), &LRCallCtx); + } + // Treat smart pointers and iterators as pointers; + // ignore the * and -> operators. + if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) { + OverloadedOperatorKind k = OE->getOperator(); + if (k == OO_Star) { + if (NDeref) ++(*NDeref); + return buildSExpr(OE->getArg(0), CallCtx, NDeref); + } + else if (k == OO_Arrow) { + return buildSExpr(OE->getArg(0), CallCtx, NDeref); + } + } unsigned NumCallArgs = CE->getNumArgs(); + unsigned Root = makeCall(NumCallArgs, 0); + unsigned Sz = buildSExpr(CE->getCallee(), CallCtx); Expr** CallArgs = CE->getArgs(); for (unsigned i = 0; i < NumCallArgs; ++i) { - buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs); + Sz += buildSExpr(CallArgs[i], CallCtx); } + NodeVec[Root].setSize(Sz+1); + return Sz+1; } else if (BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) { - buildMutexID(BOE->getLHS(), D, Parent, NumArgs, FunArgs); - buildMutexID(BOE->getRHS(), D, Parent, NumArgs, FunArgs); + unsigned Root = makeBinary(); + unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx); + Sz += buildSExpr(BOE->getRHS(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; } else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) { - buildMutexID(UOE->getSubExpr(), D, Parent, NumArgs, FunArgs); + // Ignore & and * operators -- they're no-ops. + // However, we try to figure out whether the expression is a pointer, + // so we can use . and -> appropriately in error messages. + if (UOE->getOpcode() == UO_Deref) { + if (NDeref) ++(*NDeref); + return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref); + } + if (UOE->getOpcode() == UO_AddrOf) { + if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) { + if (DRE->getDecl()->isCXXInstanceMember()) { + // This is a pointer-to-member expression, e.g. &MyClass::mu_. + // We interpret this syntax specially, as a wildcard. + unsigned Root = makeDot(DRE->getDecl(), false); + makeWildcard(); + NodeVec[Root].setSize(2); + return 2; + } + } + if (NDeref) --(*NDeref); + return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref); + } + unsigned Root = makeUnary(); + unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; } else if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(Exp)) { - buildMutexID(ASE->getBase(), D, Parent, NumArgs, FunArgs); - buildMutexID(ASE->getIdx(), D, Parent, NumArgs, FunArgs); + unsigned Root = makeIndex(); + unsigned Sz = buildSExpr(ASE->getBase(), CallCtx); + Sz += buildSExpr(ASE->getIdx(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; } else if (AbstractConditionalOperator *CE = - dyn_cast<AbstractConditionalOperator>(Exp)) { - buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs); - buildMutexID(CE->getTrueExpr(), D, Parent, NumArgs, FunArgs); - buildMutexID(CE->getFalseExpr(), D, Parent, NumArgs, FunArgs); + dyn_cast<AbstractConditionalOperator>(Exp)) { + unsigned Root = makeUnknown(3); + unsigned Sz = buildSExpr(CE->getCond(), CallCtx); + Sz += buildSExpr(CE->getTrueExpr(), CallCtx); + Sz += buildSExpr(CE->getFalseExpr(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; } else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) { - buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs); - buildMutexID(CE->getLHS(), D, Parent, NumArgs, FunArgs); - buildMutexID(CE->getRHS(), D, Parent, NumArgs, FunArgs); + unsigned Root = makeUnknown(3); + unsigned Sz = buildSExpr(CE->getCond(), CallCtx); + Sz += buildSExpr(CE->getLHS(), CallCtx); + Sz += buildSExpr(CE->getRHS(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) { - buildMutexID(CE->getSubExpr(), D, Parent, NumArgs, FunArgs); + return buildSExpr(CE->getSubExpr(), CallCtx, NDeref); } else if (ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { - buildMutexID(PE->getSubExpr(), D, Parent, NumArgs, FunArgs); + return buildSExpr(PE->getSubExpr(), CallCtx, NDeref); + } else if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) { + return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref); + } else if (CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) { + return buildSExpr(E->getSubExpr(), CallCtx, NDeref); } else if (isa<CharacterLiteral>(Exp) || - isa<CXXNullPtrLiteralExpr>(Exp) || - isa<GNUNullExpr>(Exp) || - isa<CXXBoolLiteralExpr>(Exp) || - isa<FloatingLiteral>(Exp) || - isa<ImaginaryLiteral>(Exp) || - isa<IntegerLiteral>(Exp) || - isa<StringLiteral>(Exp) || - isa<ObjCStringLiteral>(Exp)) { - return; // FIXME: Ignore literals for now + isa<CXXNullPtrLiteralExpr>(Exp) || + isa<GNUNullExpr>(Exp) || + isa<CXXBoolLiteralExpr>(Exp) || + isa<FloatingLiteral>(Exp) || + isa<ImaginaryLiteral>(Exp) || + isa<IntegerLiteral>(Exp) || + isa<StringLiteral>(Exp) || + isa<ObjCStringLiteral>(Exp)) { + makeNop(); + return 1; // FIXME: Ignore literals for now } else { - // Ignore. FIXME: mark as invalid expression? + makeNop(); + return 1; // Ignore. FIXME: mark as invalid expression? } } - /// \brief Construct a MutexID from an expression. + /// \brief Construct a SExpr from an expression. /// \param MutexExp The original mutex expression within an attribute /// \param DeclExp An expression involving the Decl on which the attribute /// occurs. /// \param D The declaration to which the lock/unlock attribute is attached. - void buildMutexIDFromExp(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D) { - Expr *Parent = 0; - unsigned NumArgs = 0; - Expr **FunArgs = 0; + void buildSExprFromExpr(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D) { + CallingContext CallCtx(D); // If we are processing a raw attribute expression, with no substitutions. if (DeclExp == 0) { - buildMutexID(MutexExp, D, 0, 0, 0); + buildSExpr(MutexExp, 0); return; } - // Examine DeclExp to find Parent and FunArgs, which are used to substitute + // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute // for formal parameters when we call buildMutexID later. if (MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) { - Parent = ME->getBase(); + CallCtx.SelfArg = ME->getBase(); + CallCtx.SelfArrow = ME->isArrow(); } else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(DeclExp)) { - Parent = CE->getImplicitObjectArgument(); - NumArgs = CE->getNumArgs(); - FunArgs = CE->getArgs(); + CallCtx.SelfArg = CE->getImplicitObjectArgument(); + CallCtx.SelfArrow = dyn_cast<MemberExpr>(CE->getCallee())->isArrow(); + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); } else if (CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) { - NumArgs = CE->getNumArgs(); - FunArgs = CE->getArgs(); + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); } else if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(DeclExp)) { - Parent = 0; // FIXME -- get the parent from DeclStmt - NumArgs = CE->getNumArgs(); - FunArgs = CE->getArgs(); + CallCtx.SelfArg = 0; // FIXME -- get the parent from DeclStmt + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); } else if (D && isa<CXXDestructorDecl>(D)) { // There's no such thing as a "destructor call" in the AST. - Parent = DeclExp; + CallCtx.SelfArg = DeclExp; } // If the attribute has no arguments, then assume the argument is "this". if (MutexExp == 0) { - buildMutexID(Parent, D, 0, 0, 0); + buildSExpr(CallCtx.SelfArg, 0); return; } - buildMutexID(MutexExp, D, Parent, NumArgs, FunArgs); + // For most attributes. + buildSExpr(MutexExp, &CallCtx); } -public: - explicit MutexID(clang::Decl::EmptyShell e) { - DeclSeq.clear(); + /// \brief Get index of next sibling of node i. + unsigned getNextSibling(unsigned i) const { + return i + NodeVec[i].size(); } +public: + explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); } + /// \param MutexExp The original mutex expression within an attribute /// \param DeclExp An expression involving the Decl on which the attribute /// occurs. /// \param D The declaration to which the lock/unlock attribute is attached. /// Caller must check isValid() after construction. - MutexID(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D) { - buildMutexIDFromExp(MutexExp, DeclExp, D); + SExpr(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D) { + buildSExprFromExpr(MutexExp, DeclExp, D); } /// Return true if this is a valid decl sequence. /// Caller must call this by hand after construction to handle errors. bool isValid() const { - return !DeclSeq.empty(); + return !NodeVec.empty(); } /// Issue a warning about an invalid lock expression @@ -255,44 +518,144 @@ public: Handler.handleInvalidLockExp(Loc); } - bool operator==(const MutexID &other) const { - return DeclSeq == other.DeclSeq; + bool operator==(const SExpr &other) const { + return NodeVec == other.NodeVec; } - bool operator!=(const MutexID &other) const { + bool operator!=(const SExpr &other) const { return !(*this == other); } - // SmallVector overloads Operator< to do lexicographic ordering. Note that - // we use pointer equality (and <) to compare NamedDecls. This means the order - // of MutexIDs in a lockset is nondeterministic. In order to output - // diagnostics in a deterministic ordering, we must order all diagnostics to - // output by SourceLocation when iterating through this lockset. - bool operator<(const MutexID &other) const { - return DeclSeq < other.DeclSeq; + bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const { + if (NodeVec[i].matches(Other.NodeVec[j])) { + unsigned n = NodeVec[i].arity(); + bool Result = true; + unsigned ci = i+1; // first child of i + unsigned cj = j+1; // first child of j + for (unsigned k = 0; k < n; + ++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) { + Result = Result && matches(Other, ci, cj); + } + return Result; + } + return false; } - /// \brief Returns the name of the first Decl in the list for a given MutexID; - /// e.g. the lock expression foo.bar() has name "bar". - /// The caret will point unambiguously to the lock expression, so using this - /// name in diagnostics is a way to get simple, and consistent, mutex names. - /// We do not want to output the entire expression text for security reasons. - std::string getName() const { + /// \brief Pretty print a lock expression for use in error messages. + std::string toString(unsigned i = 0) const { assert(isValid()); - if (!DeclSeq.front()) - return "this"; // Use 0 to represent 'this'. - return DeclSeq.front()->getNameAsString(); + if (i >= NodeVec.size()) + return ""; + + const SExprNode* N = &NodeVec[i]; + switch (N->kind()) { + case EOP_Nop: + return "_"; + case EOP_Wildcard: + return "(?)"; + case EOP_This: + return "this"; + case EOP_NVar: + case EOP_LVar: { + return N->getNamedDecl()->getNameAsString(); + } + case EOP_Dot: { + if (NodeVec[i+1].kind() == EOP_Wildcard) { + std::string S = "&"; + S += N->getNamedDecl()->getQualifiedNameAsString(); + return S; + } + std::string FieldName = N->getNamedDecl()->getNameAsString(); + if (NodeVec[i+1].kind() == EOP_This) + return FieldName; + + std::string S = toString(i+1); + if (N->isArrow()) + return S + "->" + FieldName; + else + return S + "." + FieldName; + } + case EOP_Call: { + std::string S = toString(i+1) + "("; + unsigned NumArgs = N->arity()-1; + unsigned ci = getNextSibling(i+1); + for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) { + S += toString(ci); + if (k+1 < NumArgs) S += ","; + } + S += ")"; + return S; + } + case EOP_MCall: { + std::string S = ""; + if (NodeVec[i+1].kind() != EOP_This) + S = toString(i+1) + "."; + if (const NamedDecl *D = N->getFunctionDecl()) + S += D->getNameAsString() + "("; + else + S += "#("; + unsigned NumArgs = N->arity()-1; + unsigned ci = getNextSibling(i+1); + for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) { + S += toString(ci); + if (k+1 < NumArgs) S += ","; + } + S += ")"; + return S; + } + case EOP_Index: { + std::string S1 = toString(i+1); + std::string S2 = toString(i+1 + NodeVec[i+1].size()); + return S1 + "[" + S2 + "]"; + } + case EOP_Unary: { + std::string S = toString(i+1); + return "#" + S; + } + case EOP_Binary: { + std::string S1 = toString(i+1); + std::string S2 = toString(i+1 + NodeVec[i+1].size()); + return "(" + S1 + "#" + S2 + ")"; + } + case EOP_Unknown: { + unsigned NumChildren = N->arity(); + if (NumChildren == 0) + return "(...)"; + std::string S = "("; + unsigned ci = i+1; + for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) { + S += toString(ci); + if (j+1 < NumChildren) S += "#"; + } + S += ")"; + return S; + } + } + return ""; } +}; - void Profile(llvm::FoldingSetNodeID &ID) const { - for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(), - E = DeclSeq.end(); I != E; ++I) { - ID.AddPointer(*I); - } + + +/// \brief A short list of SExprs +class MutexIDList : public SmallVector<SExpr, 3> { +public: + /// \brief Return true if the list contains the specified SExpr + /// Performs a linear search, because these lists are almost always very small. + bool contains(const SExpr& M) { + for (iterator I=begin(),E=end(); I != E; ++I) + if ((*I) == M) return true; + return false; + } + + /// \brief Push M onto list, bud discard duplicates + void push_back_nodup(const SExpr& M) { + if (!contains(M)) push_back(M); } }; + /// \brief This is a helper class that stores info about the most recent /// accquire of a Lock. /// @@ -307,14 +670,18 @@ struct LockData { /// /// FIXME: add support for re-entrant locking and lock up/downgrading LockKind LKind; - MutexID UnderlyingMutex; // for ScopedLockable objects + bool Managed; // for ScopedLockable objects + SExpr UnderlyingMutex; // for ScopedLockable objects - LockData(SourceLocation AcquireLoc, LockKind LKind) - : AcquireLoc(AcquireLoc), LKind(LKind), UnderlyingMutex(Decl::EmptyShell()) + LockData(SourceLocation AcquireLoc, LockKind LKind, bool M = false) + : AcquireLoc(AcquireLoc), LKind(LKind), Managed(M), + UnderlyingMutex(Decl::EmptyShell()) {} - LockData(SourceLocation AcquireLoc, LockKind LKind, const MutexID &Mu) - : AcquireLoc(AcquireLoc), LKind(LKind), UnderlyingMutex(Mu) {} + LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu) + : AcquireLoc(AcquireLoc), LKind(LKind), Managed(false), + UnderlyingMutex(Mu) + {} bool operator==(const LockData &other) const { return AcquireLoc == other.AcquireLoc && LKind == other.LKind; @@ -331,10 +698,102 @@ struct LockData { }; -/// A Lockset maps each MutexID (defined above) to information about how it has +/// \brief A FactEntry stores a single fact that is known at a particular point +/// in the program execution. Currently, this is information regarding a lock +/// that is held at that point. +struct FactEntry { + SExpr MutID; + LockData LDat; + + FactEntry(const SExpr& M, const LockData& L) + : MutID(M), LDat(L) + { } +}; + + +typedef unsigned short FactID; + +/// \brief FactManager manages the memory for all facts that are created during +/// the analysis of a single routine. +class FactManager { +private: + std::vector<FactEntry> Facts; + +public: + FactID newLock(const SExpr& M, const LockData& L) { + Facts.push_back(FactEntry(M,L)); + return static_cast<unsigned short>(Facts.size() - 1); + } + + const FactEntry& operator[](FactID F) const { return Facts[F]; } + FactEntry& operator[](FactID F) { return Facts[F]; } +}; + + +/// \brief A FactSet is the set of facts that are known to be true at a +/// particular program point. FactSets must be small, because they are +/// frequently copied, and are thus implemented as a set of indices into a +/// table maintained by a FactManager. A typical FactSet only holds 1 or 2 +/// locks, so we can get away with doing a linear search for lookup. Note +/// that a hashtable or map is inappropriate in this case, because lookups +/// may involve partial pattern matches, rather than exact matches. +class FactSet { +private: + typedef SmallVector<FactID, 4> FactVec; + + FactVec FactIDs; + +public: + typedef FactVec::iterator iterator; + typedef FactVec::const_iterator const_iterator; + + iterator begin() { return FactIDs.begin(); } + const_iterator begin() const { return FactIDs.begin(); } + + iterator end() { return FactIDs.end(); } + const_iterator end() const { return FactIDs.end(); } + + bool isEmpty() const { return FactIDs.size() == 0; } + + FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) { + FactID F = FM.newLock(M, L); + FactIDs.push_back(F); + return F; + } + + bool removeLock(FactManager& FM, const SExpr& M) { + unsigned n = FactIDs.size(); + if (n == 0) + return false; + + for (unsigned i = 0; i < n-1; ++i) { + if (FM[FactIDs[i]].MutID.matches(M)) { + FactIDs[i] = FactIDs[n-1]; + FactIDs.pop_back(); + return true; + } + } + if (FM[FactIDs[n-1]].MutID.matches(M)) { + FactIDs.pop_back(); + return true; + } + return false; + } + + LockData* findLock(FactManager& FM, const SExpr& M) const { + for (const_iterator I=begin(), E=end(); I != E; ++I) { + if (FM[*I].MutID.matches(M)) return &FM[*I].LDat; + } + return 0; + } +}; + + + +/// A Lockset maps each SExpr (defined above) to information about how it has /// been locked. -typedef llvm::ImmutableMap<MutexID, LockData> Lockset; -typedef llvm::ImmutableMap<NamedDecl*, unsigned> LocalVarContext; +typedef llvm::ImmutableMap<SExpr, LockData> Lockset; +typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext; class LocalVariableMap; @@ -345,15 +804,15 @@ enum CFGBlockSide { CBS_Entry, CBS_Exit }; /// maintained for each block in the CFG. See LocalVariableMap for more /// information about the contexts. struct CFGBlockInfo { - Lockset EntrySet; // Lockset held at entry to block - Lockset ExitSet; // Lockset held at exit from block + FactSet EntrySet; // Lockset held at entry to block + FactSet ExitSet; // Lockset held at exit from block LocalVarContext EntryContext; // Context held at entry to block LocalVarContext ExitContext; // Context held at exit from block SourceLocation EntryLoc; // Location of first statement in block SourceLocation ExitLoc; // Location of last statement in block. unsigned EntryIndex; // Used to replay contexts later - const Lockset &getSet(CFGBlockSide Side) const { + const FactSet &getSet(CFGBlockSide Side) const { return Side == CBS_Entry ? EntrySet : ExitSet; } SourceLocation getLocation(CFGBlockSide Side) const { @@ -361,14 +820,12 @@ struct CFGBlockInfo { } private: - CFGBlockInfo(Lockset EmptySet, LocalVarContext EmptyCtx) - : EntrySet(EmptySet), ExitSet(EmptySet), - EntryContext(EmptyCtx), ExitContext(EmptyCtx) + CFGBlockInfo(LocalVarContext EmptyCtx) + : EntryContext(EmptyCtx), ExitContext(EmptyCtx) { } public: - static CFGBlockInfo getEmptyBlockInfo(Lockset::Factory &F, - LocalVariableMap &M); + static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M); }; @@ -398,21 +855,21 @@ public: public: friend class LocalVariableMap; - NamedDecl *Dec; // The original declaration for this variable. - Expr *Exp; // The expression for this variable, OR - unsigned Ref; // Reference to another VarDefinition - Context Ctx; // The map with which Exp should be interpreted. + const NamedDecl *Dec; // The original declaration for this variable. + const Expr *Exp; // The expression for this variable, OR + unsigned Ref; // Reference to another VarDefinition + Context Ctx; // The map with which Exp should be interpreted. bool isReference() { return !Exp; } private: // Create ordinary variable definition - VarDefinition(NamedDecl *D, Expr *E, Context C) + VarDefinition(const NamedDecl *D, const Expr *E, Context C) : Dec(D), Exp(E), Ref(0), Ctx(C) { } // Create reference to previous definition - VarDefinition(NamedDecl *D, unsigned R, Context C) + VarDefinition(const NamedDecl *D, unsigned R, Context C) : Dec(D), Exp(0), Ref(R), Ctx(C) { } }; @@ -430,7 +887,7 @@ public: } /// Look up a definition, within the given context. - const VarDefinition* lookup(NamedDecl *D, Context Ctx) { + const VarDefinition* lookup(const NamedDecl *D, Context Ctx) { const unsigned *i = Ctx.lookup(D); if (!i) return 0; @@ -441,7 +898,7 @@ public: /// Look up the definition for D within the given context. Returns /// NULL if the expression is not statically known. If successful, also /// modifies Ctx to hold the context of the return Expr. - Expr* lookupExpr(NamedDecl *D, Context &Ctx) { + const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) { const unsigned *P = Ctx.lookup(D); if (!P) return 0; @@ -476,7 +933,7 @@ public: llvm::errs() << "Undefined"; return; } - NamedDecl *Dec = VarDefinitions[i].Dec; + const NamedDecl *Dec = VarDefinitions[i].Dec; if (!Dec) { llvm::errs() << "<<NULL>>"; return; @@ -488,7 +945,7 @@ public: /// Dumps an ASCII representation of the variable map to llvm::errs() void dump() { for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) { - Expr *Exp = VarDefinitions[i].Exp; + const Expr *Exp = VarDefinitions[i].Exp; unsigned Ref = VarDefinitions[i].Ref; dumpVarDefinitionName(i); @@ -504,7 +961,7 @@ public: /// Dumps an ASCII representation of a Context to llvm::errs() void dumpContext(Context C) { for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { - NamedDecl *D = I.getKey(); + const NamedDecl *D = I.getKey(); D->printName(llvm::errs()); const unsigned *i = C.lookup(D); llvm::errs() << " -> "; @@ -528,7 +985,7 @@ protected: // Adds a new definition to the given context, and returns a new context. // This method should be called when declaring a new variable. - Context addDefinition(NamedDecl *D, Expr *Exp, Context Ctx) { + Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { assert(!Ctx.contains(D)); unsigned newID = VarDefinitions.size(); Context NewCtx = ContextFactory.add(Ctx, D, newID); @@ -537,7 +994,7 @@ protected: } // Add a new reference to an existing definition. - Context addReference(NamedDecl *D, unsigned i, Context Ctx) { + Context addReference(const NamedDecl *D, unsigned i, Context Ctx) { unsigned newID = VarDefinitions.size(); Context NewCtx = ContextFactory.add(Ctx, D, newID); VarDefinitions.push_back(VarDefinition(D, i, Ctx)); @@ -546,7 +1003,7 @@ protected: // Updates a definition only if that definition is already in the map. // This method should be called when assigning to an existing variable. - Context updateDefinition(NamedDecl *D, Expr *Exp, Context Ctx) { + Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { if (Ctx.contains(D)) { unsigned newID = VarDefinitions.size(); Context NewCtx = ContextFactory.remove(Ctx, D); @@ -559,7 +1016,7 @@ protected: // Removes a definition from the context, but keeps the variable name // as a valid variable. The index 0 is a placeholder for cleared definitions. - Context clearDefinition(NamedDecl *D, Context Ctx) { + Context clearDefinition(const NamedDecl *D, Context Ctx) { Context NewCtx = Ctx; if (NewCtx.contains(D)) { NewCtx = ContextFactory.remove(NewCtx, D); @@ -569,7 +1026,7 @@ protected: } // Remove a definition entirely frmo the context. - Context removeDefinition(NamedDecl *D, Context Ctx) { + Context removeDefinition(const NamedDecl *D, Context Ctx) { Context NewCtx = Ctx; if (NewCtx.contains(D)) { NewCtx = ContextFactory.remove(NewCtx, D); @@ -586,9 +1043,8 @@ protected: // This has to be defined after LocalVariableMap. -CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(Lockset::Factory &F, - LocalVariableMap &M) { - return CFGBlockInfo(F.getEmptyMap(), M.getEmptyContext()); +CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) { + return CFGBlockInfo(M.getEmptyContext()); } @@ -655,7 +1111,7 @@ LocalVariableMap::Context LocalVariableMap::intersectContexts(Context C1, Context C2) { Context Result = C1; for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { - NamedDecl *Dec = I.getKey(); + const NamedDecl *Dec = I.getKey(); unsigned i1 = I.getData(); const unsigned *i2 = C2.lookup(Dec); if (!i2) // variable doesn't exist on second path @@ -672,7 +1128,7 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) { LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) { Context Result = getEmptyContext(); for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { - NamedDecl *Dec = I.getKey(); + const NamedDecl *Dec = I.getKey(); unsigned i = I.getData(); Result = addReference(Dec, i, Result); } @@ -684,7 +1140,7 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) { // createReferenceContext. void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { - NamedDecl *Dec = I.getKey(); + const NamedDecl *Dec = I.getKey(); unsigned i1 = I.getData(); VarDefinition *VDef = &VarDefinitions[i1]; assert(VDef->isReference()); @@ -725,7 +1181,7 @@ void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { // incoming back edge, it duplicates the context, creating new definitions // that refer back to the originals. (These correspond to places where SSA // might have to insert a phi node.) On the second pass, these definitions are -// set to NULL if the the variable has changed on the back-edge (i.e. a phi +// set to NULL if the variable has changed on the back-edge (i.e. a phi // node was actually required.) E.g. // // { Context | VarDefinitions } @@ -869,24 +1325,294 @@ static void findBlockLocations(CFG *CFGraph, class ThreadSafetyAnalyzer { friend class BuildLockset; - ThreadSafetyHandler &Handler; - Lockset::Factory LocksetFactory; - LocalVariableMap LocalVarMap; + ThreadSafetyHandler &Handler; + LocalVariableMap LocalVarMap; + FactManager FactMan; + std::vector<CFGBlockInfo> BlockInfo; public: ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {} - Lockset intersectAndWarn(const CFGBlockInfo &Block1, CFGBlockSide Side1, - const CFGBlockInfo &Block2, CFGBlockSide Side2, - LockErrorKind LEK); + void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat); + void removeLock(FactSet &FSet, const SExpr &Mutex, + SourceLocation UnlockLoc, bool FullyRemove=false); + + template <typename AttrType> + void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, + const NamedDecl *D); - Lockset addLock(Lockset &LSet, Expr *MutexExp, const NamedDecl *D, - LockKind LK, SourceLocation Loc); + template <class AttrType> + void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, + const NamedDecl *D, + const CFGBlock *PredBlock, const CFGBlock *CurrBlock, + Expr *BrE, bool Neg); + + const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C, + bool &Negate); + + void getEdgeLockset(FactSet &Result, const FactSet &ExitSet, + const CFGBlock* PredBlock, + const CFGBlock *CurrBlock); + + void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2, + SourceLocation JoinLoc, + LockErrorKind LEK1, LockErrorKind LEK2, + bool Modify=true); + + void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2, + SourceLocation JoinLoc, LockErrorKind LEK1, + bool Modify=true) { + intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify); + } void runAnalysis(AnalysisDeclContext &AC); }; +/// \brief Add a new lock to the lockset, warning if the lock is already there. +/// \param Mutex -- the Mutex expression for the lock +/// \param LDat -- the LockData for the lock +void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, + const LockData &LDat) { + // FIXME: deal with acquired before/after annotations. + // FIXME: Don't always warn when we have support for reentrant locks. + if (FSet.findLock(FactMan, Mutex)) { + Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc); + } else { + FSet.addLock(FactMan, Mutex, LDat); + } +} + + +/// \brief Remove a lock from the lockset, warning if the lock is not there. +/// \param LockExp The lock expression corresponding to the lock to be removed +/// \param UnlockLoc The source location of the unlock (only used in error msg) +void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, + const SExpr &Mutex, + SourceLocation UnlockLoc, + bool FullyRemove) { + const LockData *LDat = FSet.findLock(FactMan, Mutex); + if (!LDat) { + Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc); + return; + } + + if (LDat->UnderlyingMutex.isValid()) { + // This is scoped lockable object, which manages the real mutex. + if (FullyRemove) { + // We're destroying the managing object. + // Remove the underlying mutex if it exists; but don't warn. + if (FSet.findLock(FactMan, LDat->UnderlyingMutex)) + FSet.removeLock(FactMan, LDat->UnderlyingMutex); + } else { + // We're releasing the underlying mutex, but not destroying the + // managing object. Warn on dual release. + if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) { + Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(), + UnlockLoc); + } + FSet.removeLock(FactMan, LDat->UnderlyingMutex); + return; + } + } + FSet.removeLock(FactMan, Mutex); +} + + +/// \brief Extract the list of mutexIDs from the attribute on an expression, +/// and push them onto Mtxs, discarding any duplicates. +template <typename AttrType> +void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, + Expr *Exp, const NamedDecl *D) { + typedef typename AttrType::args_iterator iterator_type; + + if (Attr->args_size() == 0) { + // The mutex held is the "this" object. + SExpr Mu(0, Exp, D); + if (!Mu.isValid()) + SExpr::warnInvalidLock(Handler, 0, Exp, D); + else + Mtxs.push_back_nodup(Mu); + return; + } + + for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) { + SExpr Mu(*I, Exp, D); + if (!Mu.isValid()) + SExpr::warnInvalidLock(Handler, *I, Exp, D); + else + Mtxs.push_back_nodup(Mu); + } +} + + +/// \brief Extract the list of mutexIDs from a trylock attribute. If the +/// trylock applies to the given edge, then push them onto Mtxs, discarding +/// any duplicates. +template <class AttrType> +void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, + Expr *Exp, const NamedDecl *D, + const CFGBlock *PredBlock, + const CFGBlock *CurrBlock, + Expr *BrE, bool Neg) { + // Find out which branch has the lock + bool branch = 0; + if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) { + branch = BLE->getValue(); + } + else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) { + branch = ILE->getValue().getBoolValue(); + } + int branchnum = branch ? 0 : 1; + if (Neg) branchnum = !branchnum; + + // If we've taken the trylock branch, then add the lock + int i = 0; + for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(), + SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) { + if (*SI == CurrBlock && i == branchnum) { + getMutexIDs(Mtxs, Attr, Exp, D); + } + } +} + + +bool getStaticBooleanValue(Expr* E, bool& TCond) { + if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) { + TCond = false; + return true; + } else if (CXXBoolLiteralExpr *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) { + TCond = BLE->getValue(); + return true; + } else if (IntegerLiteral *ILE = dyn_cast<IntegerLiteral>(E)) { + TCond = ILE->getValue().getBoolValue(); + return true; + } else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) { + return getStaticBooleanValue(CE->getSubExpr(), TCond); + } + return false; +} + + +// If Cond can be traced back to a function call, return the call expression. +// The negate variable should be called with false, and will be set to true +// if the function call is negated, e.g. if (!mu.tryLock(...)) +const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, + LocalVarContext C, + bool &Negate) { + if (!Cond) + return 0; + + if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) { + return CallExp; + } + else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) { + return getTrylockCallExpr(PE->getSubExpr(), C, Negate); + } + else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) { + return getTrylockCallExpr(CE->getSubExpr(), C, Negate); + } + else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) { + const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C); + return getTrylockCallExpr(E, C, Negate); + } + else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) { + if (UOP->getOpcode() == UO_LNot) { + Negate = !Negate; + return getTrylockCallExpr(UOP->getSubExpr(), C, Negate); + } + return 0; + } + else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) { + if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) { + if (BOP->getOpcode() == BO_NE) + Negate = !Negate; + + bool TCond = false; + if (getStaticBooleanValue(BOP->getRHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getLHS(), C, Negate); + } + else if (getStaticBooleanValue(BOP->getLHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getRHS(), C, Negate); + } + return 0; + } + return 0; + } + // FIXME -- handle && and || as well. + return 0; +} + + +/// \brief Find the lockset that holds on the edge between PredBlock +/// and CurrBlock. The edge set is the exit set of PredBlock (passed +/// as the ExitSet parameter) plus any trylocks, which are conditionally held. +void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, + const FactSet &ExitSet, + const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) { + Result = ExitSet; + + if (!PredBlock->getTerminatorCondition()) + return; + + bool Negate = false; + const Stmt *Cond = PredBlock->getTerminatorCondition(); + const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()]; + const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext; + + CallExpr *Exp = + const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate)); + if (!Exp) + return; + + NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl()); + if(!FunDecl || !FunDecl->hasAttrs()) + return; + + + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + + // If the condition is a call to a Trylock function, then grab the attributes + AttrVec &ArgAttrs = FunDecl->getAttrs(); + for (unsigned i = 0; i < ArgAttrs.size(); ++i) { + Attr *Attr = ArgAttrs[i]; + switch (Attr->getKind()) { + case attr::ExclusiveTrylockFunction: { + ExclusiveTrylockFunctionAttr *A = + cast<ExclusiveTrylockFunctionAttr>(Attr); + getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, + PredBlock, CurrBlock, A->getSuccessValue(), Negate); + break; + } + case attr::SharedTrylockFunction: { + SharedTrylockFunctionAttr *A = + cast<SharedTrylockFunctionAttr>(Attr); + getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, + PredBlock, CurrBlock, A->getSuccessValue(), Negate); + break; + } + default: + break; + } + } + + // Add and remove locks. + SourceLocation Loc = Exp->getExprLoc(); + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + addLock(Result, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + addLock(Result, SharedLocksToAdd[i], + LockData(Loc, LK_Shared)); + } +} + + /// \brief We use this class to visit different types of expressions in /// CFGBlocks, and build up the lockset. /// An expression may cause us to add or remove locks from the lockset, or else @@ -895,50 +1621,31 @@ public: class BuildLockset : public StmtVisitor<BuildLockset> { friend class ThreadSafetyAnalyzer; - ThreadSafetyHandler &Handler; - Lockset::Factory &LocksetFactory; - LocalVariableMap &LocalVarMap; - - Lockset LSet; + ThreadSafetyAnalyzer *Analyzer; + FactSet FSet; LocalVariableMap::Context LVarCtx; unsigned CtxIndex; // Helper functions - void addLock(const MutexID &Mutex, const LockData &LDat); - void removeLock(const MutexID &Mutex, SourceLocation UnlockLoc); + const ValueDecl *getValueDecl(Expr *Exp); - template <class AttrType> - void addLocksToSet(LockKind LK, AttrType *Attr, - Expr *Exp, NamedDecl *D, VarDecl *VD = 0); - void removeLocksFromSet(UnlockFunctionAttr *Attr, - Expr *Exp, NamedDecl* FunDecl); + void warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp, AccessKind AK, + Expr *MutexExp, ProtectedOperationKind POK); - const ValueDecl *getValueDecl(Expr *Exp); - void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK, - Expr *MutexExp, ProtectedOperationKind POK); void checkAccess(Expr *Exp, AccessKind AK); void checkDereference(Expr *Exp, AccessKind AK); - void handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD = 0); - - template <class AttrType> - void addTrylock(LockKind LK, AttrType *Attr, Expr *Exp, NamedDecl *FunDecl, - const CFGBlock* PredBlock, const CFGBlock *CurrBlock, - Expr *BrE, bool Neg); - CallExpr* getTrylockCallExpr(Stmt *Cond, LocalVariableMap::Context C, - bool &Negate); - void handleTrylock(Stmt *Cond, const CFGBlock* PredBlock, - const CFGBlock *CurrBlock); + void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0); /// \brief Returns true if the lockset contains a lock, regardless of whether /// the lock is held exclusively or shared. - bool locksetContains(const MutexID &Lock) const { - return LSet.lookup(Lock); + bool locksetContains(const SExpr &Mu) const { + return FSet.findLock(Analyzer->FactMan, Mu); } /// \brief Returns true if the lockset contains a lock with the passed in /// locktype. - bool locksetContains(const MutexID &Lock, LockKind KindRequested) const { - const LockData *LockHeld = LSet.lookup(Lock); + bool locksetContains(const SExpr &Mu, LockKind KindRequested) const { + const LockData *LockHeld = FSet.findLock(Analyzer->FactMan, Mu); return (LockHeld && KindRequested == LockHeld->LKind); } @@ -946,7 +1653,7 @@ class BuildLockset : public StmtVisitor<BuildLockset> { /// passed in locktype. So for example, if we pass in LK_Shared, this function /// returns true if the lock is held LK_Shared or LK_Exclusive. If we pass in /// LK_Exclusive, this function returns true if the lock is held LK_Exclusive. - bool locksetContainsAtLeast(const MutexID &Lock, + bool locksetContainsAtLeast(const SExpr &Lock, LockKind KindRequested) const { switch (KindRequested) { case LK_Shared: @@ -958,12 +1665,10 @@ class BuildLockset : public StmtVisitor<BuildLockset> { } public: - BuildLockset(ThreadSafetyAnalyzer *analyzer, CFGBlockInfo &Info) + BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info) : StmtVisitor<BuildLockset>(), - Handler(analyzer->Handler), - LocksetFactory(analyzer->LocksetFactory), - LocalVarMap(analyzer->LocalVarMap), - LSet(Info.EntrySet), + Analyzer(Anlzr), + FSet(Info.EntrySet), LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {} @@ -976,104 +1681,6 @@ public: void VisitDeclStmt(DeclStmt *S); }; -/// \brief Add a new lock to the lockset, warning if the lock is already there. -/// \param Mutex -- the Mutex expression for the lock -/// \param LDat -- the LockData for the lock -void BuildLockset::addLock(const MutexID &Mutex, const LockData& LDat) { - // FIXME: deal with acquired before/after annotations. - // FIXME: Don't always warn when we have support for reentrant locks. - if (locksetContains(Mutex)) - Handler.handleDoubleLock(Mutex.getName(), LDat.AcquireLoc); - else - LSet = LocksetFactory.add(LSet, Mutex, LDat); -} - -/// \brief Remove a lock from the lockset, warning if the lock is not there. -/// \param LockExp The lock expression corresponding to the lock to be removed -/// \param UnlockLoc The source location of the unlock (only used in error msg) -void BuildLockset::removeLock(const MutexID &Mutex, SourceLocation UnlockLoc) { - const LockData *LDat = LSet.lookup(Mutex); - if (!LDat) - Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc); - else { - // For scoped-lockable vars, remove the mutex associated with this var. - if (LDat->UnderlyingMutex.isValid()) - removeLock(LDat->UnderlyingMutex, UnlockLoc); - LSet = LocksetFactory.remove(LSet, Mutex); - } -} - -/// \brief This function, parameterized by an attribute type, is used to add a -/// set of locks specified as attribute arguments to the lockset. -template <typename AttrType> -void BuildLockset::addLocksToSet(LockKind LK, AttrType *Attr, - Expr *Exp, NamedDecl* FunDecl, VarDecl *VD) { - typedef typename AttrType::args_iterator iterator_type; - - SourceLocation ExpLocation = Exp->getExprLoc(); - - // Figure out if we're calling the constructor of scoped lockable class - bool isScopedVar = false; - if (VD) { - if (CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FunDecl)) { - CXXRecordDecl* PD = CD->getParent(); - if (PD && PD->getAttr<ScopedLockableAttr>()) - isScopedVar = true; - } - } - - if (Attr->args_size() == 0) { - // The mutex held is the "this" object. - MutexID Mutex(0, Exp, FunDecl); - if (!Mutex.isValid()) - MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl); - else - addLock(Mutex, LockData(ExpLocation, LK)); - return; - } - - for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) { - MutexID Mutex(*I, Exp, FunDecl); - if (!Mutex.isValid()) - MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl); - else { - addLock(Mutex, LockData(ExpLocation, LK)); - if (isScopedVar) { - // For scoped lockable vars, map this var to its underlying mutex. - DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); - MutexID SMutex(&DRE, 0, 0); - addLock(SMutex, LockData(VD->getLocation(), LK, Mutex)); - } - } - } -} - -/// \brief This function removes a set of locks specified as attribute -/// arguments from the lockset. -void BuildLockset::removeLocksFromSet(UnlockFunctionAttr *Attr, - Expr *Exp, NamedDecl* FunDecl) { - SourceLocation ExpLocation; - if (Exp) ExpLocation = Exp->getExprLoc(); - - if (Attr->args_size() == 0) { - // The mutex held is the "this" object. - MutexID Mu(0, Exp, FunDecl); - if (!Mu.isValid()) - MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl); - else - removeLock(Mu, ExpLocation); - return; - } - - for (UnlockFunctionAttr::args_iterator I = Attr->args_begin(), - E = Attr->args_end(); I != E; ++I) { - MutexID Mutex(*I, Exp, FunDecl); - if (!Mutex.isValid()) - MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl); - else - removeLock(Mutex, ExpLocation); - } -} /// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) { @@ -1093,11 +1700,12 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp, ProtectedOperationKind POK) { LockKind LK = getLockKindFromAccessKind(AK); - MutexID Mutex(MutexExp, Exp, D); + SExpr Mutex(MutexExp, Exp, D); if (!Mutex.isValid()) - MutexID::warnInvalidLock(Handler, MutexExp, Exp, D); + SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); else if (!locksetContainsAtLeast(Mutex, LK)) - Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc()); + Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, + Exp->getExprLoc()); } /// \brief This method identifies variable dereferences and checks pt_guarded_by @@ -1116,8 +1724,9 @@ void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) { if(!D || !D->hasAttrs()) return; - if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty()) - Handler.handleNoMutexHeld(D, POK_VarDereference, AK, Exp->getExprLoc()); + if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK, + Exp->getExprLoc()); const AttrVec &ArgAttrs = D->getAttrs(); for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) @@ -1134,8 +1743,9 @@ void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) { if(!D || !D->hasAttrs()) return; - if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty()) - Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc()); + if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK, + Exp->getExprLoc()); const AttrVec &ArgAttrs = D->getAttrs(); for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) @@ -1153,68 +1763,68 @@ void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) { /// and check that the appropriate locks are held. Non-const method calls with /// the same signature as const method calls can be also treated as reads. /// -/// FIXME: We need to also visit CallExprs to catch/check global functions. -/// -/// FIXME: Do not flag an error for member variables accessed in constructors/ -/// destructors -void BuildLockset::handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD) { - AttrVec &ArgAttrs = D->getAttrs(); +void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { + const AttrVec &ArgAttrs = D->getAttrs(); + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + MutexIDList LocksToRemove; + for(unsigned i = 0; i < ArgAttrs.size(); ++i) { - Attr *Attr = ArgAttrs[i]; - switch (Attr->getKind()) { + Attr *At = const_cast<Attr*>(ArgAttrs[i]); + switch (At->getKind()) { // When we encounter an exclusive lock function, we need to add the lock // to our lockset with kind exclusive. case attr::ExclusiveLockFunction: { - ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(Attr); - addLocksToSet(LK_Exclusive, A, Exp, D, VD); + ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At); + Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D); break; } // When we encounter a shared lock function, we need to add the lock // to our lockset with kind shared. case attr::SharedLockFunction: { - SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(Attr); - addLocksToSet(LK_Shared, A, Exp, D, VD); + SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At); + Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D); break; } // When we encounter an unlock function, we need to remove unlocked // mutexes from the lockset, and flag a warning if they are not there. case attr::UnlockFunction: { - UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr); - removeLocksFromSet(UFAttr, Exp, D); + UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At); + Analyzer->getMutexIDs(LocksToRemove, A, Exp, D); break; } case attr::ExclusiveLocksRequired: { - ExclusiveLocksRequiredAttr *ELRAttr = - cast<ExclusiveLocksRequiredAttr>(Attr); + ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At); for (ExclusiveLocksRequiredAttr::args_iterator - I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E; ++I) + I = A->args_begin(), E = A->args_end(); I != E; ++I) warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall); break; } case attr::SharedLocksRequired: { - SharedLocksRequiredAttr *SLRAttr = cast<SharedLocksRequiredAttr>(Attr); + SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At); - for (SharedLocksRequiredAttr::args_iterator I = SLRAttr->args_begin(), - E = SLRAttr->args_end(); I != E; ++I) + for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(), + E = A->args_end(); I != E; ++I) warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall); break; } case attr::LocksExcluded: { - LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr); - for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(), - E = LEAttr->args_end(); I != E; ++I) { - MutexID Mutex(*I, Exp, D); + LocksExcludedAttr *A = cast<LocksExcludedAttr>(At); + for (LocksExcludedAttr::args_iterator I = A->args_begin(), + E = A->args_end(); I != E; ++I) { + SExpr Mutex(*I, Exp, D); if (!Mutex.isValid()) - MutexID::warnInvalidLock(Handler, *I, Exp, D); + SExpr::warnInvalidLock(Analyzer->Handler, *I, Exp, D); else if (locksetContains(Mutex)) - Handler.handleFunExcludesLock(D->getName(), Mutex.getName(), - Exp->getExprLoc()); + Analyzer->Handler.handleFunExcludesLock(D->getName(), + Mutex.toString(), + Exp->getExprLoc()); } break; } @@ -1224,102 +1834,50 @@ void BuildLockset::handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD) { break; } } -} - - -/// \brief Add lock to set, if the current block is in the taken branch of a -/// trylock. -template <class AttrType> -void BuildLockset::addTrylock(LockKind LK, AttrType *Attr, Expr *Exp, - NamedDecl *FunDecl, const CFGBlock *PredBlock, - const CFGBlock *CurrBlock, Expr *BrE, bool Neg) { - // Find out which branch has the lock - bool branch = 0; - if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) { - branch = BLE->getValue(); - } - else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) { - branch = ILE->getValue().getBoolValue(); - } - int branchnum = branch ? 0 : 1; - if (Neg) branchnum = !branchnum; - // If we've taken the trylock branch, then add the lock - int i = 0; - for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(), - SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) { - if (*SI == CurrBlock && i == branchnum) { - addLocksToSet(LK, Attr, Exp, FunDecl, 0); + // Figure out if we're calling the constructor of scoped lockable class + bool isScopedVar = false; + if (VD) { + if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) { + const CXXRecordDecl* PD = CD->getParent(); + if (PD && PD->getAttr<ScopedLockableAttr>()) + isScopedVar = true; } } -} - -// If Cond can be traced back to a function call, return the call expression. -// The negate variable should be called with false, and will be set to true -// if the function call is negated, e.g. if (!mu.tryLock(...)) -CallExpr* BuildLockset::getTrylockCallExpr(Stmt *Cond, - LocalVariableMap::Context C, - bool &Negate) { - if (!Cond) - return 0; - - if (CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) { - return CallExp; - } - else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) { - return getTrylockCallExpr(CE->getSubExpr(), C, Negate); + // Add locks. + SourceLocation Loc = Exp->getExprLoc(); + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive, isScopedVar)); } - else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) { - Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C); - return getTrylockCallExpr(E, C, Negate); - } - else if (UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) { - if (UOP->getOpcode() == UO_LNot) { - Negate = !Negate; - return getTrylockCallExpr(UOP->getSubExpr(), C, Negate); - } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SharedLocksToAdd[i], + LockData(Loc, LK_Shared, isScopedVar)); } - // FIXME -- handle && and || as well. - return NULL; -} - - -/// \brief Process a conditional branch from a previous block to the current -/// block, looking for trylock calls. -void BuildLockset::handleTrylock(Stmt *Cond, const CFGBlock *PredBlock, - const CFGBlock *CurrBlock) { - bool Negate = false; - CallExpr *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate); - if (!Exp) - return; - NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl()); - if(!FunDecl || !FunDecl->hasAttrs()) - return; + // Add the managing object as a dummy mutex, mapped to the underlying mutex. + // FIXME -- this doesn't work if we acquire multiple locks. + if (isScopedVar) { + SourceLocation MLoc = VD->getLocation(); + DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); + SExpr SMutex(&DRE, 0, 0); - // If the condition is a call to a Trylock function, then grab the attributes - AttrVec &ArgAttrs = FunDecl->getAttrs(); - for (unsigned i = 0; i < ArgAttrs.size(); ++i) { - Attr *Attr = ArgAttrs[i]; - switch (Attr->getKind()) { - case attr::ExclusiveTrylockFunction: { - ExclusiveTrylockFunctionAttr *A = - cast<ExclusiveTrylockFunctionAttr>(Attr); - addTrylock(LK_Exclusive, A, Exp, FunDecl, PredBlock, CurrBlock, - A->getSuccessValue(), Negate); - break; - } - case attr::SharedTrylockFunction: { - SharedTrylockFunctionAttr *A = - cast<SharedTrylockFunctionAttr>(Attr); - addTrylock(LK_Shared, A, Exp, FunDecl, PredBlock, CurrBlock, - A->getSuccessValue(), Negate); - break; - } - default: - break; + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, + ExclusiveLocksToAdd[i])); } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, + SharedLocksToAdd[i])); + } + } + + // Remove locks. + // FIXME -- should only fully remove if the attribute refers to 'this'. + bool Dtor = isa<CXXDestructorDecl>(D); + for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) { + Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor); } } @@ -1351,7 +1909,7 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) { return; // adjust the context - LVarCtx = LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx); + LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx); Expr *LHSExp = BO->getLHS()->IgnoreParenCasts(); checkAccess(LHSExp, AK_Written); @@ -1383,13 +1941,17 @@ void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) { void BuildLockset::VisitDeclStmt(DeclStmt *S) { // adjust the context - LVarCtx = LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); + LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); DeclGroupRef DGrp = S->getDeclGroup(); for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { Decl *D = *I; if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) { Expr *E = VD->getInit(); + // handle constructors that involve temporaries + if (ExprWithCleanups *EWC = dyn_cast_or_null<ExprWithCleanups>(E)) + E = EWC->getSubExpr(); + if (CXXConstructExpr *CE = dyn_cast_or_null<CXXConstructExpr>(E)) { NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor()); if (!CtorD || !CtorD->hasAttrs()) @@ -1401,6 +1963,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) { } + /// \brief Compute the intersection of two locksets and issue warnings for any /// locks in the symmetric difference. /// @@ -1409,58 +1972,80 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) { /// A; if () then B; else C; D; we need to check that the lockset after B and C /// are the same. In the event of a difference, we use the intersection of these /// two locksets at the start of D. -Lockset ThreadSafetyAnalyzer::intersectAndWarn(const CFGBlockInfo &Block1, - CFGBlockSide Side1, - const CFGBlockInfo &Block2, - CFGBlockSide Side2, - LockErrorKind LEK) { - Lockset LSet1 = Block1.getSet(Side1); - Lockset LSet2 = Block2.getSet(Side2); - - Lockset Intersection = LSet1; - for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) { - const MutexID &LSet2Mutex = I.getKey(); - const LockData &LSet2LockData = I.getData(); - if (const LockData *LD = LSet1.lookup(LSet2Mutex)) { - if (LD->LKind != LSet2LockData.LKind) { - Handler.handleExclusiveAndShared(LSet2Mutex.getName(), - LSet2LockData.AcquireLoc, - LD->AcquireLoc); - if (LD->LKind != LK_Exclusive) - Intersection = LocksetFactory.add(Intersection, LSet2Mutex, - LSet2LockData); +/// +/// \param LSet1 The first lockset. +/// \param LSet2 The second lockset. +/// \param JoinLoc The location of the join point for error reporting +/// \param LEK1 The error message to report if a mutex is missing from LSet1 +/// \param LEK2 The error message to report if a mutex is missing from Lset2 +void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1, + const FactSet &FSet2, + SourceLocation JoinLoc, + LockErrorKind LEK1, + LockErrorKind LEK2, + bool Modify) { + FactSet FSet1Orig = FSet1; + + for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end(); + I != E; ++I) { + const SExpr &FSet2Mutex = FactMan[*I].MutID; + const LockData &LDat2 = FactMan[*I].LDat; + + if (const LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) { + if (LDat1->LKind != LDat2.LKind) { + Handler.handleExclusiveAndShared(FSet2Mutex.toString(), + LDat2.AcquireLoc, + LDat1->AcquireLoc); + if (Modify && LDat1->LKind != LK_Exclusive) { + FSet1.removeLock(FactMan, FSet2Mutex); + FSet1.addLock(FactMan, FSet2Mutex, LDat2); + } } } else { - Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(), - LSet2LockData.AcquireLoc, - Block1.getLocation(Side1), LEK); + if (LDat2.UnderlyingMutex.isValid()) { + if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) { + // If this is a scoped lock that manages another mutex, and if the + // underlying mutex is still held, then warn about the underlying + // mutex. + Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(), + LDat2.AcquireLoc, + JoinLoc, LEK1); + } + } + else if (!LDat2.Managed) + Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(), + LDat2.AcquireLoc, + JoinLoc, LEK1); } } - for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) { - if (!LSet2.contains(I.getKey())) { - const MutexID &Mutex = I.getKey(); - const LockData &MissingLock = I.getData(); - Handler.handleMutexHeldEndOfScope(Mutex.getName(), - MissingLock.AcquireLoc, - Block2.getLocation(Side2), LEK); - Intersection = LocksetFactory.remove(Intersection, Mutex); + for (FactSet::const_iterator I = FSet1.begin(), E = FSet1.end(); + I != E; ++I) { + const SExpr &FSet1Mutex = FactMan[*I].MutID; + const LockData &LDat1 = FactMan[*I].LDat; + + if (!FSet2.findLock(FactMan, FSet1Mutex)) { + if (LDat1.UnderlyingMutex.isValid()) { + if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) { + // If this is a scoped lock that manages another mutex, and if the + // underlying mutex is still held, then warn about the underlying + // mutex. + Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(), + LDat1.AcquireLoc, + JoinLoc, LEK1); + } + } + else if (!LDat1.Managed) + Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(), + LDat1.AcquireLoc, + JoinLoc, LEK2); + if (Modify) + FSet1.removeLock(FactMan, FSet1Mutex); } } - return Intersection; } -Lockset ThreadSafetyAnalyzer::addLock(Lockset &LSet, Expr *MutexExp, - const NamedDecl *D, - LockKind LK, SourceLocation Loc) { - MutexID Mutex(MutexExp, 0, D); - if (!Mutex.isValid()) { - MutexID::warnInvalidLock(Handler, MutexExp, 0, D); - return LSet; - } - LockData NewLock(Loc, LK); - return LocksetFactory.add(LSet, Mutex, NewLock); -} + /// \brief Check a function's CFG for thread-safety violations. /// @@ -1472,6 +2057,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { if (!CFGraph) return; const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); + // AC.dumpCFG(true); + if (!D) return; // Ignore anonymous functions for now. if (D->getAttr<NoThreadSafetyAnalysisAttr>()) @@ -1485,8 +2072,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { if (isa<CXXDestructorDecl>(D)) return; // Don't check inside destructors. - std::vector<CFGBlockInfo> BlockInfo(CFGraph->getNumBlockIDs(), - CFGBlockInfo::getEmptyBlockInfo(LocksetFactory, LocalVarMap)); + BlockInfo.resize(CFGraph->getNumBlockIDs(), + CFGBlockInfo::getEmptyBlockInfo(LocalVarMap)); // We need to explore the CFG via a "topological" ordering. // That way, we will be guaranteed to have information about required @@ -1505,27 +2092,22 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // FIXME: is there a more intelligent way to check lock/unlock functions? if (!SortedGraph->empty() && D->hasAttrs()) { const CFGBlock *FirstBlock = *SortedGraph->begin(); - Lockset &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet; + FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet; const AttrVec &ArgAttrs = D->getAttrs(); + + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + + SourceLocation Loc = D->getLocation(); for (unsigned i = 0; i < ArgAttrs.size(); ++i) { Attr *Attr = ArgAttrs[i]; - SourceLocation AttrLoc = Attr->getLocation(); - if (SharedLocksRequiredAttr *SLRAttr - = dyn_cast<SharedLocksRequiredAttr>(Attr)) { - for (SharedLocksRequiredAttr::args_iterator - SLRIter = SLRAttr->args_begin(), - SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter) - InitialLockset = addLock(InitialLockset, - *SLRIter, D, LK_Shared, - AttrLoc); - } else if (ExclusiveLocksRequiredAttr *ELRAttr - = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { - for (ExclusiveLocksRequiredAttr::args_iterator - ELRIter = ELRAttr->args_begin(), - ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter) - InitialLockset = addLock(InitialLockset, - *ELRIter, D, LK_Exclusive, - AttrLoc); + Loc = Attr->getLocation(); + if (ExclusiveLocksRequiredAttr *A + = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { + getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); + } else if (SharedLocksRequiredAttr *A + = dyn_cast<SharedLocksRequiredAttr>(Attr)) { + getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); } else if (isa<UnlockFunctionAttr>(Attr)) { // Don't try to check unlock functions for now return; @@ -1535,8 +2117,24 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } else if (isa<SharedLockFunctionAttr>(Attr)) { // Don't try to check lock functions for now return; + } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) { + // Don't try to check trylock functions for now + return; + } else if (isa<SharedTrylockFunctionAttr>(Attr)) { + // Don't try to check trylock functions for now + return; } } + + // FIXME -- Loc can be wrong here. + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + addLock(InitialLockset, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + addLock(InitialLockset, SharedLocksToAdd[i], + LockData(Loc, LK_Shared)); + } } for (PostOrderCFGView::iterator I = SortedGraph->begin(), @@ -1587,15 +2185,16 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { int PrevBlockID = (*PI)->getBlockID(); CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; + FactSet PrevLockset; + getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock); if (!LocksetInitialized) { - CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet; + CurrBlockInfo->EntrySet = PrevLockset; LocksetInitialized = true; } else { - CurrBlockInfo->EntrySet = - intersectAndWarn(*CurrBlockInfo, CBS_Entry, - *PrevBlockInfo, CBS_Exit, - LEK_LockedSomePredecessors); + intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset, + CurrBlockInfo->EntryLoc, + LEK_LockedSomePredecessors); } } @@ -1619,23 +2218,20 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { const Stmt *Terminator = PrevBlock->getTerminator(); bool IsLoop = Terminator && isa<ContinueStmt>(Terminator); + FactSet PrevLockset; + getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, + PrevBlock, CurrBlock); + // Do not update EntrySet. - intersectAndWarn(*CurrBlockInfo, CBS_Entry, *PrevBlockInfo, CBS_Exit, + intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset, + PrevBlockInfo->ExitLoc, IsLoop ? LEK_LockedSomeLoopIterations - : LEK_LockedSomePredecessors); + : LEK_LockedSomePredecessors, + false); } } BuildLockset LocksetBuilder(this, *CurrBlockInfo); - CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), - PE = CurrBlock->pred_end(); - if (PI != PE) { - // If the predecessor ended in a branch, then process any trylocks. - // FIXME -- check to make sure there's only one predecessor. - if (Stmt *TCE = (*PI)->getTerminatorCondition()) { - LocksetBuilder.handleTrylock(TCE, *PI, CurrBlock); - } - } // Visit all the statements in the basic block. for (CFGBlock::const_iterator BI = CurrBlock->begin(), @@ -1665,7 +2261,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { break; } } - CurrBlockInfo->ExitSet = LocksetBuilder.LSet; + CurrBlockInfo->ExitSet = LocksetBuilder.FSet; // For every back edge from CurrBlock (the end of the loop) to another block // (FirstLoopBlock) we need to check that the Lockset of Block is equal to @@ -1679,19 +2275,24 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { continue; CFGBlock *FirstLoopBlock = *SI; - CFGBlockInfo &PreLoop = BlockInfo[FirstLoopBlock->getBlockID()]; - CFGBlockInfo &LoopEnd = BlockInfo[CurrBlockID]; - intersectAndWarn(LoopEnd, CBS_Exit, PreLoop, CBS_Entry, - LEK_LockedSomeLoopIterations); + CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()]; + CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID]; + intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet, + PreLoop->EntryLoc, + LEK_LockedSomeLoopIterations, + false); } } - CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()]; - CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()]; + CFGBlockInfo *Initial = &BlockInfo[CFGraph->getEntry().getBlockID()]; + CFGBlockInfo *Final = &BlockInfo[CFGraph->getExit().getBlockID()]; // FIXME: Should we call this function for all blocks which exit the function? - intersectAndWarn(Initial, CBS_Entry, Final, CBS_Exit, - LEK_LockedAtEndOfFunction); + intersectAndWarn(Initial->EntrySet, Final->ExitSet, + Final->ExitLoc, + LEK_LockedAtEndOfFunction, + LEK_NotLockedAtEndOfFunction, + false); } } // end anonymous namespace diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index 1c7e6b62f836..858be4561caf 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/PackedVector.h" #include "llvm/ADT/DenseMap.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/AnalysisContext.h" @@ -25,6 +26,8 @@ using namespace clang; +#define DEBUG_LOGGING 0 + static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) { if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() && !vd->isExceptionVariable() && @@ -95,143 +98,79 @@ static bool isAlwaysUninit(const Value v) { namespace { typedef llvm::PackedVector<Value, 2> ValueVector; -typedef std::pair<ValueVector *, ValueVector *> BVPair; class CFGBlockValues { const CFG &cfg; - BVPair *vals; + std::vector<ValueVector*> vals; ValueVector scratch; DeclToIndex declToIndex; - - ValueVector &lazyCreate(ValueVector *&bv); public: CFGBlockValues(const CFG &cfg); ~CFGBlockValues(); - + unsigned getNumEntries() const { return declToIndex.size(); } void computeSetOfDeclarations(const DeclContext &dc); - ValueVector &getValueVector(const CFGBlock *block, - const CFGBlock *dstBlock); - - BVPair &getValueVectors(const CFGBlock *block, bool shouldLazyCreate); + ValueVector &getValueVector(const CFGBlock *block) { + return *vals[block->getBlockID()]; + } + void setAllScratchValues(Value V); void mergeIntoScratch(ValueVector const &source, bool isFirst); bool updateValueVectorWithScratch(const CFGBlock *block); - bool updateValueVectors(const CFGBlock *block, const BVPair &newVals); bool hasNoDeclarations() const { return declToIndex.size() == 0; } void resetScratch(); - ValueVector &getScratch() { return scratch; } ValueVector::reference operator[](const VarDecl *vd); + + Value getValue(const CFGBlock *block, const CFGBlock *dstBlock, + const VarDecl *vd) { + const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd); + assert(idx.hasValue()); + return getValueVector(block)[idx.getValue()]; + } }; } // end anonymous namespace -CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) { - unsigned n = cfg.getNumBlockIDs(); - if (!n) - return; - vals = new std::pair<ValueVector*, ValueVector*>[n]; - memset((void*)vals, 0, sizeof(*vals) * n); -} +CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {} CFGBlockValues::~CFGBlockValues() { - unsigned n = cfg.getNumBlockIDs(); - if (n == 0) - return; - for (unsigned i = 0; i < n; ++i) { - delete vals[i].first; - delete vals[i].second; - } - delete [] vals; + for (std::vector<ValueVector*>::iterator I = vals.begin(), E = vals.end(); + I != E; ++I) + delete *I; } void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) { declToIndex.computeMap(dc); - scratch.resize(declToIndex.size()); -} - -ValueVector &CFGBlockValues::lazyCreate(ValueVector *&bv) { - if (!bv) - bv = new ValueVector(declToIndex.size()); - return *bv; -} - -/// This function pattern matches for a '&&' or '||' that appears at -/// the beginning of a CFGBlock that also (1) has a terminator and -/// (2) has no other elements. If such an expression is found, it is returned. -static const BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) { - if (block->empty()) - return 0; - - CFGElement front = block->front(); - const CFGStmt *cstmt = front.getAs<CFGStmt>(); - if (!cstmt) - return 0; - - const BinaryOperator *b = dyn_cast_or_null<BinaryOperator>(cstmt->getStmt()); - - if (!b || !b->isLogicalOp()) - return 0; - - if (block->pred_size() == 2) { - if (block->getTerminatorCondition() == b) { - if (block->succ_size() == 2) - return b; - } - else if (block->size() == 1) - return b; - } - - return 0; -} - -ValueVector &CFGBlockValues::getValueVector(const CFGBlock *block, - const CFGBlock *dstBlock) { - unsigned idx = block->getBlockID(); - if (dstBlock && getLogicalOperatorInChain(block)) { - if (*block->succ_begin() == dstBlock) - return lazyCreate(vals[idx].first); - assert(*(block->succ_begin()+1) == dstBlock); - return lazyCreate(vals[idx].second); - } - - assert(vals[idx].second == 0); - return lazyCreate(vals[idx].first); -} - -BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block, - bool shouldLazyCreate) { - unsigned idx = block->getBlockID(); - lazyCreate(vals[idx].first); - if (shouldLazyCreate) - lazyCreate(vals[idx].second); - return vals[idx]; + unsigned decls = declToIndex.size(); + scratch.resize(decls); + unsigned n = cfg.getNumBlockIDs(); + if (!n) + return; + vals.resize(n); + for (unsigned i = 0; i < n; ++i) + vals[i] = new ValueVector(decls); } -#if 0 +#if DEBUG_LOGGING static void printVector(const CFGBlock *block, ValueVector &bv, unsigned num) { - llvm::errs() << block->getBlockID() << " :"; for (unsigned i = 0; i < bv.size(); ++i) { llvm::errs() << ' ' << bv[i]; } llvm::errs() << " : " << num << '\n'; } +#endif -static void printVector(const char *name, ValueVector const &bv) { - llvm::errs() << name << " : "; - for (unsigned i = 0; i < bv.size(); ++i) { - llvm::errs() << ' ' << bv[i]; - } - llvm::errs() << "\n"; +void CFGBlockValues::setAllScratchValues(Value V) { + for (unsigned I = 0, E = scratch.size(); I != E; ++I) + scratch[I] = V; } -#endif void CFGBlockValues::mergeIntoScratch(ValueVector const &source, bool isFirst) { @@ -242,30 +181,16 @@ void CFGBlockValues::mergeIntoScratch(ValueVector const &source, } bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) { - ValueVector &dst = getValueVector(block, 0); + ValueVector &dst = getValueVector(block); bool changed = (dst != scratch); if (changed) dst = scratch; -#if 0 +#if DEBUG_LOGGING printVector(block, scratch, 0); #endif return changed; } -bool CFGBlockValues::updateValueVectors(const CFGBlock *block, - const BVPair &newVals) { - BVPair &vals = getValueVectors(block, true); - bool changed = *newVals.first != *vals.first || - *newVals.second != *vals.second; - *vals.first = *newVals.first; - *vals.second = *newVals.second; -#if 0 - printVector(block, *vals.first, 1); - printVector(block, *vals.second, 2); -#endif - return changed; -} - void CFGBlockValues::resetScratch() { scratch.reset(); } @@ -321,7 +246,7 @@ const CFGBlock *DataflowWorklist::dequeue() { } //------------------------------------------------------------------------====// -// Transfer function for uninitialized values analysis. +// Classification of DeclRefExprs as use or initialization. //====------------------------------------------------------------------------// namespace { @@ -329,106 +254,339 @@ class FindVarResult { const VarDecl *vd; const DeclRefExpr *dr; public: - FindVarResult(VarDecl *vd, DeclRefExpr *dr) : vd(vd), dr(dr) {} - + FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {} + const DeclRefExpr *getDeclRefExpr() const { return dr; } const VarDecl *getDecl() const { return vd; } }; - + +static const Expr *stripCasts(ASTContext &C, const Expr *Ex) { + while (Ex) { + Ex = Ex->IgnoreParenNoopCasts(C); + if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + if (CE->getCastKind() == CK_LValueBitCast) { + Ex = CE->getSubExpr(); + continue; + } + } + break; + } + return Ex; +} + +/// If E is an expression comprising a reference to a single variable, find that +/// variable. +static FindVarResult findVar(const Expr *E, const DeclContext *DC) { + if (const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E))) + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) + if (isTrackedVar(VD, DC)) + return FindVarResult(VD, DRE); + return FindVarResult(0, 0); +} + +/// \brief Classify each DeclRefExpr as an initialization or a use. Any +/// DeclRefExpr which isn't explicitly classified will be assumed to have +/// escaped the analysis and will be treated as an initialization. +class ClassifyRefs : public StmtVisitor<ClassifyRefs> { +public: + enum Class { + Init, + Use, + SelfInit, + Ignore + }; + +private: + const DeclContext *DC; + llvm::DenseMap<const DeclRefExpr*, Class> Classification; + + bool isTrackedVar(const VarDecl *VD) const { + return ::isTrackedVar(VD, DC); + } + + void classify(const Expr *E, Class C); + +public: + ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {} + + void VisitDeclStmt(DeclStmt *DS); + void VisitUnaryOperator(UnaryOperator *UO); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitCastExpr(CastExpr *CE); + + void operator()(Stmt *S) { Visit(S); } + + Class get(const DeclRefExpr *DRE) const { + llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I + = Classification.find(DRE); + if (I != Classification.end()) + return I->second; + + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (!VD || !isTrackedVar(VD)) + return Ignore; + + return Init; + } +}; +} + +static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) { + if (Expr *Init = VD->getInit()) { + const DeclRefExpr *DRE + = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init)); + if (DRE && DRE->getDecl() == VD) + return DRE; + } + return 0; +} + +void ClassifyRefs::classify(const Expr *E, Class C) { + FindVarResult Var = findVar(E, DC); + if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) + Classification[DRE] = std::max(Classification[DRE], C); +} + +void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + VarDecl *VD = dyn_cast<VarDecl>(*DI); + if (VD && isTrackedVar(VD)) + if (const DeclRefExpr *DRE = getSelfInitExpr(VD)) + Classification[DRE] = SelfInit; + } +} + +void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) { + // Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this + // is not a compound-assignment, we will treat it as initializing the variable + // when TransferFunctions visits it. A compound-assignment does not affect + // whether a variable is uninitialized, and there's no point counting it as a + // use. + if (BO->isCompoundAssignmentOp()) + classify(BO->getLHS(), Use); + else if (BO->getOpcode() == BO_Assign) + classify(BO->getLHS(), Ignore); +} + +void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) { + // Increment and decrement are uses despite there being no lvalue-to-rvalue + // conversion. + if (UO->isIncrementDecrementOp()) + classify(UO->getSubExpr(), Use); +} + +void ClassifyRefs::VisitCallExpr(CallExpr *CE) { + // If a value is passed by const reference to a function, we should not assume + // that it is initialized by the call, and we conservatively do not assume + // that it is used. + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) + if ((*I)->getType().isConstQualified() && (*I)->isGLValue()) + classify(*I, Ignore); +} + +void ClassifyRefs::VisitCastExpr(CastExpr *CE) { + if (CE->getCastKind() == CK_LValueToRValue) + classify(CE->getSubExpr(), Use); + else if (CStyleCastExpr *CSE = dyn_cast<CStyleCastExpr>(CE)) { + if (CSE->getType()->isVoidType()) { + // Squelch any detected load of an uninitialized value if + // we cast it to void. + // e.g. (void) x; + classify(CSE->getSubExpr(), Ignore); + } + } +} + +//------------------------------------------------------------------------====// +// Transfer function for uninitialized values analysis. +//====------------------------------------------------------------------------// + +namespace { class TransferFunctions : public StmtVisitor<TransferFunctions> { CFGBlockValues &vals; const CFG &cfg; + const CFGBlock *block; AnalysisDeclContext ∾ + const ClassifyRefs &classification; UninitVariablesHandler *handler; - - /// The last DeclRefExpr seen when analyzing a block. Used to - /// cheat when detecting cases when the address of a variable is taken. - DeclRefExpr *lastDR; - - /// The last lvalue-to-rvalue conversion of a variable whose value - /// was uninitialized. Normally this results in a warning, but it is - /// possible to either silence the warning in some cases, or we - /// propagate the uninitialized value. - CastExpr *lastLoad; - - /// For some expressions, we want to ignore any post-processing after - /// visitation. - bool skipProcessUses; - + public: TransferFunctions(CFGBlockValues &vals, const CFG &cfg, - AnalysisDeclContext &ac, + const CFGBlock *block, AnalysisDeclContext &ac, + const ClassifyRefs &classification, UninitVariablesHandler *handler) - : vals(vals), cfg(cfg), ac(ac), handler(handler), - lastDR(0), lastLoad(0), - skipProcessUses(false) {} - - void reportUninit(const DeclRefExpr *ex, const VarDecl *vd, - bool isAlwaysUninit); + : vals(vals), cfg(cfg), block(block), ac(ac), + classification(classification), handler(handler) {} + void reportUse(const Expr *ex, const VarDecl *vd); + + void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS); void VisitBlockExpr(BlockExpr *be); + void VisitCallExpr(CallExpr *ce); void VisitDeclStmt(DeclStmt *ds); void VisitDeclRefExpr(DeclRefExpr *dr); - void VisitUnaryOperator(UnaryOperator *uo); void VisitBinaryOperator(BinaryOperator *bo); - void VisitCastExpr(CastExpr *ce); - void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs); - void Visit(Stmt *s); - + bool isTrackedVar(const VarDecl *vd) { return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl())); } - - FindVarResult findBlockVarDecl(Expr *ex); - - void ProcessUses(Stmt *s = 0); -}; -} -static const Expr *stripCasts(ASTContext &C, const Expr *Ex) { - while (Ex) { - Ex = Ex->IgnoreParenNoopCasts(C); - if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { - if (CE->getCastKind() == CK_LValueBitCast) { - Ex = CE->getSubExpr(); - continue; + FindVarResult findVar(const Expr *ex) { + return ::findVar(ex, cast<DeclContext>(ac.getDecl())); + } + + UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) { + UninitUse Use(ex, isAlwaysUninit(v)); + + assert(isUninitialized(v)); + if (Use.getKind() == UninitUse::Always) + return Use; + + // If an edge which leads unconditionally to this use did not initialize + // the variable, we can say something stronger than 'may be uninitialized': + // we can say 'either it's used uninitialized or you have dead code'. + // + // We track the number of successors of a node which have been visited, and + // visit a node once we have visited all of its successors. Only edges where + // the variable might still be uninitialized are followed. Since a variable + // can't transfer from being initialized to being uninitialized, this will + // trace out the subgraph which inevitably leads to the use and does not + // initialize the variable. We do not want to skip past loops, since their + // non-termination might be correlated with the initialization condition. + // + // For example: + // + // void f(bool a, bool b) { + // block1: int n; + // if (a) { + // block2: if (b) + // block3: n = 1; + // block4: } else if (b) { + // block5: while (!a) { + // block6: do_work(&a); + // n = 2; + // } + // } + // block7: if (a) + // block8: g(); + // block9: return n; + // } + // + // Starting from the maybe-uninitialized use in block 9: + // * Block 7 is not visited because we have only visited one of its two + // successors. + // * Block 8 is visited because we've visited its only successor. + // From block 8: + // * Block 7 is visited because we've now visited both of its successors. + // From block 7: + // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all + // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively). + // * Block 3 is not visited because it initializes 'n'. + // Now the algorithm terminates, having visited blocks 7 and 8, and having + // found the frontier is blocks 2, 4, and 5. + // + // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2 + // and 4), so we report that any time either of those edges is taken (in + // each case when 'b == false'), 'n' is used uninitialized. + llvm::SmallVector<const CFGBlock*, 32> Queue; + llvm::SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0); + Queue.push_back(block); + // Specify that we've already visited all successors of the starting block. + // This has the dual purpose of ensuring we never add it to the queue, and + // of marking it as not being a candidate element of the frontier. + SuccsVisited[block->getBlockID()] = block->succ_size(); + while (!Queue.empty()) { + const CFGBlock *B = Queue.back(); + Queue.pop_back(); + for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end(); + I != E; ++I) { + const CFGBlock *Pred = *I; + if (vals.getValue(Pred, B, vd) == Initialized) + // This block initializes the variable. + continue; + + unsigned &SV = SuccsVisited[Pred->getBlockID()]; + if (!SV) { + // When visiting the first successor of a block, mark all NULL + // successors as having been visited. + for (CFGBlock::const_succ_iterator SI = Pred->succ_begin(), + SE = Pred->succ_end(); + SI != SE; ++SI) + if (!*SI) + ++SV; + } + + if (++SV == Pred->succ_size()) + // All paths from this block lead to the use and don't initialize the + // variable. + Queue.push_back(Pred); + } + } + + // Scan the frontier, looking for blocks where the variable was + // uninitialized. + for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { + const CFGBlock *Block = *BI; + unsigned BlockID = Block->getBlockID(); + const Stmt *Term = Block->getTerminator(); + if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() && + Term) { + // This block inevitably leads to the use. If we have an edge from here + // to a post-dominator block, and the variable is uninitialized on that + // edge, we have found a bug. + for (CFGBlock::const_succ_iterator I = Block->succ_begin(), + E = Block->succ_end(); I != E; ++I) { + const CFGBlock *Succ = *I; + if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() && + vals.getValue(Block, Succ, vd) == Uninitialized) { + // Switch cases are a special case: report the label to the caller + // as the 'terminator', not the switch statement itself. Suppress + // situations where no label matched: we can't be sure that's + // possible. + if (isa<SwitchStmt>(Term)) { + const Stmt *Label = Succ->getLabel(); + if (!Label || !isa<SwitchCase>(Label)) + // Might not be possible. + continue; + UninitUse::Branch Branch; + Branch.Terminator = Label; + Branch.Output = 0; // Ignored. + Use.addUninitBranch(Branch); + } else { + UninitUse::Branch Branch; + Branch.Terminator = Term; + Branch.Output = I - Block->succ_begin(); + Use.addUninitBranch(Branch); + } + } + } } } - break; - } - return Ex; -} -void TransferFunctions::reportUninit(const DeclRefExpr *ex, - const VarDecl *vd, bool isAlwaysUnit) { - if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit); + return Use; + } +}; } -FindVarResult TransferFunctions::findBlockVarDecl(Expr *ex) { - if (DeclRefExpr *dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts())) - if (VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl())) - if (isTrackedVar(vd)) - return FindVarResult(vd, dr); - return FindVarResult(0, 0); +void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) { + if (!handler) + return; + Value v = vals[vd]; + if (isUninitialized(v)) + handler->handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v)); } -void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs) { +void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) { // This represents an initialization of the 'element' value. - Stmt *element = fs->getElement(); - const VarDecl *vd = 0; - - if (DeclStmt *ds = dyn_cast<DeclStmt>(element)) { - vd = cast<VarDecl>(ds->getSingleDecl()); - if (!isTrackedVar(vd)) - vd = 0; - } else { - // Initialize the value of the reference variable. - const FindVarResult &res = findBlockVarDecl(cast<Expr>(element)); - vd = res.getDecl(); + if (DeclStmt *DS = dyn_cast<DeclStmt>(FS->getElement())) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (isTrackedVar(VD)) + vals[VD] = Initialized; } - - if (vd) - vals[vd] = Initialized; } void TransferFunctions::VisitBlockExpr(BlockExpr *be) { @@ -442,231 +600,112 @@ void TransferFunctions::VisitBlockExpr(BlockExpr *be) { vals[vd] = Initialized; continue; } - Value v = vals[vd]; - if (handler && isUninitialized(v)) - handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v)); + reportUse(be, vd); } } -void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { - // Record the last DeclRefExpr seen. This is an lvalue computation. - // We use this value to later detect if a variable "escapes" the analysis. - if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl())) - if (isTrackedVar(vd)) { - ProcessUses(); - lastDR = dr; - } -} - -void TransferFunctions::VisitDeclStmt(DeclStmt *ds) { - for (DeclStmt::decl_iterator DI = ds->decl_begin(), DE = ds->decl_end(); - DI != DE; ++DI) { - if (VarDecl *vd = dyn_cast<VarDecl>(*DI)) { - if (isTrackedVar(vd)) { - if (Expr *init = vd->getInit()) { - // If the initializer consists solely of a reference to itself, we - // explicitly mark the variable as uninitialized. This allows code - // like the following: - // - // int x = x; - // - // to deliberately leave a variable uninitialized. Different analysis - // clients can detect this pattern and adjust their reporting - // appropriately, but we need to continue to analyze subsequent uses - // of the variable. - if (init == lastLoad) { - const DeclRefExpr *DR - = cast<DeclRefExpr>(stripCasts(ac.getASTContext(), - lastLoad->getSubExpr())); - if (DR->getDecl() == vd) { - // int x = x; - // Propagate uninitialized value, but don't immediately report - // a problem. - vals[vd] = Uninitialized; - lastLoad = 0; - lastDR = 0; - if (handler) - handler->handleSelfInit(vd); - return; - } - } - - // All other cases: treat the new variable as initialized. - // This is a minor optimization to reduce the propagation - // of the analysis, since we will have already reported - // the use of the uninitialized value (which visiting the - // initializer). - vals[vd] = Initialized; - } - } - } - } +void TransferFunctions::VisitCallExpr(CallExpr *ce) { + // After a call to a function like setjmp or vfork, any variable which is + // initialized anywhere within this function may now be initialized. For now, + // just assume such a call initializes all variables. + // FIXME: Only mark variables as initialized if they have an initializer which + // is reachable from here. + Decl *Callee = ce->getCalleeDecl(); + if (Callee && Callee->hasAttr<ReturnsTwiceAttr>()) + vals.setAllScratchValues(Initialized); } -void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) { - if (bo->isAssignmentOp()) { - const FindVarResult &res = findBlockVarDecl(bo->getLHS()); - if (const VarDecl *vd = res.getDecl()) { - ValueVector::reference val = vals[vd]; - if (isUninitialized(val)) { - if (bo->getOpcode() != BO_Assign) - reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val)); - else - val = Initialized; - } - } +void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { + switch (classification.get(dr)) { + case ClassifyRefs::Ignore: + break; + case ClassifyRefs::Use: + reportUse(dr, cast<VarDecl>(dr->getDecl())); + break; + case ClassifyRefs::Init: + vals[cast<VarDecl>(dr->getDecl())] = Initialized; + break; + case ClassifyRefs::SelfInit: + if (handler) + handler->handleSelfInit(cast<VarDecl>(dr->getDecl())); + break; } } -void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) { - switch (uo->getOpcode()) { - case clang::UO_PostDec: - case clang::UO_PostInc: - case clang::UO_PreDec: - case clang::UO_PreInc: { - const FindVarResult &res = findBlockVarDecl(uo->getSubExpr()); - if (const VarDecl *vd = res.getDecl()) { - assert(res.getDeclRefExpr() == lastDR); - // We null out lastDR to indicate we have fully processed it - // and we don't want the auto-value setting in Visit(). - lastDR = 0; - - ValueVector::reference val = vals[vd]; - if (isUninitialized(val)) - reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val)); - } - break; - } - default: - break; +void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { + if (BO->getOpcode() == BO_Assign) { + FindVarResult Var = findVar(BO->getLHS()); + if (const VarDecl *VD = Var.getDecl()) + vals[VD] = Initialized; } } -void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) { - if (ce->getCastKind() == CK_LValueToRValue) { - const FindVarResult &res = findBlockVarDecl(ce->getSubExpr()); - if (res.getDecl()) { - assert(res.getDeclRefExpr() == lastDR); - lastLoad = ce; - } - } - else if (ce->getCastKind() == CK_NoOp || - ce->getCastKind() == CK_LValueBitCast) { - skipProcessUses = true; - } - else if (CStyleCastExpr *cse = dyn_cast<CStyleCastExpr>(ce)) { - if (cse->getType()->isVoidType()) { - // e.g. (void) x; - if (lastLoad == cse->getSubExpr()) { - // Squelch any detected load of an uninitialized value if - // we cast it to void. - lastLoad = 0; - lastDR = 0; +void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + VarDecl *VD = dyn_cast<VarDecl>(*DI); + if (VD && isTrackedVar(VD)) { + if (getSelfInitExpr(VD)) { + // If the initializer consists solely of a reference to itself, we + // explicitly mark the variable as uninitialized. This allows code + // like the following: + // + // int x = x; + // + // to deliberately leave a variable uninitialized. Different analysis + // clients can detect this pattern and adjust their reporting + // appropriately, but we need to continue to analyze subsequent uses + // of the variable. + vals[VD] = Uninitialized; + } else if (VD->getInit()) { + // Treat the new variable as initialized. + vals[VD] = Initialized; + } else { + // No initializer: the variable is now uninitialized. This matters + // for cases like: + // while (...) { + // int n; + // use(n); + // n = 0; + // } + // FIXME: Mark the variable as uninitialized whenever its scope is + // left, since its scope could be re-entered by a jump over the + // declaration. + vals[VD] = Uninitialized; } } } } -void TransferFunctions::Visit(clang::Stmt *s) { - skipProcessUses = false; - StmtVisitor<TransferFunctions>::Visit(s); - if (!skipProcessUses) - ProcessUses(s); -} - -void TransferFunctions::ProcessUses(Stmt *s) { - // This method is typically called after visiting a CFGElement statement - // in the CFG. We delay processing of reporting many loads of uninitialized - // values until here. - if (lastLoad) { - // If we just visited the lvalue-to-rvalue cast, there is nothing - // left to do. - if (lastLoad == s) - return; - - const DeclRefExpr *DR = - cast<DeclRefExpr>(stripCasts(ac.getASTContext(), - lastLoad->getSubExpr())); - const VarDecl *VD = cast<VarDecl>(DR->getDecl()); - - // If we reach here, we may have seen a load of an uninitialized value - // and it hasn't been casted to void or otherwise handled. In this - // situation, report the incident. - if (isUninitialized(vals[VD])) - reportUninit(DR, VD, isAlwaysUninit(vals[VD])); - - lastLoad = 0; - - if (DR == lastDR) { - lastDR = 0; - return; - } - } - - // Any other uses of 'lastDR' involve taking an lvalue of variable. - // In this case, it "escapes" the analysis. - if (lastDR && lastDR != s) { - vals[cast<VarDecl>(lastDR->getDecl())] = Initialized; - lastDR = 0; - } -} - //------------------------------------------------------------------------====// // High-level "driver" logic for uninitialized values analysis. //====------------------------------------------------------------------------// static bool runOnBlock(const CFGBlock *block, const CFG &cfg, AnalysisDeclContext &ac, CFGBlockValues &vals, + const ClassifyRefs &classification, llvm::BitVector &wasAnalyzed, UninitVariablesHandler *handler = 0) { - wasAnalyzed[block->getBlockID()] = true; - - if (const BinaryOperator *b = getLogicalOperatorInChain(block)) { - CFGBlock::const_pred_iterator itr = block->pred_begin(); - BVPair vA = vals.getValueVectors(*itr, false); - ++itr; - BVPair vB = vals.getValueVectors(*itr, false); - - BVPair valsAB; - - if (b->getOpcode() == BO_LAnd) { - // Merge the 'F' bits from the first and second. - vals.mergeIntoScratch(*(vA.second ? vA.second : vA.first), true); - vals.mergeIntoScratch(*(vB.second ? vB.second : vB.first), false); - valsAB.first = vA.first; - valsAB.second = &vals.getScratch(); - } else { - // Merge the 'T' bits from the first and second. - assert(b->getOpcode() == BO_LOr); - vals.mergeIntoScratch(*vA.first, true); - vals.mergeIntoScratch(*vB.first, false); - valsAB.first = &vals.getScratch(); - valsAB.second = vA.second ? vA.second : vA.first; - } - return vals.updateValueVectors(block, valsAB); - } - - // Default behavior: merge in values of predecessor blocks. vals.resetScratch(); + // Merge in values of predecessor blocks. bool isFirst = true; for (CFGBlock::const_pred_iterator I = block->pred_begin(), E = block->pred_end(); I != E; ++I) { const CFGBlock *pred = *I; if (wasAnalyzed[pred->getBlockID()]) { - vals.mergeIntoScratch(vals.getValueVector(pred, block), isFirst); + vals.mergeIntoScratch(vals.getValueVector(pred), isFirst); isFirst = false; } } // Apply the transfer function. - TransferFunctions tf(vals, cfg, ac, handler); + TransferFunctions tf(vals, cfg, block, ac, classification, handler); for (CFGBlock::const_iterator I = block->begin(), E = block->end(); I != E; ++I) { if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) { tf.Visit(const_cast<Stmt*>(cs->getStmt())); } } - tf.ProcessUses(); return vals.updateValueVectorWithScratch(block); } @@ -683,17 +722,16 @@ void clang::runUninitializedVariablesAnalysis( stats.NumVariablesAnalyzed = vals.getNumEntries(); + // Precompute which expressions are uses and which are initializations. + ClassifyRefs classification(ac); + cfg.VisitBlockStmts(classification); + // Mark all variables uninitialized at the entry. const CFGBlock &entry = cfg.getEntry(); - for (CFGBlock::const_succ_iterator i = entry.succ_begin(), - e = entry.succ_end(); i != e; ++i) { - if (const CFGBlock *succ = *i) { - ValueVector &vec = vals.getValueVector(&entry, succ); - const unsigned n = vals.getNumEntries(); - for (unsigned j = 0; j < n ; ++j) { - vec[j] = Uninitialized; - } - } + ValueVector &vec = vals.getValueVector(&entry); + const unsigned n = vals.getNumEntries(); + for (unsigned j = 0; j < n ; ++j) { + vec[j] = Uninitialized; } // Proceed with the workist. @@ -705,7 +743,8 @@ void clang::runUninitializedVariablesAnalysis( while (const CFGBlock *block = worklist.dequeue()) { // Did the block change? - bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed); + bool changed = runOnBlock(block, cfg, ac, vals, + classification, wasAnalyzed); ++stats.NumBlockVisits; if (changed || !previouslyVisited[block->getBlockID()]) worklist.enqueueSuccessors(block); @@ -716,7 +755,7 @@ void clang::runUninitializedVariablesAnalysis( for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { const CFGBlock *block = *BI; if (wasAnalyzed[block->getBlockID()]) { - runOnBlock(block, cfg, ac, vals, wasAnalyzed, &handler); + runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, &handler); ++stats.NumBlockVisits; } } diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index ef2e93c49cd9..73e693befa4f 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS mc) add_clang_library(clangBasic Builtins.cpp ConvertUTF.c + ConvertUTFWrapper.cpp Diagnostic.cpp DiagnosticIDs.cpp FileManager.cpp @@ -10,6 +11,7 @@ add_clang_library(clangBasic IdentifierTable.cpp LangOptions.cpp Module.cpp + ObjCRuntime.cpp SourceLocation.cpp SourceManager.cpp TargetInfo.cpp @@ -31,18 +33,19 @@ if (Subversion_FOUND AND EXISTS "${CLANG_SOURCE_DIR}/.svn") PROPERTIES COMPILE_DEFINITIONS "SVN_REVISION=\"${CLANG_WC_REVISION}\"") endif() -add_dependencies(clangBasic - ClangARMNeon - ClangAttrList - ClangDiagnosticAnalysis - ClangDiagnosticAST - ClangDiagnosticCommon - ClangDiagnosticDriver - ClangDiagnosticFrontend - ClangDiagnosticGroups - ClangDiagnosticLex - ClangDiagnosticParse - ClangDiagnosticSema - ClangDiagnosticSerialization - ClangDiagnosticIndexName) - +add_dependencies(clangBasic + ClangARMNeon + ClangAttrList + ClangDiagnosticAnalysis + ClangDiagnosticAST + ClangDiagnosticComment + ClangDiagnosticCommon + ClangDiagnosticDriver + ClangDiagnosticFrontend + ClangDiagnosticGroups + ClangDiagnosticIndexName + ClangDiagnosticLex + ClangDiagnosticParse + ClangDiagnosticSema + ClangDiagnosticSerialization + ) diff --git a/lib/Basic/ConvertUTF.c b/lib/Basic/ConvertUTF.c index e1970039e164..4793b251f603 100644 --- a/lib/Basic/ConvertUTF.c +++ b/lib/Basic/ConvertUTF.c @@ -285,6 +285,7 @@ ConversionResult ConvertUTF16toUTF8 ( *targetStart = target; return result; } +#endif /* --------------------------------------------------------------------- */ @@ -339,8 +340,6 @@ ConversionResult ConvertUTF32toUTF8 ( return result; } -#endif - /* --------------------------------------------------------------------- */ /* diff --git a/lib/Basic/ConvertUTFWrapper.cpp b/lib/Basic/ConvertUTFWrapper.cpp new file mode 100644 index 000000000000..a1b3f7fd9da3 --- /dev/null +++ b/lib/Basic/ConvertUTFWrapper.cpp @@ -0,0 +1,70 @@ +//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----=== +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/ConvertUTF.h" +#include "clang/Basic/LLVM.h" + +namespace clang { + +bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source, + char *&ResultPtr) { + assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4); + ConversionResult result = conversionOK; + // Copy the character span over. + if (WideCharWidth == 1) { + if (!isLegalUTF8String(reinterpret_cast<const UTF8*>(Source.begin()), + reinterpret_cast<const UTF8*>(Source.end()))) + result = sourceIllegal; + memcpy(ResultPtr, Source.data(), Source.size()); + ResultPtr += Source.size(); + } else if (WideCharWidth == 2) { + const UTF8 *sourceStart = (const UTF8*)Source.data(); + // FIXME: Make the type of the result buffer correct instead of + // using reinterpret_cast. + UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr); + ConversionFlags flags = strictConversion; + result = ConvertUTF8toUTF16( + &sourceStart, sourceStart + Source.size(), + &targetStart, targetStart + 2*Source.size(), flags); + if (result == conversionOK) + ResultPtr = reinterpret_cast<char*>(targetStart); + } else if (WideCharWidth == 4) { + const UTF8 *sourceStart = (const UTF8*)Source.data(); + // FIXME: Make the type of the result buffer correct instead of + // using reinterpret_cast. + UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr); + ConversionFlags flags = strictConversion; + result = ConvertUTF8toUTF32( + &sourceStart, sourceStart + Source.size(), + &targetStart, targetStart + 4*Source.size(), flags); + if (result == conversionOK) + ResultPtr = reinterpret_cast<char*>(targetStart); + } + assert((result != targetExhausted) + && "ConvertUTF8toUTFXX exhausted target buffer"); + return result == conversionOK; +} + +bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) { + const UTF32 *SourceStart = &Source; + const UTF32 *SourceEnd = SourceStart + 1; + UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr); + UTF8 *TargetEnd = TargetStart + 4; + ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd, + &TargetStart, TargetEnd, + strictConversion); + if (CR != conversionOK) + return false; + + ResultPtr = reinterpret_cast<char*>(TargetStart); + return true; +} + +} // end namespace clang + diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index f7d5d87b367e..e68950200fd0 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/CrashRecoveryContext.h" +#include <cctype> using namespace clang; @@ -48,6 +49,9 @@ DiagnosticsEngine::DiagnosticsEngine( ErrorsAsFatal = false; SuppressSystemWarnings = false; SuppressAllDiagnostics = false; + ElideType = true; + PrintTemplateTree = false; + ShowColors = false; ShowOverloads = Ovl_All; ExtBehavior = Ext_Ignore; @@ -115,7 +119,7 @@ void DiagnosticsEngine::Reset() { // Create a DiagState and DiagStatePoint representing diagnostic changes // through command-line. DiagStates.push_back(DiagState()); - PushDiagStatePoint(&DiagStates.back(), SourceLocation()); + DiagStatePoints.push_back(DiagStatePoint(&DiagStates.back(), FullSourceLoc())); } void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1, @@ -155,12 +159,6 @@ DiagnosticsEngine::GetDiagStatePointForLoc(SourceLocation L) const { return Pos; } -/// \brief This allows the client to specify that certain -/// warnings are ignored. Notes can never be mapped, errors can only be -/// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily. -/// -/// \param The source location that this change of diagnostic state should -/// take affect. It can be null if we are setting the latest state. void DiagnosticsEngine::setDiagnosticMapping(diag::kind Diag, diag::Mapping Map, SourceLocation L) { assert(Diag < diag::DIAG_UPPER_LIMIT && @@ -385,17 +383,34 @@ void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) { CurDiagID = ~0U; } -bool DiagnosticsEngine::EmitCurrentDiagnostic() { - // Process the diagnostic, sending the accumulated information to the - // DiagnosticConsumer. - bool Emitted = ProcessDiag(); +bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) { + assert(getClient() && "DiagnosticClient not set!"); + + bool Emitted; + if (Force) { + Diagnostic Info(this); + + // Figure out the diagnostic level of this message. + DiagnosticIDs::Level DiagLevel + = Diags->getDiagnosticLevel(Info.getID(), Info.getLocation(), *this); + + Emitted = (DiagLevel != DiagnosticIDs::Ignored); + if (Emitted) { + // Emit the diagnostic regardless of suppression level. + Diags->EmitDiag(*this, DiagLevel); + } + } else { + // Process the diagnostic, sending the accumulated information to the + // DiagnosticConsumer. + Emitted = ProcessDiag(); + } // Clear out the current diagnostic object. unsigned DiagID = CurDiagID; Clear(); // If there was a delayed diagnostic, emit it now. - if (DelayedDiagID && DelayedDiagID != DiagID) + if (!Force && DelayedDiagID && DelayedDiagID != DiagID) ReportDelayed(); return Emitted; @@ -666,6 +681,8 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, /// QualTypeVals - Pass a vector of arrays so that QualType names can be /// compared to see if more information is needed to be printed. SmallVector<intptr_t, 2> QualTypeVals; + SmallVector<char, 64> Tree; + for (unsigned i = 0, e = getNumArgs(); i < e; ++i) if (getArgKind(i) == DiagnosticsEngine::ak_qualtype) QualTypeVals.push_back(getRawArg(i)); @@ -717,7 +734,20 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic"); unsigned ArgNo = *DiagStr++ - '0'; + // Only used for type diffing. + unsigned ArgNo2 = ArgNo; + DiagnosticsEngine::ArgumentKind Kind = getArgKind(ArgNo); + if (Kind == DiagnosticsEngine::ak_qualtype && + ModifierIs(Modifier, ModifierLen, "diff")) { + Kind = DiagnosticsEngine::ak_qualtype_pair; + assert(*DiagStr == ',' && isdigit(*(DiagStr + 1)) && + "Invalid format for diff modifier"); + ++DiagStr; // Comma. + ArgNo2 = *DiagStr++ - '0'; + assert(getArgKind(ArgNo2) == DiagnosticsEngine::ak_qualtype && + "Second value of type diff must be a qualtype"); + } switch (Kind) { // ---- STRINGS ---- @@ -802,18 +832,91 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, FormattedArgs.data(), FormattedArgs.size(), OutStr, QualTypeVals); break; + case DiagnosticsEngine::ak_qualtype_pair: + // Create a struct with all the info needed for printing. + TemplateDiffTypes TDT; + TDT.FromType = getRawArg(ArgNo); + TDT.ToType = getRawArg(ArgNo2); + TDT.ElideType = getDiags()->ElideType; + TDT.ShowColors = getDiags()->ShowColors; + TDT.TemplateDiffUsed = false; + intptr_t val = reinterpret_cast<intptr_t>(&TDT); + + const char *ArgumentEnd = Argument + ArgumentLen; + const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|'); + + // Print the tree. If this diagnostic already has a tree, skip the + // second tree. + if (getDiags()->PrintTemplateTree && Tree.empty()) { + TDT.PrintFromType = true; + TDT.PrintTree = true; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), + FormattedArgs.size(), + Tree, QualTypeVals); + // If there is no tree information, fall back to regular printing. + if (!Tree.empty()) { + FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr); + break; + } + } + + // Non-tree printing, also the fall-back when tree printing fails. + // The fall-back is triggered when the types compared are not templates. + const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$'); + const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$'); + + // Append before text + FormatDiagnostic(Argument, FirstDollar, OutStr); + + // Append first type + TDT.PrintTree = false; + TDT.PrintFromType = true; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), FormattedArgs.size(), + OutStr, QualTypeVals); + if (!TDT.TemplateDiffUsed) + FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype, + TDT.FromType)); + + // Append middle text + FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr); + + // Append second type + TDT.PrintFromType = false; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), FormattedArgs.size(), + OutStr, QualTypeVals); + if (!TDT.TemplateDiffUsed) + FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype, + TDT.ToType)); + + // Append end text + FormatDiagnostic(SecondDollar + 1, Pipe, OutStr); + break; } // Remember this argument info for subsequent formatting operations. Turn // std::strings into a null terminated string to make it be the same case as // all the other ones. - if (Kind != DiagnosticsEngine::ak_std_string) + if (Kind == DiagnosticsEngine::ak_qualtype_pair) + continue; + else if (Kind != DiagnosticsEngine::ak_std_string) FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo))); else FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_c_string, (intptr_t)getArgStdStr(ArgNo).c_str())); } + + // Append the type tree to the end of the diagnostics. + OutStr.append(Tree.begin(), Tree.end()); } StoredDiagnostic::StoredDiagnostic() { } diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index 8c33a963c927..ca96fd2b9b24 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/lib/Basic/DiagnosticIDs.cpp @@ -79,6 +79,7 @@ static const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticLexKinds.inc" #include "clang/Basic/DiagnosticParseKinds.inc" #include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" #undef DIAG @@ -357,7 +358,7 @@ DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc, return CustomDiagInfo->getLevel(DiagID); unsigned DiagClass = getBuiltinDiagClass(DiagID); - assert(DiagClass != CLASS_NOTE && "Cannot get diagnostic level of a note!"); + if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note; return getDiagnosticLevel(DiagID, DiagClass, Loc, Diag); } @@ -583,24 +584,9 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const { assert(Diag.getClient() && "DiagnosticClient not set!"); // Figure out the diagnostic level of this message. - DiagnosticIDs::Level DiagLevel; unsigned DiagID = Info.getID(); - - if (DiagID >= diag::DIAG_UPPER_LIMIT) { - // Handle custom diagnostics, which cannot be mapped. - DiagLevel = CustomDiagInfo->getLevel(DiagID); - } else { - // Get the class of the diagnostic. If this is a NOTE, map it onto whatever - // the diagnostic level was for the previous diagnostic so that it is - // filtered the same as the previous diagnostic. - unsigned DiagClass = getBuiltinDiagClass(DiagID); - if (DiagClass == CLASS_NOTE) { - DiagLevel = DiagnosticIDs::Note; - } else { - DiagLevel = getDiagnosticLevel(DiagID, DiagClass, Info.getLocation(), - Diag); - } - } + DiagnosticIDs::Level DiagLevel + = getDiagnosticLevel(DiagID, Info.getLocation(), Diag); if (DiagLevel != DiagnosticIDs::Note) { // Record that a fatal error occurred only when we see a second @@ -658,6 +644,14 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const { } // Finally, report it. + EmitDiag(Diag, DiagLevel); + return true; +} + +void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const { + Diagnostic Info(&Diag); + assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!"); + Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info); if (Diag.Client->IncludeInDiagnosticCounts()) { if (DiagLevel == DiagnosticIDs::Warning) @@ -665,8 +659,6 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const { } Diag.CurDiagID = ~0U; - - return true; } bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const { diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp index fd6d33463a29..c6b894c7e2fe 100644 --- a/lib/Basic/FileManager.cpp +++ b/lib/Basic/FileManager.cpp @@ -111,6 +111,14 @@ public: } size_t size() const { return UniqueFiles.size(); } + + void erase(const FileEntry *Entry) { + std::string FullPath(GetFullPath(Entry->getName())); + + // Lowercase string because Windows filesystem is case insensitive. + FullPath = StringRef(FullPath).lower(); + UniqueFiles.erase(FullPath); + } }; //===----------------------------------------------------------------------===// @@ -152,6 +160,8 @@ public: } size_t size() const { return UniqueFiles.size(); } + + void erase(const FileEntry *Entry) { UniqueFiles.erase(*Entry); } }; #endif @@ -213,6 +223,10 @@ void FileManager::removeStatCache(FileSystemStatCache *statCache) { PrevCache->setNextStatCache(statCache->getNextStatCache()); } +void FileManager::clearStatCaches() { + StatCache.reset(0); +} + /// \brief Retrieve the directory that the given file name resides in. /// Filename can point to either a real file or a virtual file. static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr, @@ -259,16 +273,14 @@ void FileManager::addAncestorsAsVirtualDirs(StringRef Path) { addAncestorsAsVirtualDirs(DirName); } -/// getDirectory - Lookup, cache, and verify the specified directory -/// (real or virtual). This returns NULL if the directory doesn't -/// exist. -/// const DirectoryEntry *FileManager::getDirectory(StringRef DirName, bool CacheFailure) { - // stat doesn't like trailing separators. + // stat doesn't like trailing separators except for root directory. // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'. // (though it can strip '\\') - if (DirName.size() > 1 && llvm::sys::path::is_separator(DirName.back())) + if (DirName.size() > 1 && + DirName != llvm::sys::path::root_path(DirName) && + llvm::sys::path::is_separator(DirName.back())) DirName = DirName.substr(0, DirName.size()-1); ++NumDirLookups; @@ -315,9 +327,6 @@ const DirectoryEntry *FileManager::getDirectory(StringRef DirName, return &UDE; } -/// getFile - Lookup, cache, and verify the specified file (real or -/// virtual). This returns NULL if the file doesn't exist. -/// const FileEntry *FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) { ++NumFileLookups; @@ -483,15 +492,21 @@ void FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const { } llvm::MemoryBuffer *FileManager:: -getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) { +getBufferForFile(const FileEntry *Entry, std::string *ErrorStr, + bool isVolatile) { OwningPtr<llvm::MemoryBuffer> Result; llvm::error_code ec; + uint64_t FileSize = Entry->getSize(); + // If there's a high enough chance that the file have changed since we + // got its size, force a stat before opening it. + if (isVolatile) + FileSize = -1; + const char *Filename = Entry->getName(); // If the file is already open, use the open file descriptor. if (Entry->FD != -1) { - ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, - Entry->getSize()); + ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, FileSize); if (ErrorStr) *ErrorStr = ec.message(); @@ -503,7 +518,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) { // Otherwise, open the file. if (FileSystemOpts.WorkingDir.empty()) { - ec = llvm::MemoryBuffer::getFile(Filename, Result, Entry->getSize()); + ec = llvm::MemoryBuffer::getFile(Filename, Result, FileSize); if (ec && ErrorStr) *ErrorStr = ec.message(); return Result.take(); @@ -511,7 +526,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) { SmallString<128> FilePath(Entry->getName()); FixupRelativePath(FilePath); - ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, Entry->getSize()); + ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, FileSize); if (ec && ErrorStr) *ErrorStr = ec.message(); return Result.take(); @@ -564,6 +579,18 @@ bool FileManager::getNoncachedStatValue(StringRef Path, return ::stat(FilePath.c_str(), &StatBuf) != 0; } +void FileManager::invalidateCache(const FileEntry *Entry) { + assert(Entry && "Cannot invalidate a NULL FileEntry"); + + SeenFileEntries.erase(Entry->getName()); + + // FileEntry invalidation should not block future optimizations in the file + // caches. Possible alternatives are cache truncation (invalidate last N) or + // invalidation of the whole cache. + UniqueRealFiles.erase(Entry); +} + + void FileManager::GetUniqueIDMapping( SmallVectorImpl<const FileEntry *> &UIDToFiles) const { UIDToFiles.clear(); @@ -584,6 +611,12 @@ void FileManager::GetUniqueIDMapping( UIDToFiles[(*VFE)->getUID()] = *VFE; } +void FileManager::modifyFileEntry(FileEntry *File, + off_t Size, time_t ModificationTime) { + File->Size = Size; + File->ModTime = ModificationTime; +} + void FileManager::PrintStats() const { llvm::errs() << "\n*** File Manager Stats:\n"; diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 43899f0979b5..4869ae1056be 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" +#include <cctype> #include <cstdio> using namespace clang; @@ -103,7 +104,8 @@ namespace { KEYOPENCL = 0x200, KEYC11 = 0x400, KEYARC = 0x800, - KEYALL = 0x0fff + KEYNOMS = 0x01000, + KEYALL = (0xffff & ~KEYNOMS) // Because KEYNOMS is used to exclude. }; } @@ -136,6 +138,9 @@ static void AddKeyword(StringRef Keyword, else if (LangOpts.ObjC2 && (Flags & KEYARC)) AddResult = 2; else if (LangOpts.CPlusPlus && (Flags & KEYCXX0X)) AddResult = 3; + // Don't add this keyword under MicrosoftMode. + if (LangOpts.MicrosoftMode && (Flags & KEYNOMS)) + return; // Don't add this keyword if disabled in this language. if (AddResult == 0) return; @@ -154,8 +159,8 @@ static void AddCXXOperatorKeyword(StringRef Keyword, Info.setIsCPlusPlusOperatorKeyword(); } -/// AddObjCKeyword - Register an Objective-C @keyword like "class" "selector" or -/// "property". +/// AddObjCKeyword - Register an Objective-C \@keyword like "class" "selector" +/// or "property". static void AddObjCKeyword(StringRef Name, tok::ObjCKeywordKind ObjCID, IdentifierTable &Table) { @@ -335,22 +340,22 @@ public: unsigned Selector::getNumArgs() const { unsigned IIF = getIdentifierInfoFlag(); - if (IIF == ZeroArg) + if (IIF <= ZeroArg) return 0; if (IIF == OneArg) return 1; - // We point to a MultiKeywordSelector (pointer doesn't contain any flags). - MultiKeywordSelector *SI = reinterpret_cast<MultiKeywordSelector *>(InfoPtr); + // We point to a MultiKeywordSelector. + MultiKeywordSelector *SI = getMultiKeywordSelector(); return SI->getNumArgs(); } IdentifierInfo *Selector::getIdentifierInfoForSlot(unsigned argIndex) const { - if (getIdentifierInfoFlag()) { + if (getIdentifierInfoFlag() < MultiArg) { assert(argIndex == 0 && "illegal keyword index"); return getAsIdentifierInfo(); } - // We point to a MultiKeywordSelector (pointer doesn't contain any flags). - MultiKeywordSelector *SI = reinterpret_cast<MultiKeywordSelector *>(InfoPtr); + // We point to a MultiKeywordSelector. + MultiKeywordSelector *SI = getMultiKeywordSelector(); return SI->getIdentifierInfoForSlot(argIndex); } @@ -375,7 +380,7 @@ std::string Selector::getAsString() const { if (InfoPtr == 0) return "<null selector>"; - if (InfoPtr & ArgFlags) { + if (getIdentifierInfoFlag() < MultiArg) { IdentifierInfo *II = getAsIdentifierInfo(); // If the number of arguments is 0 then II is guaranteed to not be null. @@ -388,8 +393,8 @@ std::string Selector::getAsString() const { return II->getName().str() + ":"; } - // We have a multiple keyword selector (no embedded flags). - return reinterpret_cast<MultiKeywordSelector *>(InfoPtr)->getName(); + // We have a multiple keyword selector. + return getMultiKeywordSelector()->getName(); } /// Interpreting the given string using the normal CamelCase diff --git a/lib/Basic/ObjCRuntime.cpp b/lib/Basic/ObjCRuntime.cpp new file mode 100644 index 000000000000..9bd433a0649b --- /dev/null +++ b/lib/Basic/ObjCRuntime.cpp @@ -0,0 +1,86 @@ +//===- ObjCRuntime.cpp - Objective-C Runtime Handling -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the ObjCRuntime class, which represents the +// target Objective-C runtime. +// +//===----------------------------------------------------------------------===// +#include "clang/Basic/ObjCRuntime.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +std::string ObjCRuntime::getAsString() const { + std::string Result; + { + llvm::raw_string_ostream Out(Result); + Out << *this; + } + return Result; +} + +raw_ostream &clang::operator<<(raw_ostream &out, const ObjCRuntime &value) { + switch (value.getKind()) { + case ObjCRuntime::MacOSX: out << "macosx"; break; + case ObjCRuntime::FragileMacOSX: out << "macosx-fragile"; break; + case ObjCRuntime::iOS: out << "ios"; break; + case ObjCRuntime::GNUstep: out << "gnustep"; break; + case ObjCRuntime::GCC: out << "gcc"; break; + case ObjCRuntime::ObjFW: out << "objfw"; break; + } + if (value.getVersion() > VersionTuple(0)) { + out << '-' << value.getVersion(); + } + return out; +} + +bool ObjCRuntime::tryParse(StringRef input) { + // Look for the last dash. + std::size_t dash = input.rfind('-'); + + // We permit dashes in the runtime name, and we also permit the + // version to be omitted, so if we see a dash not followed by a + // digit then we need to ignore it. + if (dash != StringRef::npos && dash + 1 != input.size() && + (input[dash+1] < '0' || input[dash+1] > '9')) { + dash = StringRef::npos; + } + + // Everything prior to that must be a valid string name. + Kind kind; + StringRef runtimeName = input.substr(0, dash); + Version = VersionTuple(0); + if (runtimeName == "macosx") { + kind = ObjCRuntime::MacOSX; + } else if (runtimeName == "macosx-fragile") { + kind = ObjCRuntime::FragileMacOSX; + } else if (runtimeName == "ios") { + kind = ObjCRuntime::iOS; + } else if (runtimeName == "gnustep") { + // If no version is specified then default to the most recent one that we + // know about. + Version = VersionTuple(1, 6); + kind = ObjCRuntime::GNUstep; + } else if (runtimeName == "gcc") { + kind = ObjCRuntime::GCC; + } else if (runtimeName == "objfw") { + kind = ObjCRuntime::ObjFW; + } else { + return true; + } + TheKind = kind; + + if (dash != StringRef::npos) { + StringRef verString = input.substr(dash + 1); + if (Version.tryParse(verString)) + return true; + } + + return false; +} diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index cef091c5983b..9ec247429945 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -71,7 +71,7 @@ unsigned ContentCache::getSize() const { void ContentCache::replaceBuffer(const llvm::MemoryBuffer *B, bool DoNotFree) { - if (B == Buffer.getPointer()) { + if (B && B == Buffer.getPointer()) { assert(0 && "Replacing with the same buffer"); Buffer.setInt(DoNotFree? DoNotFreeFlag : 0); return; @@ -97,7 +97,10 @@ const llvm::MemoryBuffer *ContentCache::getBuffer(DiagnosticsEngine &Diag, } std::string ErrorStr; - Buffer.setPointer(SM.getFileManager().getBufferForFile(ContentsEntry, &ErrorStr)); + bool isVolatile = SM.userFilesAreVolatile() && !IsSystemFile; + Buffer.setPointer(SM.getFileManager().getBufferForFile(ContentsEntry, + &ErrorStr, + isVolatile)); // If we were unable to open the file, then we are in an inconsistent // situation where the content cache referenced a file which no longer @@ -189,9 +192,9 @@ unsigned LineTableInfo::getLineTableFilenameID(StringRef Name) { } /// AddLineNote - Add a line note to the line table that indicates that there -/// is a #line at the specified FID/Offset location which changes the presumed +/// is a \#line at the specified FID/Offset location which changes the presumed /// location to LineNo/FilenameID. -void LineTableInfo::AddLineNote(int FID, unsigned Offset, +void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, unsigned LineNo, int FilenameID) { std::vector<LineEntry> &Entries = LineEntries[FID]; @@ -219,10 +222,10 @@ void LineTableInfo::AddLineNote(int FID, unsigned Offset, /// AddLineNote This is the same as the previous version of AddLineNote, but is /// used for GNU line markers. If EntryExit is 0, then this doesn't change the -/// presumed #include stack. If it is 1, this is a file entry, if it is 2 then +/// presumed \#include stack. If it is 1, this is a file entry, if it is 2 then /// this is a file exit. FileKind specifies whether this is a system header or /// extern C system header. -void LineTableInfo::AddLineNote(int FID, unsigned Offset, +void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, unsigned LineNo, int FilenameID, unsigned EntryExit, SrcMgr::CharacteristicKind FileKind) { @@ -256,7 +259,7 @@ void LineTableInfo::AddLineNote(int FID, unsigned Offset, /// FindNearestLineEntry - Find the line entry nearest to FID that is before /// it. If there is no line entry before Offset in FID, return null. -const LineEntry *LineTableInfo::FindNearestLineEntry(int FID, +const LineEntry *LineTableInfo::FindNearestLineEntry(FileID FID, unsigned Offset) { const std::vector<LineEntry> &Entries = LineEntries[FID]; assert(!Entries.empty() && "No #line entries for this FID after all!"); @@ -275,7 +278,7 @@ const LineEntry *LineTableInfo::FindNearestLineEntry(int FID, /// \brief Add a new line entry that has already been encoded into /// the internal representation of the line table. -void LineTableInfo::AddEntry(int FID, +void LineTableInfo::AddEntry(FileID FID, const std::vector<LineEntry> &Entries) { LineEntries[FID] = Entries; } @@ -308,7 +311,7 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, if (LineTable == 0) LineTable = new LineTableInfo(); - LineTable->AddLineNote(LocInfo.first.ID, LocInfo.second, LineNo, FilenameID); + LineTable->AddLineNote(LocInfo.first, LocInfo.second, LineNo, FilenameID); } /// AddLineNote - Add a GNU line marker to the line table. @@ -353,7 +356,7 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, else if (IsFileExit) EntryExit = 2; - LineTable->AddLineNote(LocInfo.first.ID, LocInfo.second, LineNo, FilenameID, + LineTable->AddLineNote(LocInfo.first, LocInfo.second, LineNo, FilenameID, EntryExit, FileKind); } @@ -367,8 +370,10 @@ LineTableInfo &SourceManager::getLineTable() { // Private 'Create' methods. //===----------------------------------------------------------------------===// -SourceManager::SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr) +SourceManager::SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr, + bool UserFilesAreVolatile) : Diag(Diag), FileMgr(FileMgr), OverridenFilesKeepOriginalName(true), + UserFilesAreVolatile(UserFilesAreVolatile), ExternalSLocEntries(0), LineTable(0), NumLinearScans(0), NumBinaryProbes(0), FakeBufferForRecovery(0), FakeContentCacheForRecovery(0) { @@ -426,7 +431,8 @@ void SourceManager::clearIDTables() { /// getOrCreateContentCache - Create or return a cached ContentCache for the /// specified file. const ContentCache * -SourceManager::getOrCreateContentCache(const FileEntry *FileEnt) { +SourceManager::getOrCreateContentCache(const FileEntry *FileEnt, + bool isSystemFile) { assert(FileEnt && "Didn't specify a file entry to use?"); // Do we already have information about this file? @@ -440,16 +446,22 @@ SourceManager::getOrCreateContentCache(const FileEntry *FileEnt) { EntryAlign = std::max(8U, EntryAlign); Entry = ContentCacheAlloc.Allocate<ContentCache>(1, EntryAlign); - // If the file contents are overridden with contents from another file, - // pass that file to ContentCache. - llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator - overI = OverriddenFiles.find(FileEnt); - if (overI == OverriddenFiles.end()) + if (OverriddenFilesInfo) { + // If the file contents are overridden with contents from another file, + // pass that file to ContentCache. + llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator + overI = OverriddenFilesInfo->OverriddenFiles.find(FileEnt); + if (overI == OverriddenFilesInfo->OverriddenFiles.end()) + new (Entry) ContentCache(FileEnt); + else + new (Entry) ContentCache(OverridenFilesKeepOriginalName ? FileEnt + : overI->second, + overI->second); + } else { new (Entry) ContentCache(FileEnt); - else - new (Entry) ContentCache(OverridenFilesKeepOriginalName ? FileEnt - : overI->second, - overI->second); + } + + Entry->IsSystemFile = isSystemFile; return Entry; } @@ -622,6 +634,8 @@ void SourceManager::overrideFileContents(const FileEntry *SourceFile, const_cast<SrcMgr::ContentCache *>(IR)->replaceBuffer(Buffer, DoNotFree); const_cast<SrcMgr::ContentCache *>(IR)->BufferOverridden = true; + + getOverriddenFilesInfo().OverriddenFilesWithBuffer.insert(SourceFile); } void SourceManager::overrideFileContents(const FileEntry *SourceFile, @@ -632,7 +646,20 @@ void SourceManager::overrideFileContents(const FileEntry *SourceFile, assert(FileInfos.count(SourceFile) == 0 && "This function should be called at the initialization stage, before " "any parsing occurs."); - OverriddenFiles[SourceFile] = NewFile; + getOverriddenFilesInfo().OverriddenFiles[SourceFile] = NewFile; +} + +void SourceManager::disableFileContentsOverride(const FileEntry *File) { + if (!isFileOverridden(File)) + return; + + const SrcMgr::ContentCache *IR = getOrCreateContentCache(File); + const_cast<SrcMgr::ContentCache *>(IR)->replaceBuffer(0); + const_cast<SrcMgr::ContentCache *>(IR)->ContentsEntry = IR->OrigEntry; + + assert(OverriddenFilesInfo); + OverriddenFilesInfo->OverriddenFiles.erase(File); + OverriddenFilesInfo->OverriddenFilesWithBuffer.erase(File); } StringRef SourceManager::getBufferData(FileID FID, bool *Invalid) const { @@ -995,9 +1022,10 @@ unsigned SourceManager::getColumnNumber(FileID FID, unsigned FilePos, if (MyInvalid) return 1; - if (FilePos >= MemBuf->getBufferSize()) { + // It is okay to request a position just past the end of the buffer. + if (FilePos > MemBuf->getBufferSize()) { if (Invalid) - *Invalid = MyInvalid; + *Invalid = true; return 1; } @@ -1295,7 +1323,7 @@ SourceManager::getFileCharacteristic(SourceLocation Loc) const { assert(LineTable && "Can't have linetable entries without a LineTable!"); // See if there is a #line directive before the location. const LineEntry *Entry = - LineTable->FindNearestLineEntry(LocInfo.first.ID, LocInfo.second); + LineTable->FindNearestLineEntry(LocInfo.first, LocInfo.second); // If this is before the first line marker, use the file characteristic. if (!Entry) @@ -1305,7 +1333,7 @@ SourceManager::getFileCharacteristic(SourceLocation Loc) const { } /// Return the filename or buffer identifier of the buffer the location is in. -/// Note that this name does not respect #line directives. Use getPresumedLoc +/// Note that this name does not respect \#line directives. Use getPresumedLoc /// for normal clients. const char *SourceManager::getBufferName(SourceLocation Loc, bool *Invalid) const { @@ -1316,7 +1344,7 @@ const char *SourceManager::getBufferName(SourceLocation Loc, /// getPresumedLoc - This method returns the "presumed" location of a -/// SourceLocation specifies. A "presumed location" can be modified by #line +/// SourceLocation specifies. A "presumed location" can be modified by \#line /// or GNU line marker directives. This provides a view on the data that a /// user should see in diagnostics, for example. /// @@ -1360,7 +1388,7 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const { assert(LineTable && "Can't have linetable entries without a LineTable!"); // See if there is a #line directive before this. If so, get it. if (const LineEntry *Entry = - LineTable->FindNearestLineEntry(LocInfo.first.ID, LocInfo.second)) { + LineTable->FindNearestLineEntry(LocInfo.first, LocInfo.second)) { // If the LineEntry indicates a filename, use it. if (Entry->FilenameID != -1) Filename = LineTable->getFilename(Entry->FilenameID); @@ -1834,8 +1862,6 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS, return LOffs.first < ROffs.first; } -/// PrintStats - Print statistics to stderr. -/// void SourceManager::PrintStats() const { llvm::errs() << "\n*** Source Manager Stats:\n"; llvm::errs() << FileInfos.size() << " files mapped, " << MemBufferInfos.size() @@ -1887,10 +1913,14 @@ SourceManager::MemoryBufferSizes SourceManager::getMemoryBufferSizes() const { } size_t SourceManager::getDataStructureSizes() const { - return llvm::capacity_in_bytes(MemBufferInfos) + size_t size = llvm::capacity_in_bytes(MemBufferInfos) + llvm::capacity_in_bytes(LocalSLocEntryTable) + llvm::capacity_in_bytes(LoadedSLocEntryTable) + llvm::capacity_in_bytes(SLocEntryLoaded) - + llvm::capacity_in_bytes(FileInfos) - + llvm::capacity_in_bytes(OverriddenFiles); + + llvm::capacity_in_bytes(FileInfos); + + if (OverriddenFilesInfo) + size += llvm::capacity_in_bytes(OverriddenFilesInfo->OverriddenFiles); + + return size; } diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index 8c49486b0dc3..db5941a5d921 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -47,6 +47,7 @@ TargetInfo::TargetInfo(const std::string &T) : Triple(T) { LargeArrayMinWidth = 0; LargeArrayAlign = 0; MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0; + MaxVectorAlign = 0; SizeType = UnsignedLong; PtrDiffType = SignedLong; IntMaxType = SignedLongLong; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index dd2a89a18b72..883864fd9a18 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -316,6 +316,8 @@ protected: DefineStd(Builder, "linux", Opts); Builder.defineMacro("__gnu_linux__"); Builder.defineMacro("__ELF__"); + if (Triple.getEnvironment() == llvm::Triple::ANDROIDEABI) + Builder.defineMacro("__ANDROID__", "1"); if (Opts.POSIXThreads) Builder.defineMacro("_REENTRANT"); if (Opts.CPlusPlus) @@ -371,6 +373,7 @@ public: OpenBSDTargetInfo(const std::string &triple) : OSTargetInfo<Target>(triple) { this->UserLabelPrefix = ""; + this->TLSSupported = false; llvm::Triple Triple(triple); switch (Triple.getArch()) { @@ -391,6 +394,29 @@ public: } }; +// Bitrig Target +template<typename Target> +class BitrigTargetInfo : public OSTargetInfo<Target> { +protected: + virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const { + // Bitrig defines; list based off of gcc output + + Builder.defineMacro("__Bitrig__"); + DefineStd(Builder, "unix", Opts); + Builder.defineMacro("__ELF__"); + if (Opts.POSIXThreads) + Builder.defineMacro("_REENTRANT"); + } +public: + BitrigTargetInfo(const std::string &triple) + : OSTargetInfo<Target>(triple) { + this->UserLabelPrefix = ""; + this->TLSSupported = false; + this->MCountName = "__mcount"; + } +}; + // PSP Target template<typename Target> class PSPTargetInfo : public OSTargetInfo<Target> { @@ -573,12 +599,60 @@ class PPCTargetInfo : public TargetInfo { static const Builtin::Info BuiltinInfo[]; static const char * const GCCRegNames[]; static const TargetInfo::GCCRegAlias GCCRegAliases[]; + std::string CPU; public: PPCTargetInfo(const std::string& triple) : TargetInfo(triple) { LongDoubleWidth = LongDoubleAlign = 128; LongDoubleFormat = &llvm::APFloat::PPCDoubleDouble; } + /// \brief Flags for architecture specific defines. + typedef enum { + ArchDefineNone = 0, + ArchDefineName = 1 << 0, // <name> is substituted for arch name. + ArchDefinePpcgr = 1 << 1, + ArchDefinePpcsq = 1 << 2, + ArchDefine440 = 1 << 3, + ArchDefine603 = 1 << 4, + ArchDefine604 = 1 << 5, + ArchDefinePwr4 = 1 << 6, + ArchDefinePwr6 = 1 << 7 + } ArchDefineTypes; + + virtual bool setCPU(const std::string &Name) { + bool CPUKnown = llvm::StringSwitch<bool>(Name) + .Case("generic", true) + .Case("440", true) + .Case("450", true) + .Case("601", true) + .Case("602", true) + .Case("603", true) + .Case("603e", true) + .Case("603ev", true) + .Case("604", true) + .Case("604e", true) + .Case("620", true) + .Case("g3", true) + .Case("7400", true) + .Case("g4", true) + .Case("7450", true) + .Case("g4+", true) + .Case("750", true) + .Case("970", true) + .Case("g5", true) + .Case("a2", true) + .Case("pwr6", true) + .Case("pwr7", true) + .Case("ppc", true) + .Case("ppc64", true) + .Default(false); + + if (CPUKnown) + CPU = Name; + + return CPUKnown; + } + virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { Records = BuiltinInfo; @@ -718,8 +792,6 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__POWERPC__"); if (PointerWidth == 64) { Builder.defineMacro("_ARCH_PPC64"); - Builder.defineMacro("_LP64"); - Builder.defineMacro("__LP64__"); Builder.defineMacro("__powerpc64__"); Builder.defineMacro("__ppc64__"); } else { @@ -727,7 +799,8 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts, } // Target properties. - if (getTriple().getOS() != llvm::Triple::NetBSD) + if (getTriple().getOS() != llvm::Triple::NetBSD && + getTriple().getOS() != llvm::Triple::OpenBSD) Builder.defineMacro("_BIG_ENDIAN"); Builder.defineMacro("__BIG_ENDIAN__"); @@ -742,6 +815,47 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__VEC__", "10206"); Builder.defineMacro("__ALTIVEC__"); } + + // CPU identification. + ArchDefineTypes defs = (ArchDefineTypes)llvm::StringSwitch<int>(CPU) + .Case("440", ArchDefineName) + .Case("450", ArchDefineName | ArchDefine440) + .Case("601", ArchDefineName) + .Case("602", ArchDefineName | ArchDefinePpcgr) + .Case("603", ArchDefineName | ArchDefinePpcgr) + .Case("603e", ArchDefineName | ArchDefine603 | ArchDefinePpcgr) + .Case("603ev", ArchDefineName | ArchDefine603 | ArchDefinePpcgr) + .Case("604", ArchDefineName | ArchDefinePpcgr) + .Case("604e", ArchDefineName | ArchDefine604 | ArchDefinePpcgr) + .Case("620", ArchDefineName | ArchDefinePpcgr) + .Case("7400", ArchDefineName | ArchDefinePpcgr) + .Case("7450", ArchDefineName | ArchDefinePpcgr) + .Case("750", ArchDefineName | ArchDefinePpcgr) + .Case("970", ArchDefineName | ArchDefinePwr4 | ArchDefinePpcgr + | ArchDefinePpcsq) + .Case("pwr6", ArchDefinePwr6 | ArchDefinePpcgr | ArchDefinePpcsq) + .Case("pwr7", ArchDefineName | ArchDefinePwr6 | ArchDefinePpcgr + | ArchDefinePpcsq) + .Default(ArchDefineNone); + + if (defs & ArchDefineName) + Builder.defineMacro(Twine("_ARCH_", StringRef(CPU).upper())); + if (defs & ArchDefinePpcgr) + Builder.defineMacro("_ARCH_PPCGR"); + if (defs & ArchDefinePpcsq) + Builder.defineMacro("_ARCH_PPCSQ"); + if (defs & ArchDefine440) + Builder.defineMacro("_ARCH_440"); + if (defs & ArchDefine603) + Builder.defineMacro("_ARCH_603"); + if (defs & ArchDefine604) + Builder.defineMacro("_ARCH_604"); + if (defs & (ArchDefinePwr4 | ArchDefinePwr6)) + Builder.defineMacro("_ARCH_PWR4"); + if (defs & ArchDefinePwr6) { + Builder.defineMacro("_ARCH_PWR5"); + Builder.defineMacro("_ARCH_PWR6"); + } } bool PPCTargetInfo::hasFeature(StringRef Feature) const { @@ -878,15 +992,9 @@ public: } } - virtual const char *getVAListDeclaration() const { + virtual BuiltinVaListKind getBuiltinVaListKind() const { // This is the ELF definition, and is overridden by the Darwin sub-target - return "typedef struct __va_list_tag {" - " unsigned char gpr;" - " unsigned char fpr;" - " unsigned short reserved;" - " void* overflow_arg_area;" - " void* reg_save_area;" - "} __builtin_va_list[1];"; + return TargetInfo::PowerABIBuiltinVaList; } }; } // end anonymous namespace. @@ -907,8 +1015,8 @@ public: LongDoubleFormat = &llvm::APFloat::IEEEdouble; } } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } }; } // end anonymous namespace. @@ -927,8 +1035,8 @@ public: DescriptionString = "E-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" "i64:32:64-f32:32:32-f64:64:64-v128:128:128-n32"; } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } }; @@ -944,54 +1052,40 @@ public: } // end anonymous namespace. namespace { - static const unsigned PTXAddrSpaceMap[] = { - 0, // opencl_global - 4, // opencl_local - 1 // opencl_constant + static const unsigned NVPTXAddrSpaceMap[] = { + 1, // opencl_global + 3, // opencl_local + 4, // opencl_constant + 1, // cuda_device + 4, // cuda_constant + 3, // cuda_shared }; - class PTXTargetInfo : public TargetInfo { + class NVPTXTargetInfo : public TargetInfo { static const char * const GCCRegNames[]; static const Builtin::Info BuiltinInfo[]; std::vector<llvm::StringRef> AvailableFeatures; public: - PTXTargetInfo(const std::string& triple) : TargetInfo(triple) { + NVPTXTargetInfo(const std::string& triple) : TargetInfo(triple) { BigEndian = false; TLSSupported = false; LongWidth = LongAlign = 64; - AddrSpaceMap = &PTXAddrSpaceMap; + AddrSpaceMap = &NVPTXAddrSpaceMap; // Define available target features - // These must be defined in sorted order! - AvailableFeatures.push_back("compute10"); - AvailableFeatures.push_back("compute11"); - AvailableFeatures.push_back("compute12"); - AvailableFeatures.push_back("compute13"); - AvailableFeatures.push_back("compute20"); - AvailableFeatures.push_back("double"); - AvailableFeatures.push_back("no-fma"); - AvailableFeatures.push_back("ptx20"); - AvailableFeatures.push_back("ptx21"); - AvailableFeatures.push_back("ptx22"); - AvailableFeatures.push_back("ptx23"); - AvailableFeatures.push_back("sm10"); - AvailableFeatures.push_back("sm11"); - AvailableFeatures.push_back("sm12"); - AvailableFeatures.push_back("sm13"); - AvailableFeatures.push_back("sm20"); - AvailableFeatures.push_back("sm21"); - AvailableFeatures.push_back("sm22"); - AvailableFeatures.push_back("sm23"); + // These must be defined in sorted order! + NoAsmVariants = true; } virtual void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { Builder.defineMacro("__PTX__"); + Builder.defineMacro("__NVPTX__"); } virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { Records = BuiltinInfo; - NumRecords = clang::PTX::LastTSBuiltin-Builtin::FirstTSBuiltin; + NumRecords = clang::NVPTX::LastTSBuiltin-Builtin::FirstTSBuiltin; } virtual bool hasFeature(StringRef Feature) const { - return Feature == "ptx"; + return Feature == "ptx" || Feature == "nvptx"; } virtual void getGCCRegNames(const char * const *&Names, @@ -1011,36 +1105,38 @@ namespace { // FIXME: Is this really right? return ""; } - virtual const char *getVAListDeclaration() const { + virtual BuiltinVaListKind getBuiltinVaListKind() const { // FIXME: implement - return "typedef char* __builtin_va_list;"; + return TargetInfo::CharPtrBuiltinVaList; + } + virtual bool setCPU(const std::string &Name) { + return Name == "sm_10" || Name == "sm_13" || Name == "sm_20"; } - virtual bool setFeatureEnabled(llvm::StringMap<bool> &Features, StringRef Name, bool Enabled) const; }; - const Builtin::Info PTXTargetInfo::BuiltinInfo[] = { + const Builtin::Info NVPTXTargetInfo::BuiltinInfo[] = { #define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ ALL_LANGUAGES }, -#include "clang/Basic/BuiltinsPTX.def" +#include "clang/Basic/BuiltinsNVPTX.def" }; - const char * const PTXTargetInfo::GCCRegNames[] = { + const char * const NVPTXTargetInfo::GCCRegNames[] = { "r0" }; - void PTXTargetInfo::getGCCRegNames(const char * const *&Names, + void NVPTXTargetInfo::getGCCRegNames(const char * const *&Names, unsigned &NumNames) const { Names = GCCRegNames; NumNames = llvm::array_lengthof(GCCRegNames); } - bool PTXTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, - StringRef Name, - bool Enabled) const { + bool NVPTXTargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, + StringRef Name, + bool Enabled) const { if(std::binary_search(AvailableFeatures.begin(), AvailableFeatures.end(), Name)) { Features[Name] = Enabled; @@ -1050,24 +1146,28 @@ namespace { } } - class PTX32TargetInfo : public PTXTargetInfo { + class NVPTX32TargetInfo : public NVPTXTargetInfo { public: - PTX32TargetInfo(const std::string& triple) : PTXTargetInfo(triple) { + NVPTX32TargetInfo(const std::string& triple) : NVPTXTargetInfo(triple) { PointerWidth = PointerAlign = 32; - SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedInt; + SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedInt; DescriptionString - = "e-p:32:32-i64:64:64-f64:64:64-n1:8:16:32:64"; - } + = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-" + "f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-" + "n16:32:64"; + } }; - class PTX64TargetInfo : public PTXTargetInfo { + class NVPTX64TargetInfo : public NVPTXTargetInfo { public: - PTX64TargetInfo(const std::string& triple) : PTXTargetInfo(triple) { + NVPTX64TargetInfo(const std::string& triple) : NVPTXTargetInfo(triple) { PointerWidth = PointerAlign = 64; - SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedLongLong; + SizeType = PtrDiffType = IntPtrType = TargetInfo::UnsignedLongLong; DescriptionString - = "e-p:64:64-i64:64:64-f64:64:64-n1:8:16:32:64"; - } + = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-" + "f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-" + "n16:32:64"; + } }; } @@ -1096,8 +1196,8 @@ public: return Feature == "mblaze"; } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } virtual const char *getTargetPrefix() const { return "mblaze"; @@ -1245,11 +1345,16 @@ class X86TargetInfo : public TargetInfo { } MMX3DNowLevel; bool HasAES; + bool HasPCLMUL; bool HasLZCNT; + bool HasRDRND; bool HasBMI; bool HasBMI2; bool HasPOPCNT; + bool HasSSE4a; bool HasFMA4; + bool HasFMA; + bool HasXOP; /// \brief Enumeration of all of the X86 CPUs supported by Clang. /// @@ -1394,8 +1499,9 @@ class X86TargetInfo : public TargetInfo { public: X86TargetInfo(const std::string& triple) : TargetInfo(triple), SSELevel(NoSSE), MMX3DNowLevel(NoMMX3DNow), - HasAES(false), HasLZCNT(false), HasBMI(false), HasBMI2(false), - HasPOPCNT(false), HasFMA4(false), CPU(CK_Generic) { + HasAES(false), HasPCLMUL(false), HasLZCNT(false), HasRDRND(false), + HasBMI(false), HasBMI2(false), HasPOPCNT(false), HasSSE4a(false), + HasFMA4(false), HasFMA(false), HasXOP(false), CPU(CK_Generic) { BigEndian = false; LongDoubleFormat = &llvm::APFloat::x87DoubleExtended; } @@ -1577,13 +1683,17 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const { Features["sse42"] = false; Features["sse4a"] = false; Features["aes"] = false; + Features["pclmul"] = false; Features["avx"] = false; Features["avx2"] = false; Features["lzcnt"] = false; + Features["rdrand"] = false; Features["bmi"] = false; Features["bmi2"] = false; Features["popcnt"] = false; Features["fma4"] = false; + Features["fma"] = false; + Features["xop"] = false; // FIXME: This *really* should not be here. @@ -1637,23 +1747,30 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const { case CK_Corei7: setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse4", true); - setFeatureEnabled(Features, "aes", true); break; case CK_Corei7AVX: + setFeatureEnabled(Features, "mmx", true); + setFeatureEnabled(Features, "avx", true); + setFeatureEnabled(Features, "aes", true); + setFeatureEnabled(Features, "pclmul", true); + break; case CK_CoreAVXi: setFeatureEnabled(Features, "mmx", true); - setFeatureEnabled(Features, "sse4", true); + setFeatureEnabled(Features, "avx", true); setFeatureEnabled(Features, "aes", true); - //setFeatureEnabled(Features, "avx", true); + setFeatureEnabled(Features, "pclmul", true); + setFeatureEnabled(Features, "rdrnd", true); break; case CK_CoreAVX2: setFeatureEnabled(Features, "mmx", true); - setFeatureEnabled(Features, "sse4", true); + setFeatureEnabled(Features, "avx2", true); setFeatureEnabled(Features, "aes", true); + setFeatureEnabled(Features, "pclmul", true); setFeatureEnabled(Features, "lzcnt", true); + setFeatureEnabled(Features, "rdrnd", true); setFeatureEnabled(Features, "bmi", true); setFeatureEnabled(Features, "bmi2", true); - //setFeatureEnabled(Features, "avx2", true); + setFeatureEnabled(Features, "fma", true); break; case CK_K6: case CK_WinChipC6: @@ -1697,11 +1814,13 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap<bool> &Features) const { case CK_BTVER1: setFeatureEnabled(Features, "ssse3", true); setFeatureEnabled(Features, "sse4a", true); + break; case CK_BDVER1: case CK_BDVER2: - setFeatureEnabled(Features, "sse4", true); - setFeatureEnabled(Features, "sse4a", true); + setFeatureEnabled(Features, "avx", true); + setFeatureEnabled(Features, "xop", true); setFeatureEnabled(Features, "aes", true); + setFeatureEnabled(Features, "pclmul", true); break; case CK_C3_2: setFeatureEnabled(Features, "mmx", true); @@ -1716,7 +1835,8 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, // FIXME: This *really* should not be here. We need some way of translating // options into llvm subtarget features. if (!Features.count(Name) && - (Name != "sse4" && Name != "sse4.2" && Name != "sse4.1")) + (Name != "sse4" && Name != "sse4.2" && Name != "sse4.1" && + Name != "rdrnd")) return false; // FIXME: this should probably use a switch with fall through. @@ -1746,7 +1866,9 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, else if (Name == "3dnowa") Features["mmx"] = Features["3dnow"] = Features["3dnowa"] = true; else if (Name == "aes") - Features["aes"] = true; + Features["sse"] = Features["sse2"] = Features["aes"] = true; + else if (Name == "pclmul") + Features["sse"] = Features["sse2"] = Features["pclmul"] = true; else if (Name == "avx") Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = @@ -1755,15 +1877,27 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = Features["popcnt"] = Features["avx"] = Features["avx2"] = true; + else if (Name == "fma") + Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = + Features["ssse3"] = Features["sse41"] = Features["sse42"] = + Features["popcnt"] = Features["avx"] = Features["fma"] = true; else if (Name == "fma4") Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = - Features["popcnt"] = Features["avx"] = Features["fma4"] = true; + Features["popcnt"] = Features["avx"] = Features["sse4a"] = + Features["fma4"] = true; + else if (Name == "xop") + Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = + Features["ssse3"] = Features["sse41"] = Features["sse42"] = + Features["popcnt"] = Features["avx"] = Features["sse4a"] = + Features["fma4"] = Features["xop"] = true; else if (Name == "sse4a") Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = - Features["lzcnt"] = Features["popcnt"] = Features["sse4a"] = true; + Features["sse4a"] = true; else if (Name == "lzcnt") Features["lzcnt"] = true; + else if (Name == "rdrnd") + Features["rdrand"] = true; else if (Name == "bmi") Features["bmi"] = true; else if (Name == "bmi2") @@ -1776,33 +1910,50 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, else if (Name == "sse") Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = - Features["sse4a"] = false; + Features["sse4a"] = Features["avx"] = Features["avx2"] = + Features["fma"] = Features["fma4"] = Features["aes"] = + Features["pclmul"] = Features["xop"] = false; else if (Name == "sse2") Features["sse2"] = Features["sse3"] = Features["ssse3"] = - Features["sse41"] = Features["sse42"] = Features["sse4a"] = false; + Features["sse41"] = Features["sse42"] = Features["sse4a"] = + Features["avx"] = Features["avx2"] = Features["fma"] = + Features["fma4"] = Features["aes"] = Features["pclmul"] = + Features["xop"] = false; else if (Name == "sse3") Features["sse3"] = Features["ssse3"] = Features["sse41"] = - Features["sse42"] = Features["sse4a"] = false; + Features["sse42"] = Features["sse4a"] = Features["avx"] = + Features["avx2"] = Features["fma"] = Features["fma4"] = + Features["xop"] = false; else if (Name == "ssse3") - Features["ssse3"] = Features["sse41"] = Features["sse42"] = false; + Features["ssse3"] = Features["sse41"] = Features["sse42"] = + Features["avx"] = Features["avx2"] = Features["fma"] = false; else if (Name == "sse4" || Name == "sse4.1") - Features["sse41"] = Features["sse42"] = false; + Features["sse41"] = Features["sse42"] = Features["avx"] = + Features["avx2"] = Features["fma"] = false; else if (Name == "sse4.2") - Features["sse42"] = false; + Features["sse42"] = Features["avx"] = Features["avx2"] = + Features["fma"] = false; else if (Name == "3dnow") Features["3dnow"] = Features["3dnowa"] = false; else if (Name == "3dnowa") Features["3dnowa"] = false; else if (Name == "aes") Features["aes"] = false; + else if (Name == "pclmul") + Features["pclmul"] = false; else if (Name == "avx") - Features["avx"] = Features["avx2"] = Features["fma4"] = false; + Features["avx"] = Features["avx2"] = Features["fma"] = + Features["fma4"] = Features["xop"] = false; else if (Name == "avx2") Features["avx2"] = false; + else if (Name == "fma") + Features["fma"] = false; else if (Name == "sse4a") - Features["sse4a"] = false; + Features["sse4a"] = Features["fma4"] = Features["xop"] = false; else if (Name == "lzcnt") Features["lzcnt"] = false; + else if (Name == "rdrnd") + Features["rdrand"] = false; else if (Name == "bmi") Features["bmi"] = false; else if (Name == "bmi2") @@ -1810,7 +1961,9 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, else if (Name == "popcnt") Features["popcnt"] = false; else if (Name == "fma4") - Features["fma4"] = false; + Features["fma4"] = Features["xop"] = false; + else if (Name == "xop") + Features["xop"] = false; } return true; @@ -1832,11 +1985,21 @@ void X86TargetInfo::HandleTargetFeatures(std::vector<std::string> &Features) { continue; } + if (Feature == "pclmul") { + HasPCLMUL = true; + continue; + } + if (Feature == "lzcnt") { HasLZCNT = true; continue; } + if (Feature == "rdrand") { + HasRDRND = true; + continue; + } + if (Feature == "bmi") { HasBMI = true; continue; @@ -1852,11 +2015,26 @@ void X86TargetInfo::HandleTargetFeatures(std::vector<std::string> &Features) { continue; } + if (Feature == "sse4a") { + HasSSE4a = true; + continue; + } + if (Feature == "fma4") { HasFMA4 = true; continue; } + if (Feature == "fma") { + HasFMA = true; + continue; + } + + if (Feature == "xop") { + HasXOP = true; + continue; + } + assert(Features[i][0] == '+' && "Invalid target feature!"); X86SSEEnum Level = llvm::StringSwitch<X86SSEEnum>(Feature) .Case("avx2", AVX2) @@ -1894,10 +2072,6 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { // Target identification. if (PointerWidth == 64) { - if (getLongWidth() == 64) { - Builder.defineMacro("_LP64"); - Builder.defineMacro("__LP64__"); - } Builder.defineMacro("__amd64__"); Builder.defineMacro("__amd64"); Builder.defineMacro("__x86_64"); @@ -2039,9 +2213,15 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, if (HasAES) Builder.defineMacro("__AES__"); + if (HasPCLMUL) + Builder.defineMacro("__PCLMUL__"); + if (HasLZCNT) Builder.defineMacro("__LZCNT__"); + if (HasRDRND) + Builder.defineMacro("__RDRND__"); + if (HasBMI) Builder.defineMacro("__BMI__"); @@ -2051,9 +2231,18 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, if (HasPOPCNT) Builder.defineMacro("__POPCNT__"); + if (HasSSE4a) + Builder.defineMacro("__SSE4A__"); + if (HasFMA4) Builder.defineMacro("__FMA4__"); + if (HasFMA) + Builder.defineMacro("__FMA__"); + + if (HasXOP) + Builder.defineMacro("__XOP__"); + // Each case falls through to the previous one here. switch (SSELevel) { case AVX2: @@ -2117,11 +2306,14 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const { .Case("avx2", SSELevel >= AVX2) .Case("bmi", HasBMI) .Case("bmi2", HasBMI2) + .Case("fma", HasFMA) .Case("fma4", HasFMA4) .Case("lzcnt", HasLZCNT) + .Case("rdrnd", HasRDRND) .Case("mm3dnow", MMX3DNowLevel >= AMD3DNow) .Case("mm3dnowa", MMX3DNowLevel >= AMD3DNowAthlon) .Case("mmx", MMX3DNowLevel >= MMX) + .Case("pclmul", HasPCLMUL) .Case("popcnt", HasPOPCNT) .Case("sse", SSELevel >= SSE1) .Case("sse2", SSELevel >= SSE2) @@ -2129,9 +2321,11 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const { .Case("ssse3", SSELevel >= SSSE3) .Case("sse41", SSELevel >= SSE41) .Case("sse42", SSELevel >= SSE42) + .Case("sse4a", HasSSE4a) .Case("x86", true) .Case("x86_32", PointerWidth == 32) .Case("x86_64", PointerWidth == 64) + .Case("xop", HasXOP) .Default(false); } @@ -2227,8 +2421,8 @@ public: // MaxAtomicInlineWidth. (cmpxchg8b is an i586 instruction.) MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } int getEHDataRegisterNumber(unsigned RegNo) const { @@ -2266,6 +2460,18 @@ public: } // end anonymous namespace namespace { +class BitrigI386TargetInfo : public BitrigTargetInfo<X86_32TargetInfo> { +public: + BitrigI386TargetInfo(const std::string& triple) : + BitrigTargetInfo<X86_32TargetInfo>(triple) { + SizeType = UnsignedLong; + IntPtrType = SignedLong; + PtrDiffType = SignedLong; + } +}; +} // end anonymous namespace + +namespace { class DarwinI386TargetInfo : public DarwinTargetInfo<X86_32TargetInfo> { public: DarwinI386TargetInfo(const std::string& triple) : @@ -2273,6 +2479,7 @@ public: LongDoubleWidth = 128; LongDoubleAlign = 128; SuitableAlign = 128; + MaxVectorAlign = 256; SizeType = UnsignedLong; IntPtrType = SignedLong; DescriptionString = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" @@ -2487,14 +2694,8 @@ public: MaxAtomicPromoteWidth = 128; MaxAtomicInlineWidth = 64; } - virtual const char *getVAListDeclaration() const { - return "typedef struct __va_list_tag {" - " unsigned gp_offset;" - " unsigned fp_offset;" - " void* overflow_arg_area;" - " void* reg_save_area;" - "} __va_list_tag;" - "typedef __va_list_tag __builtin_va_list[1];"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::X86_64ABIBuiltinVaList; } int getEHDataRegisterNumber(unsigned RegNo) const { @@ -2528,8 +2729,8 @@ public: WindowsTargetInfo<X86_64TargetInfo>::getTargetDefines(Opts, Builder); Builder.defineMacro("_WIN64"); } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } }; } // end anonymous namespace @@ -2586,6 +2787,7 @@ public: DarwinX86_64TargetInfo(const std::string& triple) : DarwinTargetInfo<X86_64TargetInfo>(triple) { Int64Type = SignedLongLong; + MaxVectorAlign = 256; } }; } // end anonymous namespace @@ -2603,6 +2805,18 @@ public: } // end anonymous namespace namespace { +class BitrigX86_64TargetInfo : public BitrigTargetInfo<X86_64TargetInfo> { +public: + BitrigX86_64TargetInfo(const std::string& triple) + : BitrigTargetInfo<X86_64TargetInfo>(triple) { + IntMaxType = SignedLongLong; + UIntMaxType = UnsignedLongLong; + Int64Type = SignedLongLong; + } +}; +} // end anonymous namespace + +namespace { class ARMTargetInfo : public TargetInfo { // Possible FPU choices. enum FPUMode { @@ -2860,8 +3074,8 @@ public: NumRecords = clang::ARM::LastTSBuiltin-Builtin::FirstTSBuiltin; } virtual bool isCLZForZeroUndef() const { return false; } - virtual const char *getVAListDeclaration() const { - return "typedef void* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::VoidPtrBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const; @@ -3015,8 +3229,8 @@ public: HexagonTargetInfo(const std::string& triple) : TargetInfo(triple) { BigEndian = false; DescriptionString = ("e-p:32:32:32-" - "i64:64:64-i32:32:32-" - "i16:16:16-i1:32:32-a:0:0"); + "i64:64:64-i32:32:32-i16:16:16-i1:32:32" + "f64:64:64-f32:32:32-a0:0-n32"); // {} in inline assembly are packet specifiers, not assembly variant // specifiers. @@ -3041,8 +3255,8 @@ public: return Feature == "hexagon"; } - virtual const char *getVAListDeclaration() const { - return "typedef char* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::CharPtrBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const; @@ -3057,6 +3271,7 @@ public: .Case("hexagonv2", "2") .Case("hexagonv3", "3") .Case("hexagonv4", "4") + .Case("hexagonv5", "5") .Default(0); } @@ -3111,6 +3326,14 @@ void HexagonTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__QDSP6_ARCH__", "4"); } } + else if(CPU == "hexagonv5") { + Builder.defineMacro("__HEXAGON_V5__"); + Builder.defineMacro("__HEXAGON_ARCH__", "5"); + if(Opts.HexagonQdsp6Compat) { + Builder.defineMacro("__QDSP6_V5__"); + Builder.defineMacro("__QDSP6_ARCH__", "5"); + } + } } const char * const HexagonTargetInfo::GCCRegNames[] = { @@ -3159,7 +3382,6 @@ class SparcV8TargetInfo : public TargetInfo { public: SparcV8TargetInfo(const std::string& triple) : TargetInfo(triple) { // FIXME: Support Sparc quad-precision long double? - BigEndian = false; DescriptionString = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" "i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32"; } @@ -3200,8 +3422,8 @@ public: unsigned &NumRecords) const { // FIXME: Implement! } - virtual const char *getVAListDeclaration() const { - return "typedef void* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::VoidPtrBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const; @@ -3344,9 +3566,9 @@ namespace { // FIXME: Is this really right? return ""; } - virtual const char *getVAListDeclaration() const { + virtual BuiltinVaListKind getBuiltinVaListKind() const { // FIXME: implement - return "typedef char* __builtin_va_list;"; + return TargetInfo::CharPtrBuiltinVaList; } }; @@ -3375,7 +3597,10 @@ namespace { static const unsigned TCEOpenCLAddrSpaceMap[] = { 3, // opencl_global 4, // opencl_local - 5 // opencl_constant + 5, // opencl_constant + 0, // cuda_device + 0, // cuda_constant + 0 // cuda_shared }; class TCETargetInfo : public TargetInfo{ @@ -3425,8 +3650,8 @@ namespace { virtual const char *getClobbers() const { return ""; } - virtual const char *getVAListDeclaration() const { - return "typedef void* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::VoidPtrBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const {} @@ -3441,9 +3666,15 @@ namespace { namespace { class MipsTargetInfoBase : public TargetInfo { + static const Builtin::Info BuiltinInfo[]; std::string CPU; - bool SoftFloat; - bool SingleFloat; + bool IsMips16; + enum MipsFloatABI { + HardFloat, SingleFloat, SoftFloat + } FloatABI; + enum DspRevEnum { + NoDSP, DSP1, DSP2 + } DspRev; protected: std::string ABI; @@ -3454,7 +3685,9 @@ public: const std::string& CPUStr) : TargetInfo(triple), CPU(CPUStr), - SoftFloat(false), SingleFloat(false), + IsMips16(false), + FloatABI(HardFloat), + DspRev(NoDSP), ABI(ABIStr) {} @@ -3471,14 +3704,35 @@ public: virtual void getArchDefines(const LangOptions &Opts, MacroBuilder &Builder) const { - if (SoftFloat) - Builder.defineMacro("__mips_soft_float", Twine(1)); - else if (SingleFloat) - Builder.defineMacro("__mips_single_float", Twine(1)); - else if (!SoftFloat && !SingleFloat) + switch (FloatABI) { + case HardFloat: Builder.defineMacro("__mips_hard_float", Twine(1)); - else - llvm_unreachable("Invalid float ABI for Mips."); + break; + case SingleFloat: + Builder.defineMacro("__mips_hard_float", Twine(1)); + Builder.defineMacro("__mips_single_float", Twine(1)); + break; + case SoftFloat: + Builder.defineMacro("__mips_soft_float", Twine(1)); + break; + } + + if (IsMips16) + Builder.defineMacro("__mips16", Twine(1)); + + switch (DspRev) { + default: + break; + case DSP1: + Builder.defineMacro("__mips_dsp_rev", Twine(1)); + Builder.defineMacro("__mips_dsp", Twine(1)); + break; + case DSP2: + Builder.defineMacro("__mips_dsp_rev", Twine(2)); + Builder.defineMacro("__mips_dspr2", Twine(1)); + Builder.defineMacro("__mips_dsp", Twine(1)); + break; + } Builder.defineMacro("_MIPS_SZPTR", Twine(getPointerWidth(0))); Builder.defineMacro("_MIPS_SZINT", Twine(getIntWidth())); @@ -3489,13 +3743,14 @@ public: MacroBuilder &Builder) const = 0; virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { - // FIXME: Implement! + Records = BuiltinInfo; + NumRecords = clang::Mips::LastTSBuiltin - Builtin::FirstTSBuiltin; } virtual bool hasFeature(StringRef Feature) const { return Feature == "mips"; } - virtual const char *getVAListDeclaration() const { - return "typedef void* __builtin_va_list;"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::VoidPtrBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const { @@ -3549,7 +3804,8 @@ public: if (Name == "soft-float" || Name == "single-float" || Name == "o32" || Name == "n32" || Name == "n64" || Name == "eabi" || Name == "mips32" || Name == "mips32r2" || - Name == "mips64" || Name == "mips64r2") { + Name == "mips64" || Name == "mips64r2" || + Name == "mips16" || Name == "dsp" || Name == "dspr2") { Features[Name] = Enabled; return true; } @@ -3557,27 +3813,39 @@ public: } virtual void HandleTargetFeatures(std::vector<std::string> &Features) { - SoftFloat = false; - SingleFloat = false; + IsMips16 = false; + FloatABI = HardFloat; + DspRev = NoDSP; for (std::vector<std::string>::iterator it = Features.begin(), ie = Features.end(); it != ie; ++it) { - if (*it == "+single-float") { - SingleFloat = true; - break; - } - - if (*it == "+soft-float") { - SoftFloat = true; - // This option is front-end specific. - // Do not need to pass it to the backend. - Features.erase(it); - break; - } + if (*it == "+single-float") + FloatABI = SingleFloat; + else if (*it == "+soft-float") + FloatABI = SoftFloat; + else if (*it == "+mips16") + IsMips16 = true; + else if (*it == "+dsp") + DspRev = std::max(DspRev, DSP1); + else if (*it == "+dspr2") + DspRev = std::max(DspRev, DSP2); } + + // Remove front-end specific option. + std::vector<std::string>::iterator it = + std::find(Features.begin(), Features.end(), "+soft-float"); + if (it != Features.end()) + Features.erase(it); } }; +const Builtin::Info MipsTargetInfoBase::BuiltinInfo[] = { +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, +#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ + ALL_LANGUAGES }, +#include "clang/Basic/BuiltinsMips.def" +}; + class Mips32TargetInfoBase : public MipsTargetInfoBase { public: Mips32TargetInfoBase(const std::string& triple) : @@ -3868,8 +4136,8 @@ public: virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { } - virtual const char *getVAListDeclaration() const { - return "typedef int __builtin_va_list[4];"; + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::PNaClABIBuiltinVaList; } virtual void getGCCRegNames(const char * const *&Names, unsigned &NumNames) const; @@ -3926,6 +4194,10 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new FreeBSDTargetInfo<ARMTargetInfo>(T); case llvm::Triple::NetBSD: return new NetBSDTargetInfo<ARMTargetInfo>(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo<ARMTargetInfo>(T); + case llvm::Triple::Bitrig: + return new BitrigTargetInfo<ARMTargetInfo>(T); case llvm::Triple::RTEMS: return new RTEMSTargetInfo<ARMTargetInfo>(T); default: @@ -3973,6 +4245,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new FreeBSDTargetInfo<Mips64EBTargetInfo>(T); case llvm::Triple::NetBSD: return new NetBSDTargetInfo<Mips64EBTargetInfo>(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo<Mips64EBTargetInfo>(T); default: return new Mips64EBTargetInfo(T); } @@ -3987,6 +4261,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new FreeBSDTargetInfo<Mips64ELTargetInfo>(T); case llvm::Triple::NetBSD: return new NetBSDTargetInfo<Mips64ELTargetInfo>(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo<Mips64ELTargetInfo>(T); default: return new Mips64ELTargetInfo(T); } @@ -4009,6 +4285,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new FreeBSDTargetInfo<PPC32TargetInfo>(T); case llvm::Triple::NetBSD: return new NetBSDTargetInfo<PPC32TargetInfo>(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo<PPC32TargetInfo>(T); case llvm::Triple::RTEMS: return new RTEMSTargetInfo<PPC32TargetInfo>(T); default: @@ -4031,10 +4309,10 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new PPC64TargetInfo(T); } - case llvm::Triple::ptx32: - return new PTX32TargetInfo(T); - case llvm::Triple::ptx64: - return new PTX64TargetInfo(T); + case llvm::Triple::nvptx: + return new NVPTX32TargetInfo(T); + case llvm::Triple::nvptx64: + return new NVPTX64TargetInfo(T); case llvm::Triple::mblaze: return new MBlazeTargetInfo(T); @@ -4049,6 +4327,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new SolarisSparcV8TargetInfo(T); case llvm::Triple::NetBSD: return new NetBSDTargetInfo<SparcV8TargetInfo>(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo<SparcV8TargetInfo>(T); case llvm::Triple::RTEMS: return new RTEMSTargetInfo<SparcV8TargetInfo>(T); default: @@ -4077,6 +4357,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new NetBSDI386TargetInfo(T); case llvm::Triple::OpenBSD: return new OpenBSDI386TargetInfo(T); + case llvm::Triple::Bitrig: + return new BitrigI386TargetInfo(T); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo<X86_32TargetInfo>(T); case llvm::Triple::Minix: @@ -4112,6 +4394,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new NetBSDTargetInfo<X86_64TargetInfo>(T); case llvm::Triple::OpenBSD: return new OpenBSDX86_64TargetInfo(T); + case llvm::Triple::Bitrig: + return new BitrigX86_64TargetInfo(T); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo<X86_64TargetInfo>(T); case llvm::Triple::Solaris: diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index 8cb238632b78..9daa30a4cad0 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -32,7 +32,7 @@ std::string getClangRepositoryPath() { // If the SVN_REPOSITORY is empty, try to use the SVN keyword. This helps us // pick up a tag in an SVN export, for example. - static StringRef SVNRepository("$URL: http://llvm.org/svn/llvm-project/cfe/branches/release_31/lib/Basic/Version.cpp $"); + static StringRef SVNRepository("$URL: http://llvm.org/svn/llvm-project/cfe/trunk/lib/Basic/Version.cpp $"); if (URL.empty()) { URL = SVNRepository.slice(SVNRepository.find(':'), SVNRepository.find("/lib/Basic")); @@ -136,8 +136,7 @@ std::string getClangFullCPPVersion() { #ifdef CLANG_VENDOR OS << CLANG_VENDOR; #endif - OS << "Clang " CLANG_VERSION_STRING " (" - << getClangFullRepositoryVersion() << ')'; + OS << "Clang " CLANG_VERSION_STRING " " << getClangFullRepositoryVersion(); return OS.str(); } diff --git a/lib/Basic/VersionTuple.cpp b/lib/Basic/VersionTuple.cpp index 77aad39cbf2c..4f479d00d6cc 100644 --- a/lib/Basic/VersionTuple.cpp +++ b/lib/Basic/VersionTuple.cpp @@ -34,3 +34,55 @@ raw_ostream& clang::operator<<(raw_ostream &Out, Out << '.' << *Subminor; return Out; } + +static bool parseInt(StringRef &input, unsigned &value) { + assert(value == 0); + if (input.empty()) return true; + + char next = input[0]; + input = input.substr(1); + if (next < '0' || next > '9') return true; + value = (unsigned) (next - '0'); + + while (!input.empty()) { + next = input[0]; + if (next < '0' || next > '9') return false; + input = input.substr(1); + value = value * 10 + (unsigned) (next - '0'); + } + + return false; +} + +bool VersionTuple::tryParse(StringRef input) { + unsigned major = 0, minor = 0, micro = 0; + + // Parse the major version, [0-9]+ + if (parseInt(input, major)) return true; + + if (input.empty()) { + *this = VersionTuple(major); + return false; + } + + // If we're not done, parse the minor version, \.[0-9]+ + if (input[0] != '.') return true; + input = input.substr(1); + if (parseInt(input, minor)) return true; + + if (input.empty()) { + *this = VersionTuple(major, minor); + return false; + } + + // If we're not done, parse the micro version, \.[0-9]+ + if (input[0] != '.') return true; + input = input.substr(1); + if (parseInt(input, micro)) return true; + + // If we have characters left over, it's an error. + if (!input.empty()) return true; + + *this = VersionTuple(major, minor, micro); + return false; +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dfb9d61ff539..206c22818b3c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Basic) add_subdirectory(Lex) add_subdirectory(Parse) add_subdirectory(AST) +add_subdirectory(ASTMatchers) add_subdirectory(Sema) add_subdirectory(CodeGen) add_subdirectory(Analysis) diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index 2853bc80a5d4..86f53803d527 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -74,31 +74,42 @@ namespace clang { unsigned UIntData; bool BoolData0; bool BoolData1; + bool InReg; - ABIArgInfo(Kind K, llvm::Type *TD=0, unsigned UI=0, - bool B0 = false, bool B1 = false, llvm::Type* P = 0) + ABIArgInfo(Kind K, llvm::Type *TD, unsigned UI, bool B0, bool B1, bool IR, + llvm::Type* P) : TheKind(K), TypeData(TD), PaddingType(P), UIntData(UI), BoolData0(B0), - BoolData1(B1) {} + BoolData1(B1), InReg(IR) {} public: ABIArgInfo() : TheKind(Direct), TypeData(0), UIntData(0) {} static ABIArgInfo getDirect(llvm::Type *T = 0, unsigned Offset = 0, llvm::Type *Padding = 0) { - return ABIArgInfo(Direct, T, Offset, false, false, Padding); + return ABIArgInfo(Direct, T, Offset, false, false, false, Padding); + } + static ABIArgInfo getDirectInReg(llvm::Type *T) { + return ABIArgInfo(Direct, T, 0, false, false, true, 0); } static ABIArgInfo getExtend(llvm::Type *T = 0) { - return ABIArgInfo(Extend, T, 0); + return ABIArgInfo(Extend, T, 0, false, false, false, 0); + } + static ABIArgInfo getExtendInReg(llvm::Type *T = 0) { + return ABIArgInfo(Extend, T, 0, false, false, true, 0); } static ABIArgInfo getIgnore() { - return ABIArgInfo(Ignore); + return ABIArgInfo(Ignore, 0, 0, false, false, false, 0); } static ABIArgInfo getIndirect(unsigned Alignment, bool ByVal = true , bool Realign = false) { - return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign); + return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, false, 0); + } + static ABIArgInfo getIndirectInReg(unsigned Alignment, bool ByVal = true + , bool Realign = false) { + return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, true, 0); } static ABIArgInfo getExpand() { - return ABIArgInfo(Expand); + return ABIArgInfo(Expand, 0, 0, false, false, false, 0); } Kind getKind() const { return TheKind; } @@ -132,6 +143,11 @@ namespace clang { TypeData = T; } + bool getInReg() const { + assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); + return InReg; + } + // Indirect accessors unsigned getIndirectAlign() const { assert(TheKind == Indirect && "Invalid kind!"); diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 2f44711b607b..0a1915b485d7 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -121,6 +121,12 @@ static void addObjCARCOptPass(const PassManagerBuilder &Builder, PassManagerBase PM.add(createObjCARCOptPass()); } +static unsigned BoundsChecking; +static void addBoundsCheckingPass(const PassManagerBuilder &Builder, + PassManagerBase &PM) { + PM.add(createBoundsCheckingPass(BoundsChecking)); +} + static void addAddressSanitizerPass(const PassManagerBuilder &Builder, PassManagerBase &PM) { PM.add(createAddressSanitizerPass()); @@ -160,6 +166,14 @@ void EmitAssemblyHelper::CreatePasses() { addObjCARCOptPass); } + if (CodeGenOpts.BoundsChecking > 0) { + BoundsChecking = CodeGenOpts.BoundsChecking; + PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate, + addBoundsCheckingPass); + PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addBoundsCheckingPass); + } + if (LangOpts.AddressSanitizer) { PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate, addAddressSanitizerPass); @@ -219,7 +233,7 @@ void EmitAssemblyHelper::CreatePasses() { CodeGenOpts.EmitGcovArcs, TargetTriple.isMacOSX())); - if (!CodeGenOpts.DebugInfo) + if (CodeGenOpts.DebugInfo == CodeGenOptions::NoDebugInfo) MPM->add(createStripSymbolsPass(true)); } @@ -324,6 +338,9 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action, Options.NoFramePointerElimNonLeaf = true; } + if (CodeGenOpts.UseInitArray) + Options.UseInitArray = true; + // Set float ABI type. if (CodeGenOpts.FloatABI == "soft" || CodeGenOpts.FloatABI == "softfp") Options.FloatABIType = llvm::FloatABI::Soft; @@ -334,6 +351,19 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action, Options.FloatABIType = llvm::FloatABI::Default; } + // Set FP fusion mode. + switch (LangOpts.getFPContractMode()) { + case LangOptions::FPC_Off: + Options.AllowFPOpFusion = llvm::FPOpFusion::Strict; + break; + case LangOptions::FPC_On: + Options.AllowFPOpFusion = llvm::FPOpFusion::Standard; + break; + case LangOptions::FPC_Fast: + Options.AllowFPOpFusion = llvm::FPOpFusion::Fast; + break; + } + Options.LessPreciseFPMADOption = CodeGenOpts.LessPreciseFPMAD; Options.NoInfsFPMath = CodeGenOpts.NoInfsFPMath; Options.NoNaNsFPMath = CodeGenOpts.NoNaNsFPMath; diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index f8c7bcd4d7bc..37ef4af24690 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -458,19 +458,23 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, } } + assert(endAlign == getLowBit(blockSize)); + // At this point, we just have to add padding if the end align still // isn't aligned right. if (endAlign < maxFieldAlign) { - CharUnits padding = maxFieldAlign - endAlign; + CharUnits newBlockSize = blockSize.RoundUpToAlignment(maxFieldAlign); + CharUnits padding = newBlockSize - blockSize; elementTypes.push_back(llvm::ArrayType::get(CGM.Int8Ty, padding.getQuantity())); - blockSize += padding; - - endAlign = getLowBit(blockSize); - assert(endAlign >= maxFieldAlign); + blockSize = newBlockSize; + endAlign = getLowBit(blockSize); // might be > maxFieldAlign } + assert(endAlign >= maxFieldAlign); + assert(endAlign == getLowBit(blockSize)); + // Slam everything else on now. This works because they have // strictly decreasing alignment and we expect that size is always a // multiple of alignment. @@ -626,7 +630,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // Using the computed layout, generate the actual block function. bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda(); llvm::Constant *blockFn - = CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo, + = CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo, CurFuncDecl, LocalDeclMap, isLambdaConv); blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy); @@ -694,7 +698,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // Compute the address of the thing we're going to move into the // block literal. llvm::Value *src; - if (ci->isNested()) { + if (BlockInfo && ci->isNested()) { // We need to use the capture from the enclosing block. const CGBlockInfo::Capture &enclosingCapture = BlockInfo->getCapture(variable); @@ -872,7 +876,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E, const FunctionType *FuncTy = FnType->castAs<FunctionType>(); const CGFunctionInfo &FnInfo = - CGM.getTypes().arrangeFunctionCall(Args, FuncTy); + CGM.getTypes().arrangeFreeFunctionCall(Args, FuncTy); // Cast the function pointer to the right type. llvm::Type *BlockFTy = CGM.getTypes().GetFunctionType(FnInfo); @@ -999,7 +1003,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, // Check if we should generate debug info for this block function. if (CGM.getModuleDebugInfo()) DebugInfo = CGM.getModuleDebugInfo(); - + CurGD = GD; + BlockInfo = &blockInfo; // Arrange for local static and local extern declarations to appear @@ -1130,15 +1135,17 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, const VarDecl *variable = ci->getVariable(); DI->EmitLocation(Builder, variable->getLocation()); - const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); - if (capture.isConstant()) { - DI->EmitDeclareOfAutoVariable(variable, LocalDeclMap[variable], - Builder); - continue; - } + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { + const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); + if (capture.isConstant()) { + DI->EmitDeclareOfAutoVariable(variable, LocalDeclMap[variable], + Builder); + continue; + } - DI->EmitDeclareOfBlockDeclRefVariable(variable, BlockPointer, - Builder, blockInfo); + DI->EmitDeclareOfBlockDeclRefVariable(variable, BlockPointer, + Builder, blockInfo); + } } } diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index 8120217ac873..a790a742c942 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -10,7 +10,7 @@ #ifndef CLANG_CODEGEN_CGBUILDER_H #define CLANG_CODEGEN_CGBUILDER_H -#include "llvm/Support/IRBuilder.h" +#include "llvm/IRBuilder.h" namespace clang { namespace CodeGen { diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index e30b5136ba40..65c782e1cfe7 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -335,6 +335,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(Builder.CreateCall(F, ArgValue)); } case Builtin::BI__builtin_object_size: { + // We rely on constant folding to deal with expressions with side effects. + assert(!E->getArg(0)->HasSideEffects(getContext()) && + "should have been constant folded"); + // We pass this builtin onto the optimizer so that it can // figure out the object size in more complex cases. llvm::Type *ResType = ConvertType(E->getType()); @@ -348,9 +352,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, CI = ConstantInt::get(Builder.getInt1Ty(), (val & 0x2) >> 1); Value *F = CGM.getIntrinsic(Intrinsic::objectsize, ResType); - return RValue::get(Builder.CreateCall2(F, - EmitScalarExpr(E->getArg(0)), - CI)); + return RValue::get(Builder.CreateCall2(F, EmitScalarExpr(E->getArg(0)),CI)); } case Builtin::BI__builtin_prefetch: { Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0)); @@ -363,6 +365,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *F = CGM.getIntrinsic(Intrinsic::prefetch); return RValue::get(Builder.CreateCall4(F, Address, RW, Locality, Data)); } + case Builtin::BI__builtin_readcyclecounter: { + Value *F = CGM.getIntrinsic(Intrinsic::readcyclecounter); + return RValue::get(Builder.CreateCall(F)); + } case Builtin::BI__builtin_trap: { Value *F = CGM.getIntrinsic(Intrinsic::trap); return RValue::get(Builder.CreateCall(F)); @@ -982,9 +988,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args.add(RValue::get(llvm::Constant::getNullValue(VoidPtrTy)), getContext().VoidPtrTy); const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFunctionCall(E->getType(), Args, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeFreeFunctionCall(E->getType(), Args, + FunctionType::ExtInfo(), + RequiredArgs::All); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName); return EmitCall(FuncInfo, Func, ReturnValueSlot(), Args); @@ -1376,8 +1382,6 @@ Value *CodeGenFunction::EmitTargetBuiltinExpr(unsigned BuiltinID, case llvm::Triple::ppc: case llvm::Triple::ppc64: return EmitPPCBuiltinExpr(BuiltinID, E); - case llvm::Triple::hexagon: - return EmitHexagonBuiltinExpr(BuiltinID, E); default: return 0; } @@ -1629,13 +1633,17 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vclz_v: case ARM::BI__builtin_neon_vclzq_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vclz, Ty); + // Generate target-independent intrinsic; also need to add second argument + // for whether or not clz of zero is undefined; on ARM it isn't. + Function *F = CGM.getIntrinsic(Intrinsic::ctlz, Ty); + Ops.push_back(Builder.getInt1(Target.isCLZForZeroUndef())); return EmitNeonCall(F, Ops, "vclz"); } case ARM::BI__builtin_neon_vcnt_v: case ARM::BI__builtin_neon_vcntq_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcnt, Ty); - return EmitNeonCall(F, Ops, "vcnt"); + // generate target-independent intrinsic + Function *F = CGM.getIntrinsic(Intrinsic::ctpop, Ty); + return EmitNeonCall(F, Ops, "vctpop"); } case ARM::BI__builtin_neon_vcvt_f16_v: { assert(Type.getEltType() == NeonTypeFlags::Float16 && !quad && @@ -2411,8 +2419,11 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, return llvm::Constant::getNullValue(ConvertType(E->getType())); } case X86::BI__builtin_ia32_movntps: + case X86::BI__builtin_ia32_movntps256: case X86::BI__builtin_ia32_movntpd: + case X86::BI__builtin_ia32_movntpd256: case X86::BI__builtin_ia32_movntdq: + case X86::BI__builtin_ia32_movntdq256: case X86::BI__builtin_ia32_movnti: { llvm::MDNode *Node = llvm::MDNode::get(getLLVMContext(), Builder.getInt32(1)); @@ -2444,1996 +2455,31 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, llvm::Function *F = CGM.getIntrinsic(ID); return Builder.CreateCall(F, Ops, name); } - } -} - - -Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID, - const CallExpr *E) { - llvm::SmallVector<Value*, 4> Ops; - - for (unsigned i = 0, e = E->getNumArgs(); i != e; i++) - Ops.push_back(EmitScalarExpr(E->getArg(i))); - - Intrinsic::ID ID = Intrinsic::not_intrinsic; - - switch (BuiltinID) { - default: return 0; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpeq: - ID = Intrinsic::hexagon_C2_cmpeq; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgt: - ID = Intrinsic::hexagon_C2_cmpgt; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgtu: - ID = Intrinsic::hexagon_C2_cmpgtu; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpeqp: - ID = Intrinsic::hexagon_C2_cmpeqp; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgtp: - ID = Intrinsic::hexagon_C2_cmpgtp; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgtup: - ID = Intrinsic::hexagon_C2_cmpgtup; break; - - case Hexagon::BI__builtin_HEXAGON_C2_bitsset: - ID = Intrinsic::hexagon_C2_bitsset; break; - - case Hexagon::BI__builtin_HEXAGON_C2_bitsclr: - ID = Intrinsic::hexagon_C2_bitsclr; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpeqi: - ID = Intrinsic::hexagon_C2_cmpeqi; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgti: - ID = Intrinsic::hexagon_C2_cmpgti; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgtui: - ID = Intrinsic::hexagon_C2_cmpgtui; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgei: - ID = Intrinsic::hexagon_C2_cmpgei; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpgeui: - ID = Intrinsic::hexagon_C2_cmpgeui; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmplt: - ID = Intrinsic::hexagon_C2_cmplt; break; - - case Hexagon::BI__builtin_HEXAGON_C2_cmpltu: - ID = Intrinsic::hexagon_C2_cmpltu; break; - - case Hexagon::BI__builtin_HEXAGON_C2_bitsclri: - ID = Intrinsic::hexagon_C2_bitsclri; break; - - case Hexagon::BI__builtin_HEXAGON_C2_and: - ID = Intrinsic::hexagon_C2_and; break; - - case Hexagon::BI__builtin_HEXAGON_C2_or: - ID = Intrinsic::hexagon_C2_or; break; - - case Hexagon::BI__builtin_HEXAGON_C2_xor: - ID = Intrinsic::hexagon_C2_xor; break; - - case Hexagon::BI__builtin_HEXAGON_C2_andn: - ID = Intrinsic::hexagon_C2_andn; break; - - case Hexagon::BI__builtin_HEXAGON_C2_not: - ID = Intrinsic::hexagon_C2_not; break; - - case Hexagon::BI__builtin_HEXAGON_C2_orn: - ID = Intrinsic::hexagon_C2_orn; break; - - case Hexagon::BI__builtin_HEXAGON_C2_pxfer_map: - ID = Intrinsic::hexagon_C2_pxfer_map; break; - - case Hexagon::BI__builtin_HEXAGON_C2_any8: - ID = Intrinsic::hexagon_C2_any8; break; - - case Hexagon::BI__builtin_HEXAGON_C2_all8: - ID = Intrinsic::hexagon_C2_all8; break; - - case Hexagon::BI__builtin_HEXAGON_C2_vitpack: - ID = Intrinsic::hexagon_C2_vitpack; break; - - case Hexagon::BI__builtin_HEXAGON_C2_mux: - ID = Intrinsic::hexagon_C2_mux; break; - - case Hexagon::BI__builtin_HEXAGON_C2_muxii: - ID = Intrinsic::hexagon_C2_muxii; break; - - case Hexagon::BI__builtin_HEXAGON_C2_muxir: - ID = Intrinsic::hexagon_C2_muxir; break; - - case Hexagon::BI__builtin_HEXAGON_C2_muxri: - ID = Intrinsic::hexagon_C2_muxri; break; - - case Hexagon::BI__builtin_HEXAGON_C2_vmux: - ID = Intrinsic::hexagon_C2_vmux; break; - - case Hexagon::BI__builtin_HEXAGON_C2_mask: - ID = Intrinsic::hexagon_C2_mask; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpbeq: - ID = Intrinsic::hexagon_A2_vcmpbeq; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpbgtu: - ID = Intrinsic::hexagon_A2_vcmpbgtu; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpheq: - ID = Intrinsic::hexagon_A2_vcmpheq; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmphgt: - ID = Intrinsic::hexagon_A2_vcmphgt; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmphgtu: - ID = Intrinsic::hexagon_A2_vcmphgtu; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpweq: - ID = Intrinsic::hexagon_A2_vcmpweq; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpwgt: - ID = Intrinsic::hexagon_A2_vcmpwgt; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vcmpwgtu: - ID = Intrinsic::hexagon_A2_vcmpwgtu; break; - - case Hexagon::BI__builtin_HEXAGON_C2_tfrpr: - ID = Intrinsic::hexagon_C2_tfrpr; break; - - case Hexagon::BI__builtin_HEXAGON_C2_tfrrp: - ID = Intrinsic::hexagon_C2_tfrrp; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_acc_sat_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_acc_sat_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_nac_sat_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_nac_sat_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_rnd_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_rnd_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_rnd_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_rnd_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_rnd_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_rnd_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_rnd_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_rnd_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_rnd_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hh_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_hl_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_lh_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s0: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_sat_rnd_ll_s1: - ID = Intrinsic::hexagon_M2_mpy_sat_rnd_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hh_s0: - ID = Intrinsic::hexagon_M2_mpyd_acc_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hh_s1: - ID = Intrinsic::hexagon_M2_mpyd_acc_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hl_s0: - ID = Intrinsic::hexagon_M2_mpyd_acc_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_hl_s1: - ID = Intrinsic::hexagon_M2_mpyd_acc_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_lh_s0: - ID = Intrinsic::hexagon_M2_mpyd_acc_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_lh_s1: - ID = Intrinsic::hexagon_M2_mpyd_acc_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_ll_s0: - ID = Intrinsic::hexagon_M2_mpyd_acc_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_acc_ll_s1: - ID = Intrinsic::hexagon_M2_mpyd_acc_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hh_s0: - ID = Intrinsic::hexagon_M2_mpyd_nac_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hh_s1: - ID = Intrinsic::hexagon_M2_mpyd_nac_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hl_s0: - ID = Intrinsic::hexagon_M2_mpyd_nac_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_hl_s1: - ID = Intrinsic::hexagon_M2_mpyd_nac_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_lh_s0: - ID = Intrinsic::hexagon_M2_mpyd_nac_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_lh_s1: - ID = Intrinsic::hexagon_M2_mpyd_nac_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_ll_s0: - ID = Intrinsic::hexagon_M2_mpyd_nac_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_nac_ll_s1: - ID = Intrinsic::hexagon_M2_mpyd_nac_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hh_s0: - ID = Intrinsic::hexagon_M2_mpyd_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hh_s1: - ID = Intrinsic::hexagon_M2_mpyd_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hl_s0: - ID = Intrinsic::hexagon_M2_mpyd_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_hl_s1: - ID = Intrinsic::hexagon_M2_mpyd_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_lh_s0: - ID = Intrinsic::hexagon_M2_mpyd_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_lh_s1: - ID = Intrinsic::hexagon_M2_mpyd_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_ll_s0: - ID = Intrinsic::hexagon_M2_mpyd_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_ll_s1: - ID = Intrinsic::hexagon_M2_mpyd_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hh_s0: - ID = Intrinsic::hexagon_M2_mpyd_rnd_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hh_s1: - ID = Intrinsic::hexagon_M2_mpyd_rnd_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hl_s0: - ID = Intrinsic::hexagon_M2_mpyd_rnd_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_hl_s1: - ID = Intrinsic::hexagon_M2_mpyd_rnd_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_lh_s0: - ID = Intrinsic::hexagon_M2_mpyd_rnd_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_lh_s1: - ID = Intrinsic::hexagon_M2_mpyd_rnd_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_ll_s0: - ID = Intrinsic::hexagon_M2_mpyd_rnd_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyd_rnd_ll_s1: - ID = Intrinsic::hexagon_M2_mpyd_rnd_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hh_s0: - ID = Intrinsic::hexagon_M2_mpyu_acc_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hh_s1: - ID = Intrinsic::hexagon_M2_mpyu_acc_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hl_s0: - ID = Intrinsic::hexagon_M2_mpyu_acc_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_hl_s1: - ID = Intrinsic::hexagon_M2_mpyu_acc_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_lh_s0: - ID = Intrinsic::hexagon_M2_mpyu_acc_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_lh_s1: - ID = Intrinsic::hexagon_M2_mpyu_acc_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_ll_s0: - ID = Intrinsic::hexagon_M2_mpyu_acc_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_acc_ll_s1: - ID = Intrinsic::hexagon_M2_mpyu_acc_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hh_s0: - ID = Intrinsic::hexagon_M2_mpyu_nac_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hh_s1: - ID = Intrinsic::hexagon_M2_mpyu_nac_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hl_s0: - ID = Intrinsic::hexagon_M2_mpyu_nac_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_hl_s1: - ID = Intrinsic::hexagon_M2_mpyu_nac_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_lh_s0: - ID = Intrinsic::hexagon_M2_mpyu_nac_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_lh_s1: - ID = Intrinsic::hexagon_M2_mpyu_nac_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_ll_s0: - ID = Intrinsic::hexagon_M2_mpyu_nac_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_nac_ll_s1: - ID = Intrinsic::hexagon_M2_mpyu_nac_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hh_s0: - ID = Intrinsic::hexagon_M2_mpyu_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hh_s1: - ID = Intrinsic::hexagon_M2_mpyu_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hl_s0: - ID = Intrinsic::hexagon_M2_mpyu_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_hl_s1: - ID = Intrinsic::hexagon_M2_mpyu_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_lh_s0: - ID = Intrinsic::hexagon_M2_mpyu_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_lh_s1: - ID = Intrinsic::hexagon_M2_mpyu_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_ll_s0: - ID = Intrinsic::hexagon_M2_mpyu_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_ll_s1: - ID = Intrinsic::hexagon_M2_mpyu_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hh_s0: - ID = Intrinsic::hexagon_M2_mpyud_acc_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hh_s1: - ID = Intrinsic::hexagon_M2_mpyud_acc_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hl_s0: - ID = Intrinsic::hexagon_M2_mpyud_acc_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_hl_s1: - ID = Intrinsic::hexagon_M2_mpyud_acc_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_lh_s0: - ID = Intrinsic::hexagon_M2_mpyud_acc_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_lh_s1: - ID = Intrinsic::hexagon_M2_mpyud_acc_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_ll_s0: - ID = Intrinsic::hexagon_M2_mpyud_acc_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_acc_ll_s1: - ID = Intrinsic::hexagon_M2_mpyud_acc_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hh_s0: - ID = Intrinsic::hexagon_M2_mpyud_nac_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hh_s1: - ID = Intrinsic::hexagon_M2_mpyud_nac_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hl_s0: - ID = Intrinsic::hexagon_M2_mpyud_nac_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_hl_s1: - ID = Intrinsic::hexagon_M2_mpyud_nac_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_lh_s0: - ID = Intrinsic::hexagon_M2_mpyud_nac_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_lh_s1: - ID = Intrinsic::hexagon_M2_mpyud_nac_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_ll_s0: - ID = Intrinsic::hexagon_M2_mpyud_nac_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_nac_ll_s1: - ID = Intrinsic::hexagon_M2_mpyud_nac_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hh_s0: - ID = Intrinsic::hexagon_M2_mpyud_hh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hh_s1: - ID = Intrinsic::hexagon_M2_mpyud_hh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hl_s0: - ID = Intrinsic::hexagon_M2_mpyud_hl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_hl_s1: - ID = Intrinsic::hexagon_M2_mpyud_hl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_lh_s0: - ID = Intrinsic::hexagon_M2_mpyud_lh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_lh_s1: - ID = Intrinsic::hexagon_M2_mpyud_lh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_ll_s0: - ID = Intrinsic::hexagon_M2_mpyud_ll_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyud_ll_s1: - ID = Intrinsic::hexagon_M2_mpyud_ll_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpysmi: - ID = Intrinsic::hexagon_M2_mpysmi; break; - - case Hexagon::BI__builtin_HEXAGON_M2_macsip: - ID = Intrinsic::hexagon_M2_macsip; break; - - case Hexagon::BI__builtin_HEXAGON_M2_macsin: - ID = Intrinsic::hexagon_M2_macsin; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_s0: - ID = Intrinsic::hexagon_M2_dpmpyss_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_acc_s0: - ID = Intrinsic::hexagon_M2_dpmpyss_acc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_nac_s0: - ID = Intrinsic::hexagon_M2_dpmpyss_nac_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_s0: - ID = Intrinsic::hexagon_M2_dpmpyuu_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_acc_s0: - ID = Intrinsic::hexagon_M2_dpmpyuu_acc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyuu_nac_s0: - ID = Intrinsic::hexagon_M2_dpmpyuu_nac_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpy_up: - ID = Intrinsic::hexagon_M2_mpy_up; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyu_up: - ID = Intrinsic::hexagon_M2_mpyu_up; break; - - case Hexagon::BI__builtin_HEXAGON_M2_dpmpyss_rnd_s0: - ID = Intrinsic::hexagon_M2_dpmpyss_rnd_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyi: - ID = Intrinsic::hexagon_M2_mpyi; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mpyui: - ID = Intrinsic::hexagon_M2_mpyui; break; - - case Hexagon::BI__builtin_HEXAGON_M2_maci: - ID = Intrinsic::hexagon_M2_maci; break; - - case Hexagon::BI__builtin_HEXAGON_M2_acci: - ID = Intrinsic::hexagon_M2_acci; break; - - case Hexagon::BI__builtin_HEXAGON_M2_accii: - ID = Intrinsic::hexagon_M2_accii; break; - - case Hexagon::BI__builtin_HEXAGON_M2_nacci: - ID = Intrinsic::hexagon_M2_nacci; break; - - case Hexagon::BI__builtin_HEXAGON_M2_naccii: - ID = Intrinsic::hexagon_M2_naccii; break; - - case Hexagon::BI__builtin_HEXAGON_M2_subacc: - ID = Intrinsic::hexagon_M2_subacc; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s0: - ID = Intrinsic::hexagon_M2_vmpy2s_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s1: - ID = Intrinsic::hexagon_M2_vmpy2s_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2s_s0: - ID = Intrinsic::hexagon_M2_vmac2s_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2s_s1: - ID = Intrinsic::hexagon_M2_vmac2s_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s0pack: - ID = Intrinsic::hexagon_M2_vmpy2s_s0pack; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2s_s1pack: - ID = Intrinsic::hexagon_M2_vmpy2s_s1pack; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2: - ID = Intrinsic::hexagon_M2_vmac2; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2es_s0: - ID = Intrinsic::hexagon_M2_vmpy2es_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmpy2es_s1: - ID = Intrinsic::hexagon_M2_vmpy2es_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2es_s0: - ID = Intrinsic::hexagon_M2_vmac2es_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2es_s1: - ID = Intrinsic::hexagon_M2_vmac2es_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vmac2es: - ID = Intrinsic::hexagon_M2_vmac2es; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrmac_s0: - ID = Intrinsic::hexagon_M2_vrmac_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrmpy_s0: - ID = Intrinsic::hexagon_M2_vrmpy_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmpyrs_s0: - ID = Intrinsic::hexagon_M2_vdmpyrs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmpyrs_s1: - ID = Intrinsic::hexagon_M2_vdmpyrs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmacs_s0: - ID = Intrinsic::hexagon_M2_vdmacs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmacs_s1: - ID = Intrinsic::hexagon_M2_vdmacs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmpys_s0: - ID = Intrinsic::hexagon_M2_vdmpys_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vdmpys_s1: - ID = Intrinsic::hexagon_M2_vdmpys_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyrs_s0: - ID = Intrinsic::hexagon_M2_cmpyrs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyrs_s1: - ID = Intrinsic::hexagon_M2_cmpyrs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyrsc_s0: - ID = Intrinsic::hexagon_M2_cmpyrsc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyrsc_s1: - ID = Intrinsic::hexagon_M2_cmpyrsc_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmacs_s0: - ID = Intrinsic::hexagon_M2_cmacs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmacs_s1: - ID = Intrinsic::hexagon_M2_cmacs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmacsc_s0: - ID = Intrinsic::hexagon_M2_cmacsc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmacsc_s1: - ID = Intrinsic::hexagon_M2_cmacsc_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpys_s0: - ID = Intrinsic::hexagon_M2_cmpys_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpys_s1: - ID = Intrinsic::hexagon_M2_cmpys_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpysc_s0: - ID = Intrinsic::hexagon_M2_cmpysc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpysc_s1: - ID = Intrinsic::hexagon_M2_cmpysc_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cnacs_s0: - ID = Intrinsic::hexagon_M2_cnacs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cnacs_s1: - ID = Intrinsic::hexagon_M2_cnacs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cnacsc_s0: - ID = Intrinsic::hexagon_M2_cnacsc_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cnacsc_s1: - ID = Intrinsic::hexagon_M2_cnacsc_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_s1: - ID = Intrinsic::hexagon_M2_vrcmpys_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_acc_s1: - ID = Intrinsic::hexagon_M2_vrcmpys_acc_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpys_s1rp: - ID = Intrinsic::hexagon_M2_vrcmpys_s1rp; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacls_s0: - ID = Intrinsic::hexagon_M2_mmacls_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacls_s1: - ID = Intrinsic::hexagon_M2_mmacls_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmachs_s0: - ID = Intrinsic::hexagon_M2_mmachs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmachs_s1: - ID = Intrinsic::hexagon_M2_mmachs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_s0: - ID = Intrinsic::hexagon_M2_mmpyl_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_s1: - ID = Intrinsic::hexagon_M2_mmpyl_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_s0: - ID = Intrinsic::hexagon_M2_mmpyh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_s1: - ID = Intrinsic::hexagon_M2_mmpyh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacls_rs0: - ID = Intrinsic::hexagon_M2_mmacls_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacls_rs1: - ID = Intrinsic::hexagon_M2_mmacls_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmachs_rs0: - ID = Intrinsic::hexagon_M2_mmachs_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmachs_rs1: - ID = Intrinsic::hexagon_M2_mmachs_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_rs0: - ID = Intrinsic::hexagon_M2_mmpyl_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyl_rs1: - ID = Intrinsic::hexagon_M2_mmpyl_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_rs0: - ID = Intrinsic::hexagon_M2_mmpyh_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyh_rs1: - ID = Intrinsic::hexagon_M2_mmpyh_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_hmmpyl_rs1: - ID = Intrinsic::hexagon_M2_hmmpyl_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_hmmpyh_rs1: - ID = Intrinsic::hexagon_M2_hmmpyh_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_s0: - ID = Intrinsic::hexagon_M2_mmaculs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_s1: - ID = Intrinsic::hexagon_M2_mmaculs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_s0: - ID = Intrinsic::hexagon_M2_mmacuhs_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_s1: - ID = Intrinsic::hexagon_M2_mmacuhs_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_s0: - ID = Intrinsic::hexagon_M2_mmpyul_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_s1: - ID = Intrinsic::hexagon_M2_mmpyul_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_s0: - ID = Intrinsic::hexagon_M2_mmpyuh_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_s1: - ID = Intrinsic::hexagon_M2_mmpyuh_s1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_rs0: - ID = Intrinsic::hexagon_M2_mmaculs_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmaculs_rs1: - ID = Intrinsic::hexagon_M2_mmaculs_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_rs0: - ID = Intrinsic::hexagon_M2_mmacuhs_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmacuhs_rs1: - ID = Intrinsic::hexagon_M2_mmacuhs_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_rs0: - ID = Intrinsic::hexagon_M2_mmpyul_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyul_rs1: - ID = Intrinsic::hexagon_M2_mmpyul_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_rs0: - ID = Intrinsic::hexagon_M2_mmpyuh_rs0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_mmpyuh_rs1: - ID = Intrinsic::hexagon_M2_mmpyuh_rs1; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmaci_s0: - ID = Intrinsic::hexagon_M2_vrcmaci_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmacr_s0: - ID = Intrinsic::hexagon_M2_vrcmacr_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmaci_s0c: - ID = Intrinsic::hexagon_M2_vrcmaci_s0c; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmacr_s0c: - ID = Intrinsic::hexagon_M2_vrcmacr_s0c; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmaci_s0: - ID = Intrinsic::hexagon_M2_cmaci_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmacr_s0: - ID = Intrinsic::hexagon_M2_cmacr_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyi_s0: - ID = Intrinsic::hexagon_M2_vrcmpyi_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyr_s0: - ID = Intrinsic::hexagon_M2_vrcmpyr_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyi_s0c: - ID = Intrinsic::hexagon_M2_vrcmpyi_s0c; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vrcmpyr_s0c: - ID = Intrinsic::hexagon_M2_vrcmpyr_s0c; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyi_s0: - ID = Intrinsic::hexagon_M2_cmpyi_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_cmpyr_s0: - ID = Intrinsic::hexagon_M2_cmpyr_s0; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s0_sat_i: - ID = Intrinsic::hexagon_M2_vcmpy_s0_sat_i; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s0_sat_r: - ID = Intrinsic::hexagon_M2_vcmpy_s0_sat_r; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s1_sat_i: - ID = Intrinsic::hexagon_M2_vcmpy_s1_sat_i; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmpy_s1_sat_r: - ID = Intrinsic::hexagon_M2_vcmpy_s1_sat_r; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmac_s0_sat_i: - ID = Intrinsic::hexagon_M2_vcmac_s0_sat_i; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vcmac_s0_sat_r: - ID = Intrinsic::hexagon_M2_vcmac_s0_sat_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vcrotate: - ID = Intrinsic::hexagon_S2_vcrotate; break; - - case Hexagon::BI__builtin_HEXAGON_A2_add: - ID = Intrinsic::hexagon_A2_add; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sub: - ID = Intrinsic::hexagon_A2_sub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addsat: - ID = Intrinsic::hexagon_A2_addsat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subsat: - ID = Intrinsic::hexagon_A2_subsat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addi: - ID = Intrinsic::hexagon_A2_addi; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_ll: - ID = Intrinsic::hexagon_A2_addh_l16_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_hl: - ID = Intrinsic::hexagon_A2_addh_l16_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_sat_ll: - ID = Intrinsic::hexagon_A2_addh_l16_sat_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_l16_sat_hl: - ID = Intrinsic::hexagon_A2_addh_l16_sat_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_ll: - ID = Intrinsic::hexagon_A2_subh_l16_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_hl: - ID = Intrinsic::hexagon_A2_subh_l16_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_sat_ll: - ID = Intrinsic::hexagon_A2_subh_l16_sat_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_l16_sat_hl: - ID = Intrinsic::hexagon_A2_subh_l16_sat_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_ll: - ID = Intrinsic::hexagon_A2_addh_h16_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_lh: - ID = Intrinsic::hexagon_A2_addh_h16_lh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_hl: - ID = Intrinsic::hexagon_A2_addh_h16_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_hh: - ID = Intrinsic::hexagon_A2_addh_h16_hh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_ll: - ID = Intrinsic::hexagon_A2_addh_h16_sat_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_lh: - ID = Intrinsic::hexagon_A2_addh_h16_sat_lh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_hl: - ID = Intrinsic::hexagon_A2_addh_h16_sat_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addh_h16_sat_hh: - ID = Intrinsic::hexagon_A2_addh_h16_sat_hh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_ll: - ID = Intrinsic::hexagon_A2_subh_h16_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_lh: - ID = Intrinsic::hexagon_A2_subh_h16_lh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_hl: - ID = Intrinsic::hexagon_A2_subh_h16_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_hh: - ID = Intrinsic::hexagon_A2_subh_h16_hh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_ll: - ID = Intrinsic::hexagon_A2_subh_h16_sat_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_lh: - ID = Intrinsic::hexagon_A2_subh_h16_sat_lh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_hl: - ID = Intrinsic::hexagon_A2_subh_h16_sat_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subh_h16_sat_hh: - ID = Intrinsic::hexagon_A2_subh_h16_sat_hh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_aslh: - ID = Intrinsic::hexagon_A2_aslh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_asrh: - ID = Intrinsic::hexagon_A2_asrh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addp: - ID = Intrinsic::hexagon_A2_addp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addpsat: - ID = Intrinsic::hexagon_A2_addpsat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_addsp: - ID = Intrinsic::hexagon_A2_addsp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subp: - ID = Intrinsic::hexagon_A2_subp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_neg: - ID = Intrinsic::hexagon_A2_neg; break; - - case Hexagon::BI__builtin_HEXAGON_A2_negsat: - ID = Intrinsic::hexagon_A2_negsat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_abs: - ID = Intrinsic::hexagon_A2_abs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_abssat: - ID = Intrinsic::hexagon_A2_abssat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vconj: - ID = Intrinsic::hexagon_A2_vconj; break; - - case Hexagon::BI__builtin_HEXAGON_A2_negp: - ID = Intrinsic::hexagon_A2_negp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_absp: - ID = Intrinsic::hexagon_A2_absp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_max: - ID = Intrinsic::hexagon_A2_max; break; - - case Hexagon::BI__builtin_HEXAGON_A2_maxu: - ID = Intrinsic::hexagon_A2_maxu; break; - - case Hexagon::BI__builtin_HEXAGON_A2_min: - ID = Intrinsic::hexagon_A2_min; break; - - case Hexagon::BI__builtin_HEXAGON_A2_minu: - ID = Intrinsic::hexagon_A2_minu; break; - - case Hexagon::BI__builtin_HEXAGON_A2_maxp: - ID = Intrinsic::hexagon_A2_maxp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_maxup: - ID = Intrinsic::hexagon_A2_maxup; break; - - case Hexagon::BI__builtin_HEXAGON_A2_minp: - ID = Intrinsic::hexagon_A2_minp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_minup: - ID = Intrinsic::hexagon_A2_minup; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfr: - ID = Intrinsic::hexagon_A2_tfr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfrsi: - ID = Intrinsic::hexagon_A2_tfrsi; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfrp: - ID = Intrinsic::hexagon_A2_tfrp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfrpi: - ID = Intrinsic::hexagon_A2_tfrpi; break; - - case Hexagon::BI__builtin_HEXAGON_A2_zxtb: - ID = Intrinsic::hexagon_A2_zxtb; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sxtb: - ID = Intrinsic::hexagon_A2_sxtb; break; - - case Hexagon::BI__builtin_HEXAGON_A2_zxth: - ID = Intrinsic::hexagon_A2_zxth; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sxth: - ID = Intrinsic::hexagon_A2_sxth; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combinew: - ID = Intrinsic::hexagon_A2_combinew; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combineii: - ID = Intrinsic::hexagon_A2_combineii; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combine_hh: - ID = Intrinsic::hexagon_A2_combine_hh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combine_hl: - ID = Intrinsic::hexagon_A2_combine_hl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combine_lh: - ID = Intrinsic::hexagon_A2_combine_lh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_combine_ll: - ID = Intrinsic::hexagon_A2_combine_ll; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfril: - ID = Intrinsic::hexagon_A2_tfril; break; - - case Hexagon::BI__builtin_HEXAGON_A2_tfrih: - ID = Intrinsic::hexagon_A2_tfrih; break; - - case Hexagon::BI__builtin_HEXAGON_A2_and: - ID = Intrinsic::hexagon_A2_and; break; - - case Hexagon::BI__builtin_HEXAGON_A2_or: - ID = Intrinsic::hexagon_A2_or; break; - - case Hexagon::BI__builtin_HEXAGON_A2_xor: - ID = Intrinsic::hexagon_A2_xor; break; - - case Hexagon::BI__builtin_HEXAGON_A2_not: - ID = Intrinsic::hexagon_A2_not; break; - - case Hexagon::BI__builtin_HEXAGON_M2_xor_xacc: - ID = Intrinsic::hexagon_M2_xor_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_A2_subri: - ID = Intrinsic::hexagon_A2_subri; break; - - case Hexagon::BI__builtin_HEXAGON_A2_andir: - ID = Intrinsic::hexagon_A2_andir; break; - - case Hexagon::BI__builtin_HEXAGON_A2_orir: - ID = Intrinsic::hexagon_A2_orir; break; - - case Hexagon::BI__builtin_HEXAGON_A2_andp: - ID = Intrinsic::hexagon_A2_andp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_orp: - ID = Intrinsic::hexagon_A2_orp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_xorp: - ID = Intrinsic::hexagon_A2_xorp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_notp: - ID = Intrinsic::hexagon_A2_notp; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sxtw: - ID = Intrinsic::hexagon_A2_sxtw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sat: - ID = Intrinsic::hexagon_A2_sat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_sath: - ID = Intrinsic::hexagon_A2_sath; break; - - case Hexagon::BI__builtin_HEXAGON_A2_satuh: - ID = Intrinsic::hexagon_A2_satuh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_satub: - ID = Intrinsic::hexagon_A2_satub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_satb: - ID = Intrinsic::hexagon_A2_satb; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddub: - ID = Intrinsic::hexagon_A2_vaddub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddubs: - ID = Intrinsic::hexagon_A2_vaddubs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddh: - ID = Intrinsic::hexagon_A2_vaddh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddhs: - ID = Intrinsic::hexagon_A2_vaddhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vadduhs: - ID = Intrinsic::hexagon_A2_vadduhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddw: - ID = Intrinsic::hexagon_A2_vaddw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vaddws: - ID = Intrinsic::hexagon_A2_vaddws; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svavgh: - ID = Intrinsic::hexagon_A2_svavgh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svavghs: - ID = Intrinsic::hexagon_A2_svavghs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svnavgh: - ID = Intrinsic::hexagon_A2_svnavgh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svaddh: - ID = Intrinsic::hexagon_A2_svaddh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svaddhs: - ID = Intrinsic::hexagon_A2_svaddhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svadduhs: - ID = Intrinsic::hexagon_A2_svadduhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svsubh: - ID = Intrinsic::hexagon_A2_svsubh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svsubhs: - ID = Intrinsic::hexagon_A2_svsubhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_svsubuhs: - ID = Intrinsic::hexagon_A2_svsubuhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vraddub: - ID = Intrinsic::hexagon_A2_vraddub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vraddub_acc: - ID = Intrinsic::hexagon_A2_vraddub_acc; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vradduh: - ID = Intrinsic::hexagon_M2_vradduh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubub: - ID = Intrinsic::hexagon_A2_vsubub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsububs: - ID = Intrinsic::hexagon_A2_vsububs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubh: - ID = Intrinsic::hexagon_A2_vsubh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubhs: - ID = Intrinsic::hexagon_A2_vsubhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubuhs: - ID = Intrinsic::hexagon_A2_vsubuhs; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubw: - ID = Intrinsic::hexagon_A2_vsubw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vsubws: - ID = Intrinsic::hexagon_A2_vsubws; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vabsh: - ID = Intrinsic::hexagon_A2_vabsh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vabshsat: - ID = Intrinsic::hexagon_A2_vabshsat; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vabsw: - ID = Intrinsic::hexagon_A2_vabsw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vabswsat: - ID = Intrinsic::hexagon_A2_vabswsat; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vabsdiffw: - ID = Intrinsic::hexagon_M2_vabsdiffw; break; - - case Hexagon::BI__builtin_HEXAGON_M2_vabsdiffh: - ID = Intrinsic::hexagon_M2_vabsdiffh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vrsadub: - ID = Intrinsic::hexagon_A2_vrsadub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vrsadub_acc: - ID = Intrinsic::hexagon_A2_vrsadub_acc; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgub: - ID = Intrinsic::hexagon_A2_vavgub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavguh: - ID = Intrinsic::hexagon_A2_vavguh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgh: - ID = Intrinsic::hexagon_A2_vavgh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavgh: - ID = Intrinsic::hexagon_A2_vnavgh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgw: - ID = Intrinsic::hexagon_A2_vavgw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavgw: - ID = Intrinsic::hexagon_A2_vnavgw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgwr: - ID = Intrinsic::hexagon_A2_vavgwr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavgwr: - ID = Intrinsic::hexagon_A2_vnavgwr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgwcr: - ID = Intrinsic::hexagon_A2_vavgwcr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavgwcr: - ID = Intrinsic::hexagon_A2_vnavgwcr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavghcr: - ID = Intrinsic::hexagon_A2_vavghcr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavghcr: - ID = Intrinsic::hexagon_A2_vnavghcr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavguw: - ID = Intrinsic::hexagon_A2_vavguw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavguwr: - ID = Intrinsic::hexagon_A2_vavguwr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavgubr: - ID = Intrinsic::hexagon_A2_vavgubr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavguhr: - ID = Intrinsic::hexagon_A2_vavguhr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vavghr: - ID = Intrinsic::hexagon_A2_vavghr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vnavghr: - ID = Intrinsic::hexagon_A2_vnavghr; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vminh: - ID = Intrinsic::hexagon_A2_vminh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vmaxh: - ID = Intrinsic::hexagon_A2_vmaxh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vminub: - ID = Intrinsic::hexagon_A2_vminub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vmaxub: - ID = Intrinsic::hexagon_A2_vmaxub; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vminuh: - ID = Intrinsic::hexagon_A2_vminuh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vmaxuh: - ID = Intrinsic::hexagon_A2_vmaxuh; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vminw: - ID = Intrinsic::hexagon_A2_vminw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vmaxw: - ID = Intrinsic::hexagon_A2_vmaxw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vminuw: - ID = Intrinsic::hexagon_A2_vminuw; break; - - case Hexagon::BI__builtin_HEXAGON_A2_vmaxuw: - ID = Intrinsic::hexagon_A2_vmaxuw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r: - ID = Intrinsic::hexagon_S2_asr_r_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r: - ID = Intrinsic::hexagon_S2_asl_r_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r: - ID = Intrinsic::hexagon_S2_lsr_r_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r: - ID = Intrinsic::hexagon_S2_lsl_r_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p: - ID = Intrinsic::hexagon_S2_asr_r_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p: - ID = Intrinsic::hexagon_S2_asl_r_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p: - ID = Intrinsic::hexagon_S2_lsr_r_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p: - ID = Intrinsic::hexagon_S2_lsl_r_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_acc: - ID = Intrinsic::hexagon_S2_asr_r_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_acc: - ID = Intrinsic::hexagon_S2_asl_r_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_acc: - ID = Intrinsic::hexagon_S2_lsr_r_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_acc: - ID = Intrinsic::hexagon_S2_lsl_r_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_acc: - ID = Intrinsic::hexagon_S2_asr_r_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_acc: - ID = Intrinsic::hexagon_S2_asl_r_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_acc: - ID = Intrinsic::hexagon_S2_lsr_r_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_acc: - ID = Intrinsic::hexagon_S2_lsl_r_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_nac: - ID = Intrinsic::hexagon_S2_asr_r_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_nac: - ID = Intrinsic::hexagon_S2_asl_r_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_nac: - ID = Intrinsic::hexagon_S2_lsr_r_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_nac: - ID = Intrinsic::hexagon_S2_lsl_r_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_nac: - ID = Intrinsic::hexagon_S2_asr_r_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_nac: - ID = Intrinsic::hexagon_S2_asl_r_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_nac: - ID = Intrinsic::hexagon_S2_lsr_r_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_nac: - ID = Intrinsic::hexagon_S2_lsl_r_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_and: - ID = Intrinsic::hexagon_S2_asr_r_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_and: - ID = Intrinsic::hexagon_S2_asl_r_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_and: - ID = Intrinsic::hexagon_S2_lsr_r_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_and: - ID = Intrinsic::hexagon_S2_lsl_r_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_or: - ID = Intrinsic::hexagon_S2_asr_r_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_or: - ID = Intrinsic::hexagon_S2_asl_r_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_r_or: - ID = Intrinsic::hexagon_S2_lsr_r_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_r_or: - ID = Intrinsic::hexagon_S2_lsl_r_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_and: - ID = Intrinsic::hexagon_S2_asr_r_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_and: - ID = Intrinsic::hexagon_S2_asl_r_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_and: - ID = Intrinsic::hexagon_S2_lsr_r_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_and: - ID = Intrinsic::hexagon_S2_lsl_r_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_p_or: - ID = Intrinsic::hexagon_S2_asr_r_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_p_or: - ID = Intrinsic::hexagon_S2_asl_r_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_p_or: - ID = Intrinsic::hexagon_S2_lsr_r_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_p_or: - ID = Intrinsic::hexagon_S2_lsl_r_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_r_sat: - ID = Intrinsic::hexagon_S2_asr_r_r_sat; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_r_sat: - ID = Intrinsic::hexagon_S2_asl_r_r_sat; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r: - ID = Intrinsic::hexagon_S2_asr_i_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r: - ID = Intrinsic::hexagon_S2_lsr_i_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r: - ID = Intrinsic::hexagon_S2_asl_i_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p: - ID = Intrinsic::hexagon_S2_asr_i_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p: - ID = Intrinsic::hexagon_S2_lsr_i_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p: - ID = Intrinsic::hexagon_S2_asl_i_p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_acc: - ID = Intrinsic::hexagon_S2_asr_i_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_acc: - ID = Intrinsic::hexagon_S2_lsr_i_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_acc: - ID = Intrinsic::hexagon_S2_asl_i_r_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_acc: - ID = Intrinsic::hexagon_S2_asr_i_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_acc: - ID = Intrinsic::hexagon_S2_lsr_i_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_acc: - ID = Intrinsic::hexagon_S2_asl_i_p_acc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_nac: - ID = Intrinsic::hexagon_S2_asr_i_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_nac: - ID = Intrinsic::hexagon_S2_lsr_i_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_nac: - ID = Intrinsic::hexagon_S2_asl_i_r_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_nac: - ID = Intrinsic::hexagon_S2_asr_i_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_nac: - ID = Intrinsic::hexagon_S2_lsr_i_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_nac: - ID = Intrinsic::hexagon_S2_asl_i_p_nac; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_xacc: - ID = Intrinsic::hexagon_S2_lsr_i_r_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_xacc: - ID = Intrinsic::hexagon_S2_asl_i_r_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_xacc: - ID = Intrinsic::hexagon_S2_lsr_i_p_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_xacc: - ID = Intrinsic::hexagon_S2_asl_i_p_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_and: - ID = Intrinsic::hexagon_S2_asr_i_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_and: - ID = Intrinsic::hexagon_S2_lsr_i_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_and: - ID = Intrinsic::hexagon_S2_asl_i_r_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_or: - ID = Intrinsic::hexagon_S2_asr_i_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_r_or: - ID = Intrinsic::hexagon_S2_lsr_i_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_or: - ID = Intrinsic::hexagon_S2_asl_i_r_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_and: - ID = Intrinsic::hexagon_S2_asr_i_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_and: - ID = Intrinsic::hexagon_S2_lsr_i_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_and: - ID = Intrinsic::hexagon_S2_asl_i_p_and; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_p_or: - ID = Intrinsic::hexagon_S2_asr_i_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_p_or: - ID = Intrinsic::hexagon_S2_lsr_i_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_p_or: - ID = Intrinsic::hexagon_S2_asl_i_p_or; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_r_sat: - ID = Intrinsic::hexagon_S2_asl_i_r_sat; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_rnd: - ID = Intrinsic::hexagon_S2_asr_i_r_rnd; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_r_rnd_goodsyntax: - ID = Intrinsic::hexagon_S2_asr_i_r_rnd_goodsyntax; break; - - case Hexagon::BI__builtin_HEXAGON_S2_addasl_rrri: - ID = Intrinsic::hexagon_S2_addasl_rrri; break; - - case Hexagon::BI__builtin_HEXAGON_S2_valignib: - ID = Intrinsic::hexagon_S2_valignib; break; - - case Hexagon::BI__builtin_HEXAGON_S2_valignrb: - ID = Intrinsic::hexagon_S2_valignrb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vspliceib: - ID = Intrinsic::hexagon_S2_vspliceib; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsplicerb: - ID = Intrinsic::hexagon_S2_vsplicerb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsplatrh: - ID = Intrinsic::hexagon_S2_vsplatrh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsplatrb: - ID = Intrinsic::hexagon_S2_vsplatrb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_insert: - ID = Intrinsic::hexagon_S2_insert; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tableidxb_goodsyntax: - ID = Intrinsic::hexagon_S2_tableidxb_goodsyntax; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tableidxh_goodsyntax: - ID = Intrinsic::hexagon_S2_tableidxh_goodsyntax; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tableidxw_goodsyntax: - ID = Intrinsic::hexagon_S2_tableidxw_goodsyntax; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tableidxd_goodsyntax: - ID = Intrinsic::hexagon_S2_tableidxd_goodsyntax; break; - - case Hexagon::BI__builtin_HEXAGON_S2_extractu: - ID = Intrinsic::hexagon_S2_extractu; break; - - case Hexagon::BI__builtin_HEXAGON_S2_insertp: - ID = Intrinsic::hexagon_S2_insertp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_extractup: - ID = Intrinsic::hexagon_S2_extractup; break; - - case Hexagon::BI__builtin_HEXAGON_S2_insert_rp: - ID = Intrinsic::hexagon_S2_insert_rp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_extractu_rp: - ID = Intrinsic::hexagon_S2_extractu_rp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_insertp_rp: - ID = Intrinsic::hexagon_S2_insertp_rp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_extractup_rp: - ID = Intrinsic::hexagon_S2_extractup_rp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tstbit_i: - ID = Intrinsic::hexagon_S2_tstbit_i; break; - - case Hexagon::BI__builtin_HEXAGON_S2_setbit_i: - ID = Intrinsic::hexagon_S2_setbit_i; break; - - case Hexagon::BI__builtin_HEXAGON_S2_togglebit_i: - ID = Intrinsic::hexagon_S2_togglebit_i; break; - - case Hexagon::BI__builtin_HEXAGON_S2_clrbit_i: - ID = Intrinsic::hexagon_S2_clrbit_i; break; - - case Hexagon::BI__builtin_HEXAGON_S2_tstbit_r: - ID = Intrinsic::hexagon_S2_tstbit_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_setbit_r: - ID = Intrinsic::hexagon_S2_setbit_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_togglebit_r: - ID = Intrinsic::hexagon_S2_togglebit_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_clrbit_r: - ID = Intrinsic::hexagon_S2_clrbit_r; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_vh: - ID = Intrinsic::hexagon_S2_asr_i_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_vh: - ID = Intrinsic::hexagon_S2_lsr_i_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_vh: - ID = Intrinsic::hexagon_S2_asl_i_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_vh: - ID = Intrinsic::hexagon_S2_asr_r_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_vh: - ID = Intrinsic::hexagon_S2_asl_r_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_vh: - ID = Intrinsic::hexagon_S2_lsr_r_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_vh: - ID = Intrinsic::hexagon_S2_lsl_r_vh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_vw: - ID = Intrinsic::hexagon_S2_asr_i_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_i_svw_trun: - ID = Intrinsic::hexagon_S2_asr_i_svw_trun; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_svw_trun: - ID = Intrinsic::hexagon_S2_asr_r_svw_trun; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_i_vw: - ID = Intrinsic::hexagon_S2_lsr_i_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_i_vw: - ID = Intrinsic::hexagon_S2_asl_i_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asr_r_vw: - ID = Intrinsic::hexagon_S2_asr_r_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_asl_r_vw: - ID = Intrinsic::hexagon_S2_asl_r_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsr_r_vw: - ID = Intrinsic::hexagon_S2_lsr_r_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lsl_r_vw: - ID = Intrinsic::hexagon_S2_lsl_r_vw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vrndpackwh: - ID = Intrinsic::hexagon_S2_vrndpackwh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vrndpackwhs: - ID = Intrinsic::hexagon_S2_vrndpackwhs; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsxtbh: - ID = Intrinsic::hexagon_S2_vsxtbh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vzxtbh: - ID = Intrinsic::hexagon_S2_vzxtbh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsathub: - ID = Intrinsic::hexagon_S2_vsathub; break; - - case Hexagon::BI__builtin_HEXAGON_S2_svsathub: - ID = Intrinsic::hexagon_S2_svsathub; break; - - case Hexagon::BI__builtin_HEXAGON_S2_svsathb: - ID = Intrinsic::hexagon_S2_svsathb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsathb: - ID = Intrinsic::hexagon_S2_vsathb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vtrunohb: - ID = Intrinsic::hexagon_S2_vtrunohb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vtrunewh: - ID = Intrinsic::hexagon_S2_vtrunewh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vtrunowh: - ID = Intrinsic::hexagon_S2_vtrunowh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vtrunehb: - ID = Intrinsic::hexagon_S2_vtrunehb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsxthw: - ID = Intrinsic::hexagon_S2_vsxthw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vzxthw: - ID = Intrinsic::hexagon_S2_vzxthw; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsatwh: - ID = Intrinsic::hexagon_S2_vsatwh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsatwuh: - ID = Intrinsic::hexagon_S2_vsatwuh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_packhl: - ID = Intrinsic::hexagon_S2_packhl; break; - - case Hexagon::BI__builtin_HEXAGON_A2_swiz: - ID = Intrinsic::hexagon_A2_swiz; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsathub_nopack: - ID = Intrinsic::hexagon_S2_vsathub_nopack; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsathb_nopack: - ID = Intrinsic::hexagon_S2_vsathb_nopack; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsatwh_nopack: - ID = Intrinsic::hexagon_S2_vsatwh_nopack; break; - - case Hexagon::BI__builtin_HEXAGON_S2_vsatwuh_nopack: - ID = Intrinsic::hexagon_S2_vsatwuh_nopack; break; - - case Hexagon::BI__builtin_HEXAGON_S2_shuffob: - ID = Intrinsic::hexagon_S2_shuffob; break; - - case Hexagon::BI__builtin_HEXAGON_S2_shuffeb: - ID = Intrinsic::hexagon_S2_shuffeb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_shuffoh: - ID = Intrinsic::hexagon_S2_shuffoh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_shuffeh: - ID = Intrinsic::hexagon_S2_shuffeh; break; - - case Hexagon::BI__builtin_HEXAGON_S2_parityp: - ID = Intrinsic::hexagon_S2_parityp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_lfsp: - ID = Intrinsic::hexagon_S2_lfsp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_clbnorm: - ID = Intrinsic::hexagon_S2_clbnorm; break; - - case Hexagon::BI__builtin_HEXAGON_S2_clb: - ID = Intrinsic::hexagon_S2_clb; break; - - case Hexagon::BI__builtin_HEXAGON_S2_cl0: - ID = Intrinsic::hexagon_S2_cl0; break; - - case Hexagon::BI__builtin_HEXAGON_S2_cl1: - ID = Intrinsic::hexagon_S2_cl1; break; - - case Hexagon::BI__builtin_HEXAGON_S2_clbp: - ID = Intrinsic::hexagon_S2_clbp; break; - - case Hexagon::BI__builtin_HEXAGON_S2_cl0p: - ID = Intrinsic::hexagon_S2_cl0p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_cl1p: - ID = Intrinsic::hexagon_S2_cl1p; break; - - case Hexagon::BI__builtin_HEXAGON_S2_brev: - ID = Intrinsic::hexagon_S2_brev; break; - - case Hexagon::BI__builtin_HEXAGON_S2_ct0: - ID = Intrinsic::hexagon_S2_ct0; break; - - case Hexagon::BI__builtin_HEXAGON_S2_ct1: - ID = Intrinsic::hexagon_S2_ct1; break; - - case Hexagon::BI__builtin_HEXAGON_S2_interleave: - ID = Intrinsic::hexagon_S2_interleave; break; - - case Hexagon::BI__builtin_HEXAGON_S2_deinterleave: - ID = Intrinsic::hexagon_S2_deinterleave; break; - - case Hexagon::BI__builtin_SI_to_SXTHI_asrh: - ID = Intrinsic::hexagon_SI_to_SXTHI_asrh; break; - - case Hexagon::BI__builtin_HEXAGON_A4_orn: - ID = Intrinsic::hexagon_A4_orn; break; - - case Hexagon::BI__builtin_HEXAGON_A4_andn: - ID = Intrinsic::hexagon_A4_andn; break; - - case Hexagon::BI__builtin_HEXAGON_A4_ornp: - ID = Intrinsic::hexagon_A4_ornp; break; - - case Hexagon::BI__builtin_HEXAGON_A4_andnp: - ID = Intrinsic::hexagon_A4_andnp; break; - - case Hexagon::BI__builtin_HEXAGON_A4_combineir: - ID = Intrinsic::hexagon_A4_combineir; break; - - case Hexagon::BI__builtin_HEXAGON_A4_combineri: - ID = Intrinsic::hexagon_A4_combineri; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmpneqi: - ID = Intrinsic::hexagon_C4_cmpneqi; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmpneq: - ID = Intrinsic::hexagon_C4_cmpneq; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmpltei: - ID = Intrinsic::hexagon_C4_cmpltei; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmplte: - ID = Intrinsic::hexagon_C4_cmplte; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmplteui: - ID = Intrinsic::hexagon_C4_cmplteui; break; - - case Hexagon::BI__builtin_HEXAGON_C4_cmplteu: - ID = Intrinsic::hexagon_C4_cmplteu; break; - - case Hexagon::BI__builtin_HEXAGON_A4_rcmpneq: - ID = Intrinsic::hexagon_A4_rcmpneq; break; - - case Hexagon::BI__builtin_HEXAGON_A4_rcmpneqi: - ID = Intrinsic::hexagon_A4_rcmpneqi; break; - - case Hexagon::BI__builtin_HEXAGON_A4_rcmpeq: - ID = Intrinsic::hexagon_A4_rcmpeq; break; - - case Hexagon::BI__builtin_HEXAGON_A4_rcmpeqi: - ID = Intrinsic::hexagon_A4_rcmpeqi; break; - - case Hexagon::BI__builtin_HEXAGON_C4_fastcorner9: - ID = Intrinsic::hexagon_C4_fastcorner9; break; - - case Hexagon::BI__builtin_HEXAGON_C4_fastcorner9_not: - ID = Intrinsic::hexagon_C4_fastcorner9_not; break; - - case Hexagon::BI__builtin_HEXAGON_C4_and_andn: - ID = Intrinsic::hexagon_C4_and_andn; break; - - case Hexagon::BI__builtin_HEXAGON_C4_and_and: - ID = Intrinsic::hexagon_C4_and_and; break; - - case Hexagon::BI__builtin_HEXAGON_C4_and_orn: - ID = Intrinsic::hexagon_C4_and_orn; break; - - case Hexagon::BI__builtin_HEXAGON_C4_and_or: - ID = Intrinsic::hexagon_C4_and_or; break; - - case Hexagon::BI__builtin_HEXAGON_C4_or_andn: - ID = Intrinsic::hexagon_C4_or_andn; break; - - case Hexagon::BI__builtin_HEXAGON_C4_or_and: - ID = Intrinsic::hexagon_C4_or_and; break; - - case Hexagon::BI__builtin_HEXAGON_C4_or_orn: - ID = Intrinsic::hexagon_C4_or_orn; break; - - case Hexagon::BI__builtin_HEXAGON_C4_or_or: - ID = Intrinsic::hexagon_C4_or_or; break; - - case Hexagon::BI__builtin_HEXAGON_S4_addaddi: - ID = Intrinsic::hexagon_S4_addaddi; break; - - case Hexagon::BI__builtin_HEXAGON_S4_subaddi: - ID = Intrinsic::hexagon_S4_subaddi; break; - - case Hexagon::BI__builtin_HEXAGON_M4_xor_xacc: - ID = Intrinsic::hexagon_M4_xor_xacc; break; - - case Hexagon::BI__builtin_HEXAGON_M4_and_and: - ID = Intrinsic::hexagon_M4_and_and; break; - - case Hexagon::BI__builtin_HEXAGON_M4_and_or: - ID = Intrinsic::hexagon_M4_and_or; break; - - case Hexagon::BI__builtin_HEXAGON_M4_and_xor: - ID = Intrinsic::hexagon_M4_and_xor; break; - - case Hexagon::BI__builtin_HEXAGON_M4_and_andn: - ID = Intrinsic::hexagon_M4_and_andn; break; - - case Hexagon::BI__builtin_HEXAGON_M4_xor_and: - ID = Intrinsic::hexagon_M4_xor_and; break; - - case Hexagon::BI__builtin_HEXAGON_M4_xor_or: - ID = Intrinsic::hexagon_M4_xor_or; break; - - case Hexagon::BI__builtin_HEXAGON_M4_xor_andn: - ID = Intrinsic::hexagon_M4_xor_andn; break; - - case Hexagon::BI__builtin_HEXAGON_M4_or_and: - ID = Intrinsic::hexagon_M4_or_and; break; - - case Hexagon::BI__builtin_HEXAGON_M4_or_or: - ID = Intrinsic::hexagon_M4_or_or; break; - - case Hexagon::BI__builtin_HEXAGON_M4_or_xor: - ID = Intrinsic::hexagon_M4_or_xor; break; - - case Hexagon::BI__builtin_HEXAGON_M4_or_andn: - ID = Intrinsic::hexagon_M4_or_andn; break; - - case Hexagon::BI__builtin_HEXAGON_S4_or_andix: - ID = Intrinsic::hexagon_S4_or_andix; break; - - case Hexagon::BI__builtin_HEXAGON_S4_or_andi: - ID = Intrinsic::hexagon_S4_or_andi; break; - - case Hexagon::BI__builtin_HEXAGON_S4_or_ori: - ID = Intrinsic::hexagon_S4_or_ori; break; - - case Hexagon::BI__builtin_HEXAGON_A4_modwrapu: - ID = Intrinsic::hexagon_A4_modwrapu; break; - - case Hexagon::BI__builtin_HEXAGON_A4_cround_rr: - ID = Intrinsic::hexagon_A4_cround_rr; break; - - case Hexagon::BI__builtin_HEXAGON_A4_round_ri: - ID = Intrinsic::hexagon_A4_round_ri; break; - - case Hexagon::BI__builtin_HEXAGON_A4_round_rr: - ID = Intrinsic::hexagon_A4_round_rr; break; - - case Hexagon::BI__builtin_HEXAGON_A4_round_ri_sat: - ID = Intrinsic::hexagon_A4_round_ri_sat; break; - - case Hexagon::BI__builtin_HEXAGON_A4_round_rr_sat: - ID = Intrinsic::hexagon_A4_round_rr_sat; break; + case X86::BI__builtin_ia32_rdrand16_step: + case X86::BI__builtin_ia32_rdrand32_step: + case X86::BI__builtin_ia32_rdrand64_step: { + Intrinsic::ID ID; + switch (BuiltinID) { + default: llvm_unreachable("Unsupported intrinsic!"); + case X86::BI__builtin_ia32_rdrand16_step: + ID = Intrinsic::x86_rdrand_16; + break; + case X86::BI__builtin_ia32_rdrand32_step: + ID = Intrinsic::x86_rdrand_32; + break; + case X86::BI__builtin_ia32_rdrand64_step: + ID = Intrinsic::x86_rdrand_64; + break; + } + Value *Call = Builder.CreateCall(CGM.getIntrinsic(ID)); + Builder.CreateStore(Builder.CreateExtractValue(Call, 0), Ops[0]); + return Builder.CreateExtractValue(Call, 1); + } } - - llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, Ops, ""); } + Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { SmallVector<Value*, 4> Ops; diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 7c08650d278e..003fef520c90 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -53,7 +53,7 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) { // destructor separately. for (CXXRecordDecl::field_iterator I = Class->field_begin(), E = Class->field_end(); I != E; ++I) - if ((*I)->getType().isDestructedType()) + if (I->getType().isDestructedType()) return true; // Try to find a unique base class with a non-trivial destructor. @@ -91,7 +91,7 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) { // If the base is at a non-zero offset, give up. const ASTRecordLayout &ClassLayout = Context.getASTRecordLayout(Class); - if (ClassLayout.getBaseClassOffsetInBits(UniqueBase) != 0) + if (!ClassLayout.getBaseClassOffset(UniqueBase).isZero()) return true; return TryEmitDefinitionAsAlias(GlobalDecl(D, Dtor_Base), diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index befebbecbddb..aba5d755d5c4 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -23,7 +23,7 @@ static void ErrorUnsupportedABI(CodeGenFunction &CGF, StringRef S) { DiagnosticsEngine &Diags = CGF.CGM.getDiags(); unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, - "cannot yet compile %1 in this ABI"); + "cannot yet compile %0 in this ABI"); Diags.Report(CGF.getContext().getFullLoc(CGF.CurCodeDecl->getLocation()), DiagID) << S; @@ -145,6 +145,13 @@ void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF, } CharUnits CGCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) { + if (!requiresArrayCookie(expr)) + return CharUnits::Zero(); + return getArrayCookieSizeImpl(expr->getAllocatedType()); +} + +CharUnits CGCXXABI::getArrayCookieSizeImpl(QualType elementType) { + // BOGUS return CharUnits::Zero(); } @@ -158,16 +165,53 @@ llvm::Value *CGCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, return 0; } -void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, - const CXXDeleteExpr *expr, QualType ElementType, - llvm::Value *&NumElements, - llvm::Value *&AllocPtr, CharUnits &CookieSize) { - ErrorUnsupportedABI(CGF, "array cookie reading"); +bool CGCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, + QualType elementType) { + // If the class's usual deallocation function takes two arguments, + // it needs a cookie. + if (expr->doesUsualArrayDeleteWantSize()) + return true; - // This should be enough to avoid assertions. - NumElements = 0; - AllocPtr = llvm::Constant::getNullValue(CGF.Builder.getInt8PtrTy()); - CookieSize = CharUnits::Zero(); + return elementType.isDestructedType(); +} + +bool CGCXXABI::requiresArrayCookie(const CXXNewExpr *expr) { + // If the class's usual deallocation function takes two arguments, + // it needs a cookie. + if (expr->doesUsualArrayDeleteWantSize()) + return true; + + return expr->getAllocatedType().isDestructedType(); +} + +void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *ptr, + const CXXDeleteExpr *expr, QualType eltTy, + llvm::Value *&numElements, + llvm::Value *&allocPtr, CharUnits &cookieSize) { + // Derive a char* in the same address space as the pointer. + unsigned AS = cast<llvm::PointerType>(ptr->getType())->getAddressSpace(); + llvm::Type *charPtrTy = CGF.Int8Ty->getPointerTo(AS); + ptr = CGF.Builder.CreateBitCast(ptr, charPtrTy); + + // If we don't need an array cookie, bail out early. + if (!requiresArrayCookie(expr, eltTy)) { + allocPtr = ptr; + numElements = 0; + cookieSize = CharUnits::Zero(); + return; + } + + cookieSize = getArrayCookieSizeImpl(eltTy); + allocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(ptr, + -cookieSize.getQuantity()); + numElements = readArrayCookieImpl(CGF, allocPtr, cookieSize); +} + +llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *ptr, + CharUnits cookieSize) { + ErrorUnsupportedABI(CGF, "reading a new[] cookie"); + return llvm::ConstantInt::get(CGF.SizeTy, 0); } void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF, @@ -177,6 +221,13 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF, ErrorUnsupportedABI(CGF, "static local variable initialization"); } +void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + llvm::Constant *dtor, + llvm::Constant *addr) { + // The default behavior is to use atexit. + CGF.registerGlobalDtorWithAtExit(dtor, addr); +} + /// Returns the adjustment, in bytes, required for the given /// member-pointer operation. Returns null if no adjustment is /// required. diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 4e045f5f3275..a0dcdfdcc686 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -38,7 +38,7 @@ namespace CodeGen { class CodeGenFunction; class CodeGenModule; -/// Implements C++ ABI-specific code generation functions. +/// \brief Implements C++ ABI-specific code generation functions. class CGCXXABI { protected: CodeGenModule &CGM; @@ -71,6 +71,9 @@ protected: ASTContext &getContext() const { return CGM.getContext(); } + virtual bool requiresArrayCookie(const CXXDeleteExpr *E, QualType eltType); + virtual bool requiresArrayCookie(const CXXNewExpr *E); + public: virtual ~CGCXXABI(); @@ -190,18 +193,20 @@ public: virtual void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType); + /// Gets the pure virtual member call function. + virtual StringRef GetPureVirtualCallName() = 0; + /**************************** Array cookies ******************************/ /// Returns the extra size required in order to store the array - /// cookie for the given type. May return 0 to indicate that no + /// cookie for the given new-expression. May return 0 to indicate that no /// array cookie is required. /// /// Several cases are filtered out before this method is called: /// - non-array allocations never need a cookie - /// - calls to ::operator new(size_t, void*) never need a cookie + /// - calls to \::operator new(size_t, void*) never need a cookie /// - /// \param ElementType - the allocated type of the expression, - /// i.e. the pointee type of the expression result type + /// \param expr - the new-expression being allocated. virtual CharUnits GetArrayCookieSize(const CXXNewExpr *expr); /// Initialize the array cookie for the given allocation. @@ -209,7 +214,8 @@ public: /// \param NewPtr - a char* which is the presumed-non-null /// return value of the allocation function /// \param NumElements - the computed number of elements, - /// potentially collapsed from the multidimensional array case + /// potentially collapsed from the multidimensional array case; + /// always a size_t /// \param ElementType - the base element allocated type, /// i.e. the allocated type after stripping all array types virtual llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF, @@ -236,6 +242,27 @@ public: QualType ElementType, llvm::Value *&NumElements, llvm::Value *&AllocPtr, CharUnits &CookieSize); +protected: + /// Returns the extra size required in order to store the array + /// cookie for the given type. Assumes that an array cookie is + /// required. + virtual CharUnits getArrayCookieSizeImpl(QualType elementType); + + /// Reads the array cookie for an allocation which is known to have one. + /// This is called by the standard implementation of ReadArrayCookie. + /// + /// \param ptr - a pointer to the allocation made for an array, as a char* + /// \param cookieSize - the computed cookie size of an array + /// + /// Other parameters are as above. + /// + /// \return a size_t + virtual llvm::Value *readArrayCookieImpl(CodeGenFunction &IGF, + llvm::Value *ptr, + CharUnits cookieSize); + +public: + /*************************** Static local guards ****************************/ /// Emits the guarded initializer and destructor setup for the given @@ -249,6 +276,18 @@ public: virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); + /// Emit code to force the execution of a destructor during global + /// teardown. The default implementation of this uses atexit. + /// + /// \param dtor - a function taking a single pointer argument + /// \param addr - a pointer to pass to the destructor function. + virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, + llvm::Constant *addr); + + /***************************** Virtual Tables *******************************/ + + /// Generates and emits the virtual tables for a class. + virtual void EmitVTables(const CXXRecordDecl *Class) = 0; }; /// Creates an instance of a C++ ABI class. diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 82ee4fc1ee36..7d2b9d355ec6 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -67,39 +67,68 @@ static CanQualType GetReturnType(QualType RetTy) { return RetTy->getCanonicalTypeUnqualified().getUnqualifiedType(); } -/// Arrange the argument and result information for a value of the -/// given unprototyped function type. +/// Arrange the argument and result information for a value of the given +/// unprototyped freestanding function type. const CGFunctionInfo & -CodeGenTypes::arrangeFunctionType(CanQual<FunctionNoProtoType> FTNP) { +CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> FTNP) { // When translating an unprototyped function type, always use a // variadic type. - return arrangeFunctionType(FTNP->getResultType().getUnqualifiedType(), - ArrayRef<CanQualType>(), - FTNP->getExtInfo(), - RequiredArgs(0)); + return arrangeLLVMFunctionInfo(FTNP->getResultType().getUnqualifiedType(), + ArrayRef<CanQualType>(), + FTNP->getExtInfo(), + RequiredArgs(0)); } -/// Arrange the argument and result information for a value of the -/// given function type, on top of any implicit parameters already -/// stored. -static const CGFunctionInfo &arrangeFunctionType(CodeGenTypes &CGT, - SmallVectorImpl<CanQualType> &argTypes, - CanQual<FunctionProtoType> FTP) { - RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, argTypes.size()); +/// Arrange the LLVM function layout for a value of the given function +/// type, on top of any implicit parameters already stored. Use the +/// given ExtInfo instead of the ExtInfo from the function type. +static const CGFunctionInfo &arrangeLLVMFunctionInfo(CodeGenTypes &CGT, + SmallVectorImpl<CanQualType> &prefix, + CanQual<FunctionProtoType> FTP, + FunctionType::ExtInfo extInfo) { + RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size()); // FIXME: Kill copy. for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) - argTypes.push_back(FTP->getArgType(i)); + prefix.push_back(FTP->getArgType(i)); CanQualType resultType = FTP->getResultType().getUnqualifiedType(); - return CGT.arrangeFunctionType(resultType, argTypes, - FTP->getExtInfo(), required); + return CGT.arrangeLLVMFunctionInfo(resultType, prefix, extInfo, required); +} + +/// Arrange the argument and result information for a free function (i.e. +/// not a C++ or ObjC instance method) of the given type. +static const CGFunctionInfo &arrangeFreeFunctionType(CodeGenTypes &CGT, + SmallVectorImpl<CanQualType> &prefix, + CanQual<FunctionProtoType> FTP) { + return arrangeLLVMFunctionInfo(CGT, prefix, FTP, FTP->getExtInfo()); +} + +/// Given the formal ext-info of a C++ instance method, adjust it +/// according to the C++ ABI in effect. +static void adjustCXXMethodInfo(CodeGenTypes &CGT, + FunctionType::ExtInfo &extInfo, + bool isVariadic) { + if (extInfo.getCC() == CC_Default) { + CallingConv CC = CGT.getContext().getDefaultCXXMethodCallConv(isVariadic); + extInfo = extInfo.withCallingConv(CC); + } +} + +/// Arrange the argument and result information for a free function (i.e. +/// not a C++ or ObjC instance method) of the given type. +static const CGFunctionInfo &arrangeCXXMethodType(CodeGenTypes &CGT, + SmallVectorImpl<CanQualType> &prefix, + CanQual<FunctionProtoType> FTP) { + FunctionType::ExtInfo extInfo = FTP->getExtInfo(); + adjustCXXMethodInfo(CGT, extInfo, FTP->isVariadic()); + return arrangeLLVMFunctionInfo(CGT, prefix, FTP, extInfo); } /// Arrange the argument and result information for a value of the -/// given function type. +/// given freestanding function type. const CGFunctionInfo & -CodeGenTypes::arrangeFunctionType(CanQual<FunctionProtoType> FTP) { +CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionProtoType> FTP) { SmallVector<CanQualType, 16> argTypes; - return ::arrangeFunctionType(*this, argTypes, FTP); + return ::arrangeFreeFunctionType(*this, argTypes, FTP); } static CallingConv getCallingConventionForDecl(const Decl *D) { @@ -134,7 +163,7 @@ CodeGenTypes::arrangeCXXMethodType(const CXXRecordDecl *RD, // Add the 'this' pointer. argTypes.push_back(GetThisType(Context, RD)); - return ::arrangeFunctionType(*this, argTypes, + return ::arrangeCXXMethodType(*this, argTypes, FTP->getCanonicalTypeUnqualified().getAs<FunctionProtoType>()); } @@ -154,7 +183,7 @@ CodeGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *MD) { return arrangeCXXMethodType(MD->getParent(), prototype.getTypePtr()); } - return arrangeFunctionType(prototype); + return arrangeFreeFunctionType(prototype); } /// Arrange the argument and result information for a declaration @@ -176,7 +205,9 @@ CodeGenTypes::arrangeCXXConstructorDeclaration(const CXXConstructorDecl *D, for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) argTypes.push_back(FTP->getArgType(i)); - return arrangeFunctionType(resultType, argTypes, FTP->getExtInfo(), required); + FunctionType::ExtInfo extInfo = FTP->getExtInfo(); + adjustCXXMethodInfo(*this, extInfo, FTP->isVariadic()); + return arrangeLLVMFunctionInfo(resultType, argTypes, extInfo, required); } /// Arrange the argument and result information for a declaration, @@ -193,9 +224,12 @@ CodeGenTypes::arrangeCXXDestructor(const CXXDestructorDecl *D, CanQual<FunctionProtoType> FTP = GetFormalType(D); assert(FTP->getNumArgs() == 0 && "dtor with formal parameters"); + assert(FTP->isVariadic() == 0 && "dtor with formal parameters"); - return arrangeFunctionType(resultType, argTypes, FTP->getExtInfo(), - RequiredArgs::All); + FunctionType::ExtInfo extInfo = FTP->getExtInfo(); + adjustCXXMethodInfo(*this, extInfo, false); + return arrangeLLVMFunctionInfo(resultType, argTypes, extInfo, + RequiredArgs::All); } /// Arrange the argument and result information for the declaration or @@ -214,14 +248,14 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) { // non-variadic type. if (isa<FunctionNoProtoType>(FTy)) { CanQual<FunctionNoProtoType> noProto = FTy.getAs<FunctionNoProtoType>(); - return arrangeFunctionType(noProto->getResultType(), - ArrayRef<CanQualType>(), - noProto->getExtInfo(), - RequiredArgs::All); + return arrangeLLVMFunctionInfo(noProto->getResultType(), + ArrayRef<CanQualType>(), + noProto->getExtInfo(), + RequiredArgs::All); } assert(isa<FunctionProtoType>(FTy)); - return arrangeFunctionType(FTy.getAs<FunctionProtoType>()); + return arrangeFreeFunctionType(FTy.getAs<FunctionProtoType>()); } /// Arrange the argument and result information for the declaration or @@ -261,8 +295,8 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, RequiredArgs required = (MD->isVariadic() ? RequiredArgs(argTys.size()) : RequiredArgs::All); - return arrangeFunctionType(GetReturnType(MD->getResultType()), argTys, - einfo, required); + return arrangeLLVMFunctionInfo(GetReturnType(MD->getResultType()), argTys, + einfo, required); } const CGFunctionInfo & @@ -284,8 +318,8 @@ CodeGenTypes::arrangeGlobalDeclaration(GlobalDecl GD) { /// because the function might be unprototyped, in which case it's /// target-dependent in crazy ways. const CGFunctionInfo & -CodeGenTypes::arrangeFunctionCall(const CallArgList &args, - const FunctionType *fnType) { +CodeGenTypes::arrangeFreeFunctionCall(const CallArgList &args, + const FunctionType *fnType) { RequiredArgs required = RequiredArgs::All; if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) { if (proto->isVariadic()) @@ -295,22 +329,39 @@ CodeGenTypes::arrangeFunctionCall(const CallArgList &args, required = RequiredArgs(0); } - return arrangeFunctionCall(fnType->getResultType(), args, - fnType->getExtInfo(), required); + return arrangeFreeFunctionCall(fnType->getResultType(), args, + fnType->getExtInfo(), required); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeFreeFunctionCall(QualType resultType, + const CallArgList &args, + FunctionType::ExtInfo info, + RequiredArgs required) { + // FIXME: Kill copy. + SmallVector<CanQualType, 16> argTypes; + for (CallArgList::const_iterator i = args.begin(), e = args.end(); + i != e; ++i) + argTypes.push_back(Context.getCanonicalParamType(i->Ty)); + return arrangeLLVMFunctionInfo(GetReturnType(resultType), argTypes, info, + required); } +/// Arrange a call to a C++ method, passing the given arguments. const CGFunctionInfo & -CodeGenTypes::arrangeFunctionCall(QualType resultType, - const CallArgList &args, - const FunctionType::ExtInfo &info, - RequiredArgs required) { +CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, + const FunctionProtoType *FPT, + RequiredArgs required) { // FIXME: Kill copy. SmallVector<CanQualType, 16> argTypes; for (CallArgList::const_iterator i = args.begin(), e = args.end(); i != e; ++i) argTypes.push_back(Context.getCanonicalParamType(i->Ty)); - return arrangeFunctionType(GetReturnType(resultType), argTypes, info, - required); + + FunctionType::ExtInfo info = FPT->getExtInfo(); + adjustCXXMethodInfo(*this, info, FPT->isVariadic()); + return arrangeLLVMFunctionInfo(GetReturnType(FPT->getResultType()), + argTypes, info, required); } const CGFunctionInfo & @@ -326,23 +377,23 @@ CodeGenTypes::arrangeFunctionDeclaration(QualType resultType, RequiredArgs required = (isVariadic ? RequiredArgs(args.size()) : RequiredArgs::All); - return arrangeFunctionType(GetReturnType(resultType), argTypes, info, - required); + return arrangeLLVMFunctionInfo(GetReturnType(resultType), argTypes, info, + required); } const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() { - return arrangeFunctionType(getContext().VoidTy, ArrayRef<CanQualType>(), - FunctionType::ExtInfo(), RequiredArgs::All); + return arrangeLLVMFunctionInfo(getContext().VoidTy, ArrayRef<CanQualType>(), + FunctionType::ExtInfo(), RequiredArgs::All); } /// Arrange the argument and result information for an abstract value /// of a given function type. This is the method which all of the /// above functions ultimately defer to. const CGFunctionInfo & -CodeGenTypes::arrangeFunctionType(CanQualType resultType, - ArrayRef<CanQualType> argTypes, - const FunctionType::ExtInfo &info, - RequiredArgs required) { +CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType, + ArrayRef<CanQualType> argTypes, + FunctionType::ExtInfo info, + RequiredArgs required) { #ifndef NDEBUG for (ArrayRef<CanQualType>::const_iterator I = argTypes.begin(), E = argTypes.end(); I != E; ++I) @@ -445,10 +496,9 @@ void CodeGenTypes::GetExpandedTypes(QualType type, } else { for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i) { - const FieldDecl *FD = *i; - assert(!FD->isBitField() && + assert(!i->isBitField() && "Cannot expand structure with bit-field members."); - GetExpandedTypes(FD->getType(), expandedTypes); + GetExpandedTypes(i->getType(), expandedTypes); } } } else if (const ComplexType *CT = type->getAs<ComplexType>()) { @@ -933,14 +983,18 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, case ABIArgInfo::Ignore: break; - case ABIArgInfo::Indirect: - PAL.push_back(llvm::AttributeWithIndex::get(Index, - llvm::Attribute::StructRet)); + case ABIArgInfo::Indirect: { + llvm::Attributes SRETAttrs = llvm::Attribute::StructRet; + if (RetAI.getInReg()) + SRETAttrs |= llvm::Attribute::InReg; + PAL.push_back(llvm::AttributeWithIndex::get(Index, SRETAttrs)); + ++Index; // sret disables readnone and readonly FuncAttrs &= ~(llvm::Attribute::ReadOnly | llvm::Attribute::ReadNone); break; + } case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); @@ -949,14 +1003,6 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, if (RetAttrs) PAL.push_back(llvm::AttributeWithIndex::get(0, RetAttrs)); - // FIXME: RegParm should be reduced in case of global register variable. - signed RegParm; - if (FI.getHasRegParm()) - RegParm = FI.getRegParm(); - else - RegParm = CodeGenOpts.NumRegisterParameters; - - unsigned PointerWidth = getContext().getTargetInfo().getPointerWidth(0); for (CGFunctionInfo::const_arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) { QualType ParamType = it->type; @@ -974,22 +1020,22 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, Attrs |= llvm::Attribute::ZExt; // FALL THROUGH case ABIArgInfo::Direct: - if (RegParm > 0 && - (ParamType->isIntegerType() || ParamType->isPointerType() || - ParamType->isReferenceType())) { - RegParm -= - (Context.getTypeSize(ParamType) + PointerWidth - 1) / PointerWidth; - if (RegParm >= 0) + if (AI.getInReg()) Attrs |= llvm::Attribute::InReg; - } + // FIXME: handle sseregparm someday... // Increment Index if there is padding. Index += (AI.getPaddingType() != 0); if (llvm::StructType *STy = - dyn_cast<llvm::StructType>(AI.getCoerceToType())) - Index += STy->getNumElements()-1; // 1 will be added below. + dyn_cast<llvm::StructType>(AI.getCoerceToType())) { + unsigned Extra = STy->getNumElements()-1; // 1 will be added below. + if (Attrs != llvm::Attribute::None) + for (unsigned I = 0; I < Extra; ++I) + PAL.push_back(llvm::AttributeWithIndex::get(Index + I, Attrs)); + Index += Extra; + } break; case ABIArgInfo::Indirect: @@ -1355,7 +1401,8 @@ static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF, static llvm::Value *tryRemoveRetainOfSelf(CodeGenFunction &CGF, llvm::Value *result) { // This is only applicable to a method with an immutable 'self'. - const ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CGF.CurCodeDecl); + const ObjCMethodDecl *method = + dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl); if (!method) return 0; const VarDecl *self = method->getSelfDecl(); if (!self->getType().isConstQualified()) return 0; @@ -2066,8 +2113,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, unsigned CallingConv; CodeGen::AttributeListType AttributeList; CGM.ConstructAttributeList(CallInfo, TargetDecl, AttributeList, CallingConv); - llvm::AttrListPtr Attrs = llvm::AttrListPtr::get(AttributeList.begin(), - AttributeList.end()); + llvm::AttrListPtr Attrs = llvm::AttrListPtr::get(AttributeList); llvm::BasicBlock *InvokeDest = 0; if (!(Attrs.getFnAttributes() & llvm::Attribute::NoUnwind)) diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 2aedf95e6a11..e37fa3ab72b7 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -105,30 +105,28 @@ CodeGenFunction::GetAddressOfDirectBaseInCompleteClass(llvm::Value *This, } static llvm::Value * -ApplyNonVirtualAndVirtualOffset(CodeGenFunction &CGF, llvm::Value *ThisPtr, - CharUnits NonVirtual, llvm::Value *Virtual) { - llvm::Type *PtrDiffTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - - llvm::Value *NonVirtualOffset = 0; - if (!NonVirtual.isZero()) - NonVirtualOffset = llvm::ConstantInt::get(PtrDiffTy, - NonVirtual.getQuantity()); - - llvm::Value *BaseOffset; - if (Virtual) { - if (NonVirtualOffset) - BaseOffset = CGF.Builder.CreateAdd(Virtual, NonVirtualOffset); - else - BaseOffset = Virtual; - } else - BaseOffset = NonVirtualOffset; +ApplyNonVirtualAndVirtualOffset(CodeGenFunction &CGF, llvm::Value *ptr, + CharUnits nonVirtualOffset, + llvm::Value *virtualOffset) { + // Assert that we have something to do. + assert(!nonVirtualOffset.isZero() || virtualOffset != 0); + + // Compute the offset from the static and dynamic components. + llvm::Value *baseOffset; + if (!nonVirtualOffset.isZero()) { + baseOffset = llvm::ConstantInt::get(CGF.PtrDiffTy, + nonVirtualOffset.getQuantity()); + if (virtualOffset) { + baseOffset = CGF.Builder.CreateAdd(virtualOffset, baseOffset); + } + } else { + baseOffset = virtualOffset; + } // Apply the base offset. - ThisPtr = CGF.Builder.CreateBitCast(ThisPtr, CGF.Int8PtrTy); - ThisPtr = CGF.Builder.CreateGEP(ThisPtr, BaseOffset, "add.ptr"); - - return ThisPtr; + ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy); + ptr = CGF.Builder.CreateInBoundsGEP(ptr, baseOffset, "add.ptr"); + return ptr; } llvm::Value * @@ -142,72 +140,81 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, CastExpr::path_const_iterator Start = PathBegin; const CXXRecordDecl *VBase = 0; - // Get the virtual base. + // Sema has done some convenient canonicalization here: if the + // access path involved any virtual steps, the conversion path will + // *start* with a step down to the correct virtual base subobject, + // and hence will not require any further steps. if ((*Start)->isVirtual()) { VBase = cast<CXXRecordDecl>((*Start)->getType()->getAs<RecordType>()->getDecl()); ++Start; } - + + // Compute the static offset of the ultimate destination within its + // allocating subobject (the virtual base, if there is one, or else + // the "complete" object that we see). CharUnits NonVirtualOffset = ComputeNonVirtualBaseClassOffset(getContext(), VBase ? VBase : Derived, Start, PathEnd); + // If there's a virtual step, we can sometimes "devirtualize" it. + // For now, that's limited to when the derived type is final. + // TODO: "devirtualize" this for accesses to known-complete objects. + if (VBase && Derived->hasAttr<FinalAttr>()) { + const ASTRecordLayout &layout = getContext().getASTRecordLayout(Derived); + CharUnits vBaseOffset = layout.getVBaseClassOffset(VBase); + NonVirtualOffset += vBaseOffset; + VBase = 0; // we no longer have a virtual step + } + // Get the base pointer type. llvm::Type *BasePtrTy = ConvertType((PathEnd[-1])->getType())->getPointerTo(); - + + // If the static offset is zero and we don't have a virtual step, + // just do a bitcast; null checks are unnecessary. if (NonVirtualOffset.isZero() && !VBase) { - // Just cast back. return Builder.CreateBitCast(Value, BasePtrTy); } + + llvm::BasicBlock *origBB = 0; + llvm::BasicBlock *endBB = 0; - llvm::BasicBlock *CastNull = 0; - llvm::BasicBlock *CastNotNull = 0; - llvm::BasicBlock *CastEnd = 0; - + // Skip over the offset (and the vtable load) if we're supposed to + // null-check the pointer. if (NullCheckValue) { - CastNull = createBasicBlock("cast.null"); - CastNotNull = createBasicBlock("cast.notnull"); - CastEnd = createBasicBlock("cast.end"); + origBB = Builder.GetInsertBlock(); + llvm::BasicBlock *notNullBB = createBasicBlock("cast.notnull"); + endBB = createBasicBlock("cast.end"); - llvm::Value *IsNull = Builder.CreateIsNull(Value); - Builder.CreateCondBr(IsNull, CastNull, CastNotNull); - EmitBlock(CastNotNull); + llvm::Value *isNull = Builder.CreateIsNull(Value); + Builder.CreateCondBr(isNull, endBB, notNullBB); + EmitBlock(notNullBB); } + // Compute the virtual offset. llvm::Value *VirtualOffset = 0; - if (VBase) { - if (Derived->hasAttr<FinalAttr>()) { - VirtualOffset = 0; - - const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Derived); - - CharUnits VBaseOffset = Layout.getVBaseClassOffset(VBase); - NonVirtualOffset += VBaseOffset; - } else - VirtualOffset = GetVirtualBaseClassOffset(Value, Derived, VBase); + VirtualOffset = GetVirtualBaseClassOffset(Value, Derived, VBase); } - // Apply the offsets. + // Apply both offsets. Value = ApplyNonVirtualAndVirtualOffset(*this, Value, NonVirtualOffset, VirtualOffset); - // Cast back. + // Cast to the destination type. Value = Builder.CreateBitCast(Value, BasePtrTy); - + + // Build a phi if we needed a null check. if (NullCheckValue) { - Builder.CreateBr(CastEnd); - EmitBlock(CastNull); - Builder.CreateBr(CastEnd); - EmitBlock(CastEnd); + llvm::BasicBlock *notNullBB = Builder.GetInsertBlock(); + Builder.CreateBr(endBB); + EmitBlock(endBB); - llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2); - PHI->addIncoming(Value, CastNotNull); - PHI->addIncoming(llvm::Constant::getNullValue(Value->getType()), - CastNull); + llvm::PHINode *PHI = Builder.CreatePHI(BasePtrTy, 2, "cast.result"); + PHI->addIncoming(Value, notNullBB); + PHI->addIncoming(llvm::Constant::getNullValue(BasePtrTy), origBB); Value = PHI; } @@ -556,16 +563,19 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, llvm::Value *ThisPtr = CGF.LoadCXXThis(); QualType RecordTy = CGF.getContext().getTypeDeclType(ClassDecl); - LValue LHS; + LValue LHS = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy); - // If we are initializing an anonymous union field, drill down to the field. if (MemberInit->isIndirectMemberInitializer()) { - LHS = CGF.EmitLValueForAnonRecordField(ThisPtr, - MemberInit->getIndirectMember(), 0); + // If we are initializing an anonymous union field, drill down to + // the field. + IndirectFieldDecl *IndirectField = MemberInit->getIndirectMember(); + IndirectFieldDecl::chain_iterator I = IndirectField->chain_begin(), + IEnd = IndirectField->chain_end(); + for ( ; I != IEnd; ++I) + LHS = CGF.EmitLValueForFieldInitialization(LHS, cast<FieldDecl>(*I)); FieldType = MemberInit->getIndirectMember()->getAnonField()->getType(); } else { - LValue ThisLHSLV = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy); - LHS = CGF.EmitLValueForFieldInitialization(ThisLHSLV, Field); + LHS = CGF.EmitLValueForFieldInitialization(LHS, Field); } // Special case: if we are in a copy or move constructor, and we are copying @@ -717,7 +727,8 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { // Before we go any further, try the complete->base constructor // delegation optimization. - if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor)) { + if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) && + CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) { if (CGDebugInfo *DI = getDebugInfo()) DI->EmitLocation(Builder, Ctor->getLocEnd()); EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args); @@ -916,7 +927,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { // Enter the cleanup scopes for virtual bases. EnterDtorCleanups(Dtor, Dtor_Complete); - if (!isTryBody) { + if (!isTryBody && CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) { EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false, LoadCXXThis()); break; @@ -1226,7 +1237,8 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, CallExpr::const_arg_iterator ArgEnd) { CGDebugInfo *DI = getDebugInfo(); - if (DI && CGM.getCodeGenOpts().LimitDebugInfo) { + if (DI && + CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo) { // If debug info for this class has not been emitted then this is the // right time to do so. const CXXRecordDecl *Parent = D->getParent(); @@ -1308,8 +1320,8 @@ CodeGenFunction::EmitSynthesizedCXXCopyCtorCall(const CXXConstructorDecl *D, EmitCallArg(Args, *Arg, ArgType); } - EmitCall(CGM.getTypes().arrangeFunctionCall(Args, FPT), Callee, - ReturnValueSlot(), Args, D); + EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, RequiredArgs::All), + Callee, ReturnValueSlot(), Args, D); } void @@ -1742,38 +1754,42 @@ CodeGenFunction::EmitCXXOperatorMemberCallee(const CXXOperatorCallExpr *E, return CGM.GetAddrOfFunction(MD, fnType); } -void CodeGenFunction::EmitForwardingCallToLambda(const CXXRecordDecl *Lambda, - CallArgList &CallArgs) { +void CodeGenFunction::EmitForwardingCallToLambda(const CXXRecordDecl *lambda, + CallArgList &callArgs) { // Lookup the call operator - DeclarationName Name + DeclarationName operatorName = getContext().DeclarationNames.getCXXOperatorName(OO_Call); - DeclContext::lookup_const_result Calls = Lambda->lookup(Name); - CXXMethodDecl *CallOperator = cast<CXXMethodDecl>(*Calls.first++); - const FunctionProtoType *FPT = - CallOperator->getType()->getAs<FunctionProtoType>(); - QualType ResultType = FPT->getResultType(); + CXXMethodDecl *callOperator = + cast<CXXMethodDecl>(*lambda->lookup(operatorName).first); // Get the address of the call operator. - GlobalDecl GD(CallOperator); - const CGFunctionInfo &CalleeFnInfo = - CGM.getTypes().arrangeFunctionCall(ResultType, CallArgs, FPT->getExtInfo(), - RequiredArgs::forPrototypePlus(FPT, 1)); - llvm::Type *Ty = CGM.getTypes().GetFunctionType(CalleeFnInfo); - llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty); - - // Determine whether we have a return value slot to use. - ReturnValueSlot Slot; - if (!ResultType->isVoidType() && - CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect && - hasAggregateLLVMType(CurFnInfo->getReturnType())) - Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified()); + const CGFunctionInfo &calleeFnInfo = + CGM.getTypes().arrangeCXXMethodDeclaration(callOperator); + llvm::Value *callee = + CGM.GetAddrOfFunction(GlobalDecl(callOperator), + CGM.getTypes().GetFunctionType(calleeFnInfo)); + + // Prepare the return slot. + const FunctionProtoType *FPT = + callOperator->getType()->castAs<FunctionProtoType>(); + QualType resultType = FPT->getResultType(); + ReturnValueSlot returnSlot; + if (!resultType->isVoidType() && + calleeFnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect && + hasAggregateLLVMType(calleeFnInfo.getReturnType())) + returnSlot = ReturnValueSlot(ReturnValue, resultType.isVolatileQualified()); + + // We don't need to separately arrange the call arguments because + // the call can't be variadic anyway --- it's impossible to forward + // variadic arguments. // Now emit our call. - RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, CallOperator); + RValue RV = EmitCall(calleeFnInfo, callee, returnSlot, + callArgs, callOperator); - // Forward the returned value - if (!ResultType->isVoidType() && Slot.isNull()) - EmitReturnOfRValue(RV, ResultType); + // If necessary, copy the returned value into the slot. + if (!resultType->isVoidType() && returnSlot.isNull()) + EmitReturnOfRValue(RV, resultType); } void CodeGenFunction::EmitLambdaBlockInvokeBody() { diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp index b00e2a21bf77..f9ea7e0a26a7 100644 --- a/lib/CodeGen/CGCleanup.cpp +++ b/lib/CodeGen/CGCleanup.cpp @@ -831,8 +831,12 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { EmitBlock(EHEntry); - cleanupFlags.setIsForEHCleanup(); - EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag); + // We only actually emit the cleanup code if the cleanup is either + // active or was used before it was deactivated. + if (EHActiveFlag || IsActive) { + cleanupFlags.setIsForEHCleanup(); + EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag); + } Builder.CreateBr(getEHDispatchBlock(EHParent)); diff --git a/lib/CodeGen/CGCleanup.h b/lib/CodeGen/CGCleanup.h index 7726e442c025..d8dbe41bf0f3 100644 --- a/lib/CodeGen/CGCleanup.h +++ b/lib/CodeGen/CGCleanup.h @@ -131,7 +131,7 @@ public: /// A scope which attempts to handle some, possibly all, types of /// exceptions. /// -/// Objective C @finally blocks are represented using a cleanup scope +/// Objective C \@finally blocks are represented using a cleanup scope /// after the catch scope. class EHCatchScope : public EHScope { // In effect, we have a flexible array member diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index d286d24715eb..00127ac72f4d 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -320,7 +320,7 @@ void CGDebugInfo::CreateCompileUnit() { // Figure out which version of the ObjC runtime we have. unsigned RuntimeVers = 0; if (LO.ObjC1) - RuntimeVers = LO.ObjCNonFragileABI ? 2 : 1; + RuntimeVers = LO.ObjCRuntime.isNonFragile() ? 2 : 1; // Create new compile unit. DBuilder.createCompileUnit( @@ -335,7 +335,7 @@ void CGDebugInfo::CreateCompileUnit() { /// one if necessary. llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) { unsigned Encoding = 0; - const char *BTName = NULL; + StringRef BTName; switch (BT->getKind()) { #define BUILTIN_TYPE(Id, SingletonId) #define PLACEHOLDER_TYPE(Id, SingletonId) \ @@ -350,8 +350,8 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) { return llvm::DIType(); case BuiltinType::ObjCClass: return DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, - "objc_class", getOrCreateMainFile(), - 0); + "objc_class", TheCU, + getOrCreateMainFile(), 0); case BuiltinType::ObjCId: { // typedef struct objc_class *Class; // typedef struct objc_object { @@ -361,8 +361,7 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) { // TODO: Cache these two types to avoid duplicates. llvm::DIType OCTy = DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, - "objc_class", getOrCreateMainFile(), - 0); + "objc_class", TheCU, getOrCreateMainFile(), 0); unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy); llvm::DIType ISATy = DBuilder.createPointerType(OCTy, Size); @@ -382,7 +381,7 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) { case BuiltinType::ObjCSel: { return DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, - "objc_selector", getOrCreateMainFile(), + "objc_selector", TheCU, getOrCreateMainFile(), 0); } case BuiltinType::UChar: @@ -514,7 +513,7 @@ llvm::DIType CGDebugInfo::createRecordFwdDecl(const RecordDecl *RD, llvm_unreachable("Unknown RecordDecl type!"); // Create the type. - return DBuilder.createForwardDecl(Tag, RDName, DefUnit, Line); + return DBuilder.createForwardDecl(Tag, RDName, Ctx, DefUnit, Line); } // Walk up the context chain and create forward decls for record decls, @@ -547,7 +546,7 @@ llvm::DIDescriptor CGDebugInfo::createContextChain(const Decl *Context) { /// then emit record's fwd if debug info size reduction is enabled. llvm::DIType CGDebugInfo::CreatePointeeType(QualType PointeeTy, llvm::DIFile Unit) { - if (!CGM.getCodeGenOpts().LimitDebugInfo) + if (CGM.getCodeGenOpts().DebugInfo != CodeGenOptions::LimitedDebugInfo) return getOrCreateType(PointeeTy, Unit); // Limit debug info for the pointee type. @@ -577,8 +576,10 @@ llvm::DIType CGDebugInfo::CreatePointerLikeType(unsigned Tag, const Type *Ty, QualType PointeeTy, llvm::DIFile Unit) { - if (Tag == llvm::dwarf::DW_TAG_reference_type) - return DBuilder.createReferenceType(CreatePointeeType(PointeeTy, Unit)); + if (Tag == llvm::dwarf::DW_TAG_reference_type || + Tag == llvm::dwarf::DW_TAG_rvalue_reference_type) + return DBuilder.createReferenceType(Tag, + CreatePointeeType(PointeeTy, Unit)); // Bit size, align and offset of the type. // Size is always the size of a pointer. We can't use getTypeSize here @@ -683,15 +684,13 @@ llvm::DIType CGDebugInfo::CreateType(const FunctionType *Ty, // FIXME: IF NOT, HOW IS THIS REPRESENTED? llvm-gcc doesn't represent '...'! if (isa<FunctionNoProtoType>(Ty)) EltTys.push_back(DBuilder.createUnspecifiedParameter()); - else if (const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(Ty)) { - for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) - EltTys.push_back(getOrCreateType(FTP->getArgType(i), Unit)); + else if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(Ty)) { + for (unsigned i = 0, e = FPT->getNumArgs(); i != e; ++i) + EltTys.push_back(getOrCreateType(FPT->getArgType(i), Unit)); } llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(EltTys); - - llvm::DIType DbgTy = DBuilder.createSubroutineType(Unit, EltTypeArray); - return DbgTy; + return DBuilder.createSubroutineType(Unit, EltTypeArray); } @@ -765,7 +764,7 @@ CollectRecordFields(const RecordDecl *record, llvm::DIFile tunit, const ASTRecordLayout &layout = CGM.getContext().getASTRecordLayout(record); const CXXRecordDecl *CXXDecl = dyn_cast<CXXRecordDecl>(record); - // For C++11 Lambdas a Fields will be the same as a Capture, but the Capture + // For C++11 Lambdas a Field will be the same as a Capture, but the Capture // has the name and the location of the variable so we should iterate over // both concurrently. if (CXXDecl && CXXDecl->isLambda()) { @@ -912,7 +911,7 @@ CGDebugInfo::CreateCXXMemberFunction(const CXXMethodDecl *Method, StringRef MethodName = getFunctionName(Method); llvm::DIType MethodTy = getOrCreateMethodType(Method, Unit); - + // Since a single ctor/dtor corresponds to multiple functions, it doesn't // make sense to give a single ctor/dtor a linkage name. StringRef MethodLinkageName; @@ -992,15 +991,17 @@ CollectCXXMemberFunctions(const CXXRecordDecl *RD, llvm::DIFile Unit, if (D->isImplicit() && !D->isUsed()) continue; - if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) - EltTys.push_back(CreateCXXMemberFunction(Method, Unit, RecordTy)); + if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) { + // Only emit debug information for user provided functions, we're + // unlikely to want info for artificial functions. + if (Method->isUserProvided()) + EltTys.push_back(CreateCXXMemberFunction(Method, Unit, RecordTy)); + } else if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D)) for (FunctionTemplateDecl::spec_iterator SI = FTD->spec_begin(), - SE = FTD->spec_end(); SI != SE; ++SI) { - FunctionDecl *FD = *SI; - if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(FD)) - EltTys.push_back(CreateCXXMemberFunction(M, Unit, RecordTy)); - } + SE = FTD->spec_end(); SI != SE; ++SI) + EltTys.push_back(CreateCXXMemberFunction(cast<CXXMethodDecl>(*SI), Unit, + RecordTy)); } } @@ -1047,7 +1048,7 @@ CollectCXXBases(const CXXRecordDecl *RD, llvm::DIFile Unit, .getVirtualBaseOffsetOffset(RD, Base).getQuantity(); BFlags = llvm::DIDescriptor::FlagVirtual; } else - BaseOffset = RL.getBaseClassOffsetInBits(Base); + BaseOffset = CGM.getContext().toBits(RL.getBaseClassOffset(Base)); // FIXME: Inconsistent units for BaseOffset. It is in bytes when // BI->isVirtual() and bits when not. @@ -1083,7 +1084,7 @@ CollectTemplateParams(const TemplateParameterList *TPList, llvm::DIType TTy = getOrCreateType(TA.getIntegralType(), Unit); llvm::DITemplateValueParameter TVP = DBuilder.createTemplateValueParameter(TheCU, ND->getName(), TTy, - TA.getAsIntegral()->getZExtValue()); + TA.getAsIntegral().getZExtValue()); TemplateParams.push_back(TVP); } } @@ -1177,6 +1178,7 @@ CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile Unit, /// getOrCreateRecordType - Emit record type's standalone debug info. llvm::DIType CGDebugInfo::getOrCreateRecordType(QualType RTy, SourceLocation Loc) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); llvm::DIType T = getOrCreateType(RTy, getOrCreateFile(Loc)); return T; } @@ -1185,6 +1187,7 @@ llvm::DIType CGDebugInfo::getOrCreateRecordType(QualType RTy, /// debug info. llvm::DIType CGDebugInfo::getOrCreateInterfaceType(QualType D, SourceLocation Loc) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); llvm::DIType T = getOrCreateType(D, getOrCreateFile(Loc)); DBuilder.retainType(T); return T; @@ -1287,7 +1290,7 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, if (!Def) { llvm::DIType FwdDecl = DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, - ID->getName(), DefUnit, Line, + ID->getName(), TheCU, DefUnit, Line, RuntimeLang); return FwdDecl; } @@ -1385,8 +1388,8 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, // the non-fragile abi and the debugger should ignore the value anyways. // Call it the FieldNo+1 due to how debuggers use the information, // e.g. negating the value when it needs a lookup in the dynamic table. - uint64_t FieldOffset = CGM.getLangOpts().ObjCNonFragileABI ? FieldNo+1 - : RL.getFieldOffset(FieldNo); + uint64_t FieldOffset = CGM.getLangOpts().ObjCRuntime.isNonFragile() + ? FieldNo+1 : RL.getFieldOffset(FieldNo); unsigned Flags = 0; if (Field->getAccessControl() == ObjCIvarDecl::Protected) @@ -1456,7 +1459,6 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty, uint64_t Size; uint64_t Align; - // FIXME: make getTypeAlign() aware of VLAs and incomplete array types if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(Ty)) { Size = 0; @@ -1464,7 +1466,10 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty, CGM.getContext().getTypeAlign(CGM.getContext().getBaseElementType(VAT)); } else if (Ty->isIncompleteArrayType()) { Size = 0; - Align = CGM.getContext().getTypeAlign(Ty->getElementType()); + if (Ty->getElementType()->isIncompleteType()) + Align = 0; + else + Align = CGM.getContext().getTypeAlign(Ty->getElementType()); } else if (Ty->isDependentSizedArrayType() || Ty->isIncompleteType()) { Size = 0; Align = 0; @@ -1479,25 +1484,21 @@ llvm::DIType CGDebugInfo::CreateType(const ArrayType *Ty, // obvious/recursive way? SmallVector<llvm::Value *, 8> Subscripts; QualType EltTy(Ty, 0); - if (Ty->isIncompleteArrayType()) + while ((Ty = dyn_cast<ArrayType>(EltTy))) { + int64_t UpperBound = 0; + int64_t LowerBound = 0; + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) { + if (CAT->getSize().getZExtValue()) + UpperBound = CAT->getSize().getZExtValue() - 1; + } else + // This is an unbounded array. Use Low = 1, Hi = 0 to express such + // arrays. + LowerBound = 1; + + // FIXME: Verify this is right for VLAs. + Subscripts.push_back(DBuilder.getOrCreateSubrange(LowerBound, + UpperBound)); EltTy = Ty->getElementType(); - else { - while ((Ty = dyn_cast<ArrayType>(EltTy))) { - int64_t UpperBound = 0; - int64_t LowerBound = 0; - if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) { - if (CAT->getSize().getZExtValue()) - UpperBound = CAT->getSize().getZExtValue() - 1; - } else - // This is an unbounded array. Use Low = 1, Hi = 0 to express such - // arrays. - LowerBound = 1; - - // FIXME: Verify this is right for VLAs. - Subscripts.push_back(DBuilder.getOrCreateSubrange(LowerBound, - UpperBound)); - EltTy = Ty->getElementType(); - } } llvm::DIArray SubscriptArray = DBuilder.getOrCreateArray(Subscripts); @@ -1537,7 +1538,7 @@ llvm::DIType CGDebugInfo::CreateType(const MemberPointerType *Ty, uint64_t FieldOffset = 0; llvm::Value *ElementTypes[2]; - // FIXME: This should probably be a function type instead. + // FIXME: This should be a DW_TAG_pointer_to_member type. ElementTypes[0] = DBuilder.createMemberType(U, "ptr", U, 0, Info.first, Info.second, FieldOffset, 0, @@ -1565,7 +1566,6 @@ llvm::DIType CGDebugInfo::CreateType(const AtomicType *Ty, /// CreateEnumType - get enumeration type. llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) { - llvm::DIFile Unit = getOrCreateFile(ED->getLocation()); SmallVector<llvm::Value *, 16> Enumerators; // Create DIEnumerator elements for each enumerator. @@ -1590,9 +1590,13 @@ llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) { } llvm::DIDescriptor EnumContext = getContextDescriptor(cast<Decl>(ED->getDeclContext())); + llvm::DIType ClassTy = ED->isScopedUsingClassTag() ? + getOrCreateType(ED->getIntegerType(), DefUnit) : llvm::DIType(); + unsigned Flags = !ED->isCompleteDefinition() ? llvm::DIDescriptor::FlagFwdDecl : 0; llvm::DIType DbgTy = DBuilder.createEnumerationType(EnumContext, ED->getName(), DefUnit, Line, - Size, Align, EltArray); + Size, Align, EltArray, + ClassTy, Flags); return DbgTy; } @@ -1626,8 +1630,13 @@ static QualType UnwrapTypeForDebugInfo(QualType T) { case Type::Paren: T = cast<ParenType>(T)->getInnerType(); break; - case Type::SubstTemplateTypeParm: + case Type::SubstTemplateTypeParm: { + // We need to keep the qualifiers handy since getReplacementType() + // will strip them away. + unsigned Quals = T.getLocalFastQualifiers(); T = cast<SubstTemplateTypeParmType>(T)->getReplacementType(); + T.addFastQualifiers(Quals); + } break; case Type::Auto: T = cast<AutoType>(T)->getDeducedType(); @@ -1686,23 +1695,26 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) { // Unwrap the type as needed for debug information. Ty = UnwrapTypeForDebugInfo(Ty); - + llvm::DIType T = getCompletedTypeOrNull(Ty); - if (T.Verify()) return T; + if (T.Verify()) + return T; // Otherwise create the type. llvm::DIType Res = CreateTypeNode(Ty, Unit); llvm::DIType TC = getTypeOrNull(Ty); if (TC.Verify() && TC.isForwardDecl()) - ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), TC)); + ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), + static_cast<llvm::Value*>(TC))); // And update the type cache. TypeCache[Ty.getAsOpaquePtr()] = Res; if (!Res.isForwardDecl()) CompletedTypeCache[Ty.getAsOpaquePtr()] = Res; + return Res; } @@ -1807,7 +1819,8 @@ llvm::DIType CGDebugInfo::getOrCreateLimitedType(QualType Ty, llvm::DIType Res = CreateLimitedTypeNode(Ty, Unit); if (T.Verify() && T.isForwardDecl()) - ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), T)); + ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), + static_cast<llvm::Value*>(T))); // And update the type cache. TypeCache[Ty.getAsOpaquePtr()] = Res; @@ -1824,7 +1837,7 @@ llvm::DIType CGDebugInfo::CreateLimitedType(const RecordType *Ty) { StringRef RDName = RD->getName(); llvm::DIDescriptor RDContext; - if (CGM.getCodeGenOpts().LimitDebugInfo) + if (CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo) RDContext = createContextChain(cast<Decl>(RD->getDeclContext())); else RDContext = getContextDescriptor(cast<Decl>(RD->getDeclContext())); @@ -1953,6 +1966,7 @@ llvm::DISubprogram CGDebugInfo::getFunctionDeclaration(const Decl *D) { llvm::DIType CGDebugInfo::getOrCreateFunctionType(const Decl * D, QualType FnType, llvm::DIFile F) { + if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) return getOrCreateMethodType(Method, F); if (const ObjCMethodDecl *OMethod = dyn_cast<ObjCMethodDecl>(D)) { @@ -2013,18 +2027,21 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType, LinkageName = CGM.getMangledName(GD); Flags |= llvm::DIDescriptor::FlagPrototyped; } - if (LinkageName == Name) + if (LinkageName == Name || + CGM.getCodeGenOpts().DebugInfo <= CodeGenOptions::DebugLineTablesOnly) LinkageName = StringRef(); - if (const NamespaceDecl *NSDecl = - dyn_cast_or_null<NamespaceDecl>(FD->getDeclContext())) - FDContext = getOrCreateNameSpace(NSDecl); - else if (const RecordDecl *RDecl = - dyn_cast_or_null<RecordDecl>(FD->getDeclContext())) - FDContext = getContextDescriptor(cast<Decl>(RDecl->getDeclContext())); + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { + if (const NamespaceDecl *NSDecl = + dyn_cast_or_null<NamespaceDecl>(FD->getDeclContext())) + FDContext = getOrCreateNameSpace(NSDecl); + else if (const RecordDecl *RDecl = + dyn_cast_or_null<RecordDecl>(FD->getDeclContext())) + FDContext = getContextDescriptor(cast<Decl>(RDecl->getDeclContext())); - // Collect template parameters. - TParamsArray = CollectFunctionTemplateParams(FD, Unit); + // Collect template parameters. + TParamsArray = CollectFunctionTemplateParams(FD, Unit); + } } else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) { Name = getObjCMethodName(OMD); Flags |= llvm::DIDescriptor::FlagPrototyped; @@ -2040,14 +2057,27 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType, if (D->isImplicit()) Flags |= llvm::DIDescriptor::FlagArtificial; - llvm::DISubprogram SPDecl = getFunctionDeclaration(D); - llvm::DISubprogram SP = - DBuilder.createFunction(FDContext, Name, LinkageName, Unit, - LineNo, getOrCreateFunctionType(D, FnType, Unit), - Fn->hasInternalLinkage(), true/*definition*/, - getLineNumber(CurLoc), - Flags, CGM.getLangOpts().Optimize, Fn, - TParamsArray, SPDecl); + llvm::DIType DIFnType; + llvm::DISubprogram SPDecl; + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { + DIFnType = getOrCreateFunctionType(D, FnType, Unit); + SPDecl = getFunctionDeclaration(D); + } else { + // Create fake but valid subroutine type. Otherwise + // llvm::DISubprogram::Verify() would return false, and + // subprogram DIE will miss DW_AT_decl_file and + // DW_AT_decl_line fields. + SmallVector<llvm::Value*, 16> Elts; + llvm::DIArray EltTypeArray = DBuilder.getOrCreateArray(Elts); + DIFnType = DBuilder.createSubroutineType(Unit, EltTypeArray); + } + llvm::DISubprogram SP; + SP = DBuilder.createFunction(FDContext, Name, LinkageName, Unit, + LineNo, DIFnType, + Fn->hasInternalLinkage(), true/*definition*/, + getLineNumber(CurLoc), Flags, + CGM.getLangOpts().Optimize, + Fn, TParamsArray, SPDecl); // Push function on region stack. llvm::MDNode *SPN = SP; @@ -2205,6 +2235,7 @@ llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const ValueDecl *VD, void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, llvm::Value *Storage, unsigned ArgNo, CGBuilderTy &Builder) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); llvm::DIFile Unit = getOrCreateFile(VD->getLocation()); @@ -2224,14 +2255,14 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, // If Storage is an aggregate returned as 'sret' then let debugger know // about this. if (Arg->hasStructRetAttr()) - Ty = DBuilder.createReferenceType(Ty); + Ty = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, Ty); else if (CXXRecordDecl *Record = VD->getType()->getAsCXXRecordDecl()) { // If an aggregate variable has non trivial destructor or non trivial copy // constructor than it is pass indirectly. Let debug info know about this // by using reference of the aggregate type as a argument type. if (!Record->hasTrivialCopyConstructor() || !Record->hasTrivialDestructor()) - Ty = DBuilder.createReferenceType(Ty); + Ty = DBuilder.createReferenceType(llvm::dwarf::DW_TAG_reference_type, Ty); } } @@ -2272,8 +2303,25 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, DBuilder.insertDeclare(Storage, D, Builder.GetInsertBlock()); Call->setDebugLoc(llvm::DebugLoc::get(Line, Column, Scope)); return; - } + } else if (isa<VariableArrayType>(VD->getType())) { + // These are "complex" variables in that they need an op_deref. // Create the descriptor for the variable. + llvm::Value *Addr = llvm::ConstantInt::get(CGM.Int64Ty, + llvm::DIBuilder::OpDeref); + llvm::DIVariable D = + DBuilder.createComplexVariable(Tag, + llvm::DIDescriptor(Scope), + Name, Unit, Line, Ty, + Addr, ArgNo); + + // Insert an llvm.dbg.declare into the current block. + llvm::Instruction *Call = + DBuilder.insertDeclare(Storage, D, Builder.GetInsertBlock()); + Call->setDebugLoc(llvm::DebugLoc::get(Line, Column, Scope)); + return; + } + + // Create the descriptor for the variable. llvm::DIVariable D = DBuilder.createLocalVariable(Tag, llvm::DIDescriptor(Scope), Name, Unit, Line, Ty, @@ -2321,12 +2369,14 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); EmitDeclare(VD, llvm::dwarf::DW_TAG_auto_variable, Storage, 0, Builder); } void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder, const CGBlockInfo &blockInfo) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); if (Builder.GetInsertBlock() == 0) @@ -2387,6 +2437,7 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, unsigned ArgNo, CGBuilderTy &Builder) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); EmitDeclare(VD, llvm::dwarf::DW_TAG_arg_variable, AI, ArgNo, Builder); } @@ -2403,6 +2454,7 @@ namespace { void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, llvm::Value *addr, CGBuilderTy &Builder) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); ASTContext &C = CGM.getContext(); const BlockDecl *blockDecl = block.getBlockDecl(); @@ -2547,6 +2599,7 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, /// EmitGlobalVariable - Emit information about a global variable. void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, const VarDecl *D) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); // Create global variable debug descriptor. llvm::DIFile Unit = getOrCreateFile(D->getLocation()); unsigned LineNo = getLineNumber(D->getLocation()); @@ -2557,9 +2610,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, if (T->isIncompleteArrayType()) { // CodeGen turns int[] into int[1] so we'll do the same here. - llvm::APSInt ConstVal(32); - - ConstVal = 1; + llvm::APInt ConstVal(32, 1); QualType ET = CGM.getContext().getAsArrayType(T)->getElementType(); T = CGM.getContext().getConstantArrayType(ET, ConstVal, @@ -2582,6 +2633,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, /// EmitGlobalVariable - Emit information about an objective-c interface. void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, ObjCInterfaceDecl *ID) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); // Create global variable debug descriptor. llvm::DIFile Unit = getOrCreateFile(ID->getLocation()); unsigned LineNo = getLineNumber(ID->getLocation()); @@ -2592,9 +2644,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, if (T->isIncompleteArrayType()) { // CodeGen turns int[] into int[1] so we'll do the same here. - llvm::APSInt ConstVal(32); - - ConstVal = 1; + llvm::APInt ConstVal(32, 1); QualType ET = CGM.getContext().getAsArrayType(T)->getElementType(); T = CGM.getContext().getConstantArrayType(ET, ConstVal, @@ -2609,13 +2659,15 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, /// EmitGlobalVariable - Emit global variable's debug info. void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, llvm::Constant *Init) { + assert(CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo); // Create the descriptor for the variable. llvm::DIFile Unit = getOrCreateFile(VD->getLocation()); StringRef Name = VD->getName(); llvm::DIType Ty = getOrCreateType(VD->getType(), Unit); if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(VD)) { - if (const EnumDecl *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) - Ty = CreateEnumType(ED); + const EnumDecl *ED = cast<EnumDecl>(ECD->getDeclContext()); + assert(isa<EnumType>(ED->getTypeForDecl()) && "Enum without EnumType?"); + Ty = getOrCreateType(QualType(ED->getTypeForDecl(), 0), Unit); } // Do not use DIGlobalVariable for enums. if (Ty.getTag() == llvm::dwarf::DW_TAG_enumeration_type) diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index ec7705c05447..44cc49ade123 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -17,9 +17,9 @@ #include "clang/AST/Type.h" #include "clang/AST/Expr.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/DebugInfo.h" +#include "llvm/DIBuilder.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/Analysis/DebugInfo.h" -#include "llvm/Analysis/DIBuilder.h" #include "llvm/Support/ValueHandle.h" #include "llvm/Support/Allocator.h" @@ -30,6 +30,7 @@ namespace llvm { } namespace clang { + class CXXMethodDecl; class VarDecl; class ObjCInterfaceDecl; class ClassTemplateSpecializationDecl; diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 644777963b21..be6638e0e57b 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -188,11 +188,15 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D, new llvm::GlobalVariable(CGM.getModule(), LTy, Ty.isConstant(getContext()), Linkage, CGM.EmitNullConstant(D.getType()), Name, 0, - D.isThreadSpecified(), + llvm::GlobalVariable::NotThreadLocal, CGM.getContext().getTargetAddressSpace(Ty)); GV->setAlignment(getContext().getDeclAlign(&D).getQuantity()); if (Linkage != llvm::GlobalValue::InternalLinkage) GV->setVisibility(CurFn->getVisibility()); + + if (D.isThreadSpecified()) + CGM.setTLSMode(GV, D); + return GV; } @@ -239,7 +243,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D, OldGV->isConstant(), OldGV->getLinkage(), Init, "", /*InsertBefore*/ OldGV, - D.isThreadSpecified(), + OldGV->getThreadLocalMode(), CGM.getContext().getTargetAddressSpace(D.getType())); GV->setVisibility(OldGV->getVisibility()); @@ -326,7 +330,8 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); - if (DI) { + if (DI && + CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { DI->setLocation(D.getLocation()); DI->EmitGlobalVariable(var, &D); } @@ -489,6 +494,14 @@ static bool isAccessedBy(const VarDecl &var, const Stmt *s) { if (const DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) return (ref->getDecl() == &var); + if (const BlockExpr *be = dyn_cast<BlockExpr>(e)) { + const BlockDecl *block = be->getBlockDecl(); + for (BlockDecl::capture_const_iterator i = block->capture_begin(), + e = block->capture_end(); i != e; ++i) { + if (i->getVariable() == &var) + return true; + } + } } for (Stmt::const_child_range children = s->children(); children; ++children) @@ -897,11 +910,14 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // Emit debug info for local var declaration. if (HaveInsertPoint()) if (CGDebugInfo *DI = getDebugInfo()) { - DI->setLocation(D.getLocation()); - if (Target.useGlobalsForAutomaticVariables()) { - DI->EmitGlobalVariable(static_cast<llvm::GlobalVariable *>(DeclPtr), &D); - } else - DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder); + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { + DI->setLocation(D.getLocation()); + if (Target.useGlobalsForAutomaticVariables()) { + DI->EmitGlobalVariable(static_cast<llvm::GlobalVariable *>(DeclPtr), + &D); + } else + DI->EmitDeclareOfAutoVariable(&D, DeclPtr, Builder); + } } if (D.hasAttr<AnnotateAttr>()) @@ -1054,7 +1070,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), constant->getType(), true, llvm::GlobalValue::PrivateLinkage, - constant, Name, 0, false, 0); + constant, Name); GV->setAlignment(alignment.getQuantity()); GV->setUnnamedAddr(true); @@ -1477,8 +1493,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, LocalDeclMap[&D] = Arg; if (CGDebugInfo *DI = getDebugInfo()) { - DI->setLocation(D.getLocation()); - DI->EmitDeclareOfBlockLiteralArgVariable(*BlockInfo, Arg, Builder); + if (CGM.getCodeGenOpts().DebugInfo >= + CodeGenOptions::LimitedDebugInfo) { + DI->setLocation(D.getLocation()); + DI->EmitDeclareOfBlockLiteralArgVariable(*BlockInfo, Arg, Builder); + } } return; @@ -1556,8 +1575,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, DMEntry = DeclPtr; // Emit debug info for param declaration. - if (CGDebugInfo *DI = getDebugInfo()) - DI->EmitDeclareOfArgVariable(&D, DeclPtr, ArgNo, Builder); + if (CGDebugInfo *DI = getDebugInfo()) { + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) { + DI->EmitDeclareOfArgVariable(&D, DeclPtr, ArgNo, Builder); + } + } if (D.hasAttr<AnnotateAttr>()) EmitVarAnnotations(&D, DeclPtr); diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 10f0b83e4053..492b95ad155b 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -98,7 +98,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, argument = llvm::Constant::getNullValue(CGF.Int8PtrTy); } - CGF.EmitCXXGlobalDtorRegistration(function, argument); + CGM.getCXXABI().registerGlobalDtor(CGF, function, argument); } /// Emit code to cause the variable at the given address to be considered as @@ -145,39 +145,6 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T); } -/// Register a global destructor using __cxa_atexit. -static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, - llvm::Constant *dtor, - llvm::Constant *addr) { - // We're assuming that the destructor function is something we can - // reasonably call with the default CC. Go ahead and cast it to the - // right prototype. - llvm::Type *dtorTy = - llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo(); - - // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); - llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy }; - llvm::FunctionType *atexitTy = - llvm::FunctionType::get(CGF.IntTy, paramTys, false); - - // Fetch the actual function. - llvm::Constant *atexit = - CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit"); - if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit)) - fn->setDoesNotThrow(); - - // Create a variable that binds the atexit to this shared object. - llvm::Constant *handle = - CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle"); - - llvm::Value *args[] = { - llvm::ConstantExpr::getBitCast(dtor, dtorTy), - llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy), - handle - }; - CGF.Builder.CreateCall(atexit, args); -} - static llvm::Function * CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, llvm::FunctionType *ty, @@ -212,43 +179,22 @@ static llvm::Constant *createAtExitStub(CodeGenModule &CGM, return fn; } -/// Register a global destructor using atexit. -static void emitGlobalDtorWithAtExit(CodeGenFunction &CGF, - llvm::Constant *dtor, - llvm::Constant *addr) { +/// Register a global destructor using the C atexit runtime function. +void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtor, + llvm::Constant *addr) { // Create a function which calls the destructor. - llvm::Constant *dtorStub = createAtExitStub(CGF.CGM, dtor, addr); + llvm::Constant *dtorStub = createAtExitStub(CGM, dtor, addr); // extern "C" int atexit(void (*f)(void)); llvm::FunctionType *atexitTy = - llvm::FunctionType::get(CGF.IntTy, dtorStub->getType(), false); + llvm::FunctionType::get(IntTy, dtorStub->getType(), false); llvm::Constant *atexit = - CGF.CGM.CreateRuntimeFunction(atexitTy, "atexit"); + CGM.CreateRuntimeFunction(atexitTy, "atexit"); if (llvm::Function *atexitFn = dyn_cast<llvm::Function>(atexit)) atexitFn->setDoesNotThrow(); - CGF.Builder.CreateCall(atexit, dtorStub); -} - -void CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *dtor, - llvm::Constant *addr) { - // Use __cxa_atexit if available. - if (CGM.getCodeGenOpts().CXAAtExit) { - emitGlobalDtorWithCXAAtExit(*this, dtor, addr); - return; - } - - // In Apple kexts, we want to add a global destructor entry. - // FIXME: shouldn't this be guarded by some variable? - if (CGM.getContext().getLangOpts().AppleKext) { - // Generate a global destructor entry. - CGM.AddCXXDtorEntry(dtor, addr); - return; - } - - // Otherwise, we just use atexit. - emitGlobalDtorWithAtExit(*this, dtor, addr); + Builder.CreateCall(atexit, dtorStub)->setDoesNotThrow(); } void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, @@ -282,6 +228,9 @@ CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, if (!CGM.getLangOpts().Exceptions) Fn->setDoesNotThrow(); + if (CGM.getLangOpts().AddressSanitizer) + Fn->addFnAttr(llvm::Attribute::AddressSafety); + return Fn; } @@ -372,9 +321,12 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, const VarDecl *D, llvm::GlobalVariable *Addr, bool PerformInit) { - StartFunction(GlobalDecl(), getContext().VoidTy, Fn, + if (CGM.getModuleDebugInfo() && !D->hasAttr<NoDebugAttr>()) + DebugInfo = CGM.getModuleDebugInfo(); + + StartFunction(GlobalDecl(D), getContext().VoidTy, Fn, getTypes().arrangeNullaryFunction(), - FunctionArgList(), SourceLocation()); + FunctionArgList(), D->getInit()->getExprLoc()); // Use guarded initialization if the global variable is weak. This // occurs for, e.g., instantiated static data members and diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 95e0030866d7..ba9c2967cd3d 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -126,7 +126,7 @@ static llvm::Constant *getTerminateFn(CodeGenFunction &CGF) { if (CGF.getLangOpts().CPlusPlus) name = "_ZSt9terminatev"; // FIXME: mangling! else if (CGF.getLangOpts().ObjC1 && - CGF.CGM.getCodeGenOpts().ObjCRuntimeHasTerminate) + CGF.getLangOpts().ObjCRuntime.hasTerminate()) name = "objc_terminate"; else name = "abort"; @@ -180,12 +180,18 @@ static const EHPersonality &getCPersonality(const LangOptions &L) { } static const EHPersonality &getObjCPersonality(const LangOptions &L) { - if (L.NeXTRuntime) { - if (L.ObjCNonFragileABI) return EHPersonality::NeXT_ObjC; - else return getCPersonality(L); - } else { + switch (L.ObjCRuntime.getKind()) { + case ObjCRuntime::FragileMacOSX: + return getCPersonality(L); + case ObjCRuntime::MacOSX: + case ObjCRuntime::iOS: + return EHPersonality::NeXT_ObjC; + case ObjCRuntime::GNUstep: + case ObjCRuntime::GCC: + case ObjCRuntime::ObjFW: return EHPersonality::GNU_ObjC; } + llvm_unreachable("bad runtime kind"); } static const EHPersonality &getCXXPersonality(const LangOptions &L) { @@ -198,22 +204,28 @@ static const EHPersonality &getCXXPersonality(const LangOptions &L) { /// Determines the personality function to use when both C++ /// and Objective-C exceptions are being caught. static const EHPersonality &getObjCXXPersonality(const LangOptions &L) { + switch (L.ObjCRuntime.getKind()) { // The ObjC personality defers to the C++ personality for non-ObjC // handlers. Unlike the C++ case, we use the same personality // function on targets using (backend-driven) SJLJ EH. - if (L.NeXTRuntime) { - if (L.ObjCNonFragileABI) - return EHPersonality::NeXT_ObjC; + case ObjCRuntime::MacOSX: + case ObjCRuntime::iOS: + return EHPersonality::NeXT_ObjC; - // In the fragile ABI, just use C++ exception handling and hope - // they're not doing crazy exception mixing. - else - return getCXXPersonality(L); - } + // In the fragile ABI, just use C++ exception handling and hope + // they're not doing crazy exception mixing. + case ObjCRuntime::FragileMacOSX: + return getCXXPersonality(L); - // The GNU runtime's personality function inherently doesn't support + // The GCC runtime's personality function inherently doesn't support // mixed EH. Use the C++ personality just to avoid returning null. - return EHPersonality::GNU_ObjCXX; + case ObjCRuntime::GCC: + case ObjCRuntime::ObjFW: // XXX: this will change soon + return EHPersonality::GNU_ObjC; + case ObjCRuntime::GNUstep: + return EHPersonality::GNU_ObjCXX; + } + llvm_unreachable("bad runtime kind"); } const EHPersonality &EHPersonality::get(const LangOptions &L) { @@ -1127,14 +1139,6 @@ static void BeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) { CGF.EmitAutoVarCleanups(var); } -namespace { - struct CallRethrow : EHScopeStack::Cleanup { - void Emit(CodeGenFunction &CGF, Flags flags) { - CGF.EmitCallOrInvoke(getReThrowFn(CGF)); - } - }; -} - /// Emit the structure of the dispatch block for the given catch scope. /// It is an invariant that the dispatch block already exists. static void emitCatchDispatchBlock(CodeGenFunction &CGF, @@ -1246,11 +1250,12 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { if (HaveInsertPoint()) Builder.CreateBr(ContBB); - // Determine if we need an implicit rethrow for all these catch handlers. - bool ImplicitRethrow = false; + // Determine if we need an implicit rethrow for all these catch handlers; + // see the comment below. + bool doImplicitRethrow = false; if (IsFnTryBlock) - ImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) || - isa<CXXConstructorDecl>(CurCodeDecl); + doImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) || + isa<CXXConstructorDecl>(CurCodeDecl); // Perversely, we emit the handlers backwards precisely because we // want them to appear in source order. In all of these cases, the @@ -1273,15 +1278,24 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { // Initialize the catch variable and set up the cleanups. BeginCatch(*this, C); - // If there's an implicit rethrow, push a normal "cleanup" to call - // _cxa_rethrow. This needs to happen before __cxa_end_catch is - // called, and so it is pushed after BeginCatch. - if (ImplicitRethrow) - EHStack.pushCleanup<CallRethrow>(NormalCleanup); - // Perform the body of the catch. EmitStmt(C->getHandlerBlock()); + // [except.handle]p11: + // The currently handled exception is rethrown if control + // reaches the end of a handler of the function-try-block of a + // constructor or destructor. + + // It is important that we only do this on fallthrough and not on + // return. Note that it's illegal to put a return in a + // constructor function-try-block's catch handler (p14), so this + // really only applies to destructors. + if (doImplicitRethrow && HaveInsertPoint()) { + EmitCallOrInvoke(getReThrowFn(*this)); + Builder.CreateUnreachable(); + Builder.ClearInsertionPoint(); + } + // Fall out through the catch cleanups. CatchScope.ForceCleanup(); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 5f2b1f055dba..ecee7b4931be 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -21,10 +21,11 @@ #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/Basic/ConvertUTF.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/Intrinsics.h" #include "llvm/LLVMContext.h" -#include "llvm/Support/MDBuilder.h" +#include "llvm/MDBuilder.h" #include "llvm/Target/TargetData.h" using namespace clang; using namespace CodeGen; @@ -108,15 +109,18 @@ void CodeGenFunction::EmitIgnoredExpr(const Expr *E) { /// can have any type. The result is returned as an RValue struct. /// If this is an aggregate expression, AggSlot indicates where the /// result should be returned. -RValue CodeGenFunction::EmitAnyExpr(const Expr *E, AggValueSlot AggSlot, - bool IgnoreResult) { +RValue CodeGenFunction::EmitAnyExpr(const Expr *E, + AggValueSlot aggSlot, + bool ignoreResult) { if (!hasAggregateLLVMType(E->getType())) - return RValue::get(EmitScalarExpr(E, IgnoreResult)); + return RValue::get(EmitScalarExpr(E, ignoreResult)); else if (E->getType()->isAnyComplexType()) - return RValue::getComplex(EmitComplexExpr(E, IgnoreResult, IgnoreResult)); + return RValue::getComplex(EmitComplexExpr(E, ignoreResult, ignoreResult)); - EmitAggExpr(E, AggSlot, IgnoreResult); - return AggSlot.asRValue(); + if (!ignoreResult && aggSlot.isIgnored()) + aggSlot = CreateAggTemp(E->getType(), "agg-temp"); + EmitAggExpr(E, aggSlot); + return aggSlot.asRValue(); } /// EmitAnyExprToTemp - Similary to EmitAnyExpr(), however, the result will @@ -156,7 +160,11 @@ namespace { /// \brief An adjustment to be made to the temporary created when emitting a /// reference binding, which accesses a particular subobject of that temporary. struct SubobjectAdjustment { - enum { DerivedToBaseAdjustment, FieldAdjustment } Kind; + enum { + DerivedToBaseAdjustment, + FieldAdjustment, + MemberPointerAdjustment + } Kind; union { struct { @@ -165,6 +173,11 @@ namespace { } DerivedToBase; FieldDecl *Field; + + struct { + const MemberPointerType *MPT; + llvm::Value *Ptr; + } Ptr; }; SubobjectAdjustment(const CastExpr *BasePath, @@ -178,6 +191,12 @@ namespace { : Kind(FieldAdjustment) { this->Field = Field; } + + SubobjectAdjustment(const MemberPointerType *MPT, llvm::Value *Ptr) + : Kind(MemberPointerAdjustment) { + this->Ptr.MPT = MPT; + this->Ptr.Ptr = Ptr; + } }; } @@ -345,6 +364,15 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, continue; } } + } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { + if (BO->isPtrMemOp()) { + assert(BO->getLHS()->isRValue()); + E = BO->getLHS(); + const MemberPointerType *MPT = + BO->getRHS()->getType()->getAs<MemberPointerType>(); + llvm::Value *Ptr = CGF.EmitScalarExpr(BO->getRHS()); + Adjustments.push_back(SubobjectAdjustment(MPT, Ptr)); + } } if (const OpaqueValueExpr *opaque = dyn_cast<OpaqueValueExpr>(E)) @@ -417,6 +445,11 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, break; } + case SubobjectAdjustment::MemberPointerAdjustment: { + Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress( + CGF, Object, Adjustment.Ptr.Ptr, Adjustment.Ptr.MPT); + break; + } } } @@ -462,7 +495,7 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, if (ReferenceTemporaryDtor) { llvm::Constant *DtorFn = CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); - EmitCXXGlobalDtorRegistration(DtorFn, + CGM.getCXXABI().registerGlobalDtor(*this, DtorFn, cast<llvm::Constant>(ReferenceTemporary)); } else { assert(!ObjCARCReferenceLifetimeType.isNull()); @@ -525,15 +558,9 @@ void CodeGenFunction::EmitCheck(llvm::Value *Address, unsigned Size) { llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::objectsize, IntPtrTy); - // In time, people may want to control this and use a 1 here. - llvm::Value *Arg = Builder.getFalse(); - llvm::Value *C = Builder.CreateCall2(F, Address, Arg); + llvm::Value *Min = Builder.getFalse(); + llvm::Value *C = Builder.CreateCall2(F, Address, Min); llvm::BasicBlock *Cont = createBasicBlock(); - llvm::BasicBlock *Check = createBasicBlock(); - llvm::Value *NegativeOne = llvm::ConstantInt::get(IntPtrTy, -1ULL); - Builder.CreateCondBr(Builder.CreateICmpEQ(C, NegativeOne), Cont, Check); - - EmitBlock(Check); Builder.CreateCondBr(Builder.CreateICmpUGE(C, llvm::ConstantInt::get(IntPtrTy, Size)), Cont, getTrapBB()); @@ -676,10 +703,7 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { case Expr::PseudoObjectExprClass: return EmitPseudoObjectLValue(cast<PseudoObjectExpr>(E)); case Expr::InitListExprClass: - assert(cast<InitListExpr>(E)->getNumInits() == 1 && - "Only single-element init list can be lvalue."); - return EmitLValue(cast<InitListExpr>(E)->getInit(0)); - + return EmitInitListLValue(cast<InitListExpr>(E)); case Expr::CXXTemporaryObjectExprClass: case Expr::CXXConstructExprClass: return EmitCXXConstructLValue(cast<CXXConstructExpr>(E)); @@ -880,7 +904,6 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) { CGM.getCodeGenOpts().StrictEnums && !ET->getDecl()->isFixed()); bool IsBool = hasBooleanRepresentation(Ty); - llvm::Type *LTy; if (!IsBool && !IsRegularCPlusPlusEnum) return NULL; @@ -889,10 +912,9 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) { if (IsBool) { Min = llvm::APInt(8, 0); End = llvm::APInt(8, 2); - LTy = Int8Ty; } else { const EnumDecl *ED = ET->getDecl(); - LTy = ConvertTypeForMem(ED->getIntegerType()); + llvm::Type *LTy = ConvertTypeForMem(ED->getIntegerType()); unsigned Bitwidth = LTy->getScalarSizeInBits(); unsigned NumNegativeBits = ED->getNumNegativeBits(); unsigned NumPositiveBits = ED->getNumPositiveBits(); @@ -1028,6 +1050,9 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) { llvm::Value *Res = 0; for (unsigned i = 0, e = Info.getNumComponents(); i != e; ++i) { const CGBitFieldInfo::AccessInfo &AI = Info.getComponent(i); + CharUnits AccessAlignment = AI.AccessAlignment; + if (!LV.getAlignment().isZero()) + AccessAlignment = std::min(AccessAlignment, LV.getAlignment()); // Get the field pointer. llvm::Value *Ptr = LV.getBitFieldBaseAddr(); @@ -1051,8 +1076,7 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) { // Perform the load. llvm::LoadInst *Load = Builder.CreateLoad(Ptr, LV.isVolatileQualified()); - if (!AI.AccessAlignment.isZero()) - Load->setAlignment(AI.AccessAlignment.getQuantity()); + Load->setAlignment(AccessAlignment.getQuantity()); // Shift out unused low bits and mask out unused high bits. llvm::Value *Val = Load; @@ -1251,6 +1275,9 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, // Iterate over the components, writing each piece to memory. for (unsigned i = 0, e = Info.getNumComponents(); i != e; ++i) { const CGBitFieldInfo::AccessInfo &AI = Info.getComponent(i); + CharUnits AccessAlignment = AI.AccessAlignment; + if (!Dst.getAlignment().isZero()) + AccessAlignment = std::min(AccessAlignment, Dst.getAlignment()); // Get the field pointer. llvm::Value *Ptr = Dst.getBitFieldBaseAddr(); @@ -1297,8 +1324,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, // If necessary, load and OR in bits that are outside of the bit-field. if (AI.TargetBitWidth != AI.AccessWidth) { llvm::LoadInst *Load = Builder.CreateLoad(Ptr, Dst.isVolatileQualified()); - if (!AI.AccessAlignment.isZero()) - Load->setAlignment(AI.AccessAlignment.getQuantity()); + Load->setAlignment(AccessAlignment.getQuantity()); // Compute the mask for zeroing the bits that are part of the bit-field. llvm::APInt InvMask = @@ -1312,8 +1338,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, // Write the value. llvm::StoreInst *Store = Builder.CreateStore(Val, Ptr, Dst.isVolatileQualified()); - if (!AI.AccessAlignment.isZero()) - Store->setAlignment(AI.AccessAlignment.getQuantity()); + Store->setAlignment(AccessAlignment.getQuantity()); } } @@ -1683,6 +1708,39 @@ LValue CodeGenFunction::EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E) { E->getType()); } +static llvm::Constant* +GetAddrOfConstantWideString(StringRef Str, + const char *GlobalName, + ASTContext &Context, + QualType Ty, SourceLocation Loc, + CodeGenModule &CGM) { + + StringLiteral *SL = StringLiteral::Create(Context, + Str, + StringLiteral::Wide, + /*Pascal = */false, + Ty, Loc); + llvm::Constant *C = CGM.GetConstantArrayFromStringLiteral(SL); + llvm::GlobalVariable *GV = + new llvm::GlobalVariable(CGM.getModule(), C->getType(), + !CGM.getLangOpts().WritableStrings, + llvm::GlobalValue::PrivateLinkage, + C, GlobalName); + const unsigned WideAlignment = + Context.getTypeAlignInChars(Ty).getQuantity(); + GV->setAlignment(WideAlignment); + return GV; +} + +static void ConvertUTF8ToWideString(unsigned CharByteWidth, StringRef Source, + SmallString<32>& Target) { + Target.resize(CharByteWidth * (Source.size() + 1)); + char* ResultPtr = &Target[0]; + bool success = ConvertUTF8toWide(CharByteWidth, Source, ResultPtr); + (void)success; + assert(success); + Target.resize(ResultPtr - &Target[0]); +} LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { switch (E->getIdentType()) { @@ -1691,11 +1749,12 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { case PredefinedExpr::Func: case PredefinedExpr::Function: + case PredefinedExpr::LFunction: case PredefinedExpr::PrettyFunction: { - unsigned Type = E->getIdentType(); + unsigned IdentType = E->getIdentType(); std::string GlobalVarName; - switch (Type) { + switch (IdentType) { default: llvm_unreachable("Invalid type"); case PredefinedExpr::Func: GlobalVarName = "__func__."; @@ -1703,6 +1762,9 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { case PredefinedExpr::Function: GlobalVarName = "__FUNCTION__."; break; + case PredefinedExpr::LFunction: + GlobalVarName = "L__FUNCTION__."; + break; case PredefinedExpr::PrettyFunction: GlobalVarName = "__PRETTY_FUNCTION__."; break; @@ -1720,10 +1782,27 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { std::string FunctionName = (isa<BlockDecl>(CurDecl) ? FnName.str() - : PredefinedExpr::ComputeName((PredefinedExpr::IdentType)Type, CurDecl)); - - llvm::Constant *C = - CGM.GetAddrOfConstantCString(FunctionName, GlobalVarName.c_str()); + : PredefinedExpr::ComputeName((PredefinedExpr::IdentType)IdentType, + CurDecl)); + + const Type* ElemType = E->getType()->getArrayElementTypeNoTypeQual(); + llvm::Constant *C; + if (ElemType->isWideCharType()) { + SmallString<32> RawChars; + ConvertUTF8ToWideString( + getContext().getTypeSizeInChars(ElemType).getQuantity(), + FunctionName, RawChars); + C = GetAddrOfConstantWideString(RawChars, + GlobalVarName.c_str(), + getContext(), + E->getType(), + E->getLocation(), + CGM); + } else { + C = CGM.GetAddrOfConstantCString(FunctionName, + GlobalVarName.c_str(), + 1); + } return MakeAddrLValue(C, E->getType()); } } @@ -1794,25 +1873,6 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E) { // Extend or truncate the index type to 32 or 64-bits. if (Idx->getType() != IntPtrTy) Idx = Builder.CreateIntCast(Idx, IntPtrTy, IdxSigned, "idxprom"); - - // FIXME: As llvm implements the object size checking, this can come out. - if (CatchUndefined) { - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E->getBase())){ - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ICE->getSubExpr())) { - if (ICE->getCastKind() == CK_ArrayToPointerDecay) { - if (const ConstantArrayType *CAT - = getContext().getAsConstantArrayType(DRE->getType())) { - llvm::APInt Size = CAT->getSize(); - llvm::BasicBlock *Cont = createBasicBlock("cont"); - Builder.CreateCondBr(Builder.CreateICmpULE(Idx, - llvm::ConstantInt::get(Idx->getType(), Size)), - Cont, getTrapBB()); - EmitBlock(Cont); - } - } - } - } - } // We know that the pointer points to a type of the correct size, unless the // size is a VLA or Objective-C interface. @@ -1996,43 +2056,17 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { llvm_unreachable("Unhandled member declaration!"); } -LValue CodeGenFunction::EmitLValueForBitfield(llvm::Value *BaseValue, - const FieldDecl *Field, - unsigned CVRQualifiers) { - const CGRecordLayout &RL = - CGM.getTypes().getCGRecordLayout(Field->getParent()); - const CGBitFieldInfo &Info = RL.getBitFieldInfo(Field); - return LValue::MakeBitfield(BaseValue, Info, - Field->getType().withCVRQualifiers(CVRQualifiers)); -} - -/// EmitLValueForAnonRecordField - Given that the field is a member of -/// an anonymous struct or union buried inside a record, and given -/// that the base value is a pointer to the enclosing record, derive -/// an lvalue for the ultimate field. -LValue CodeGenFunction::EmitLValueForAnonRecordField(llvm::Value *BaseValue, - const IndirectFieldDecl *Field, - unsigned CVRQualifiers) { - IndirectFieldDecl::chain_iterator I = Field->chain_begin(), - IEnd = Field->chain_end(); - while (true) { - QualType RecordTy = - getContext().getTypeDeclType(cast<FieldDecl>(*I)->getParent()); - LValue LV = EmitLValueForField(MakeAddrLValue(BaseValue, RecordTy), - cast<FieldDecl>(*I)); - if (++I == IEnd) return LV; - - assert(LV.isSimple()); - BaseValue = LV.getAddress(); - CVRQualifiers |= LV.getVRQualifiers(); - } -} - LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field) { - if (field->isBitField()) - return EmitLValueForBitfield(base.getAddress(), field, - base.getVRQualifiers()); + if (field->isBitField()) { + const CGRecordLayout &RL = + CGM.getTypes().getCGRecordLayout(field->getParent()); + const CGBitFieldInfo &Info = RL.getBitFieldInfo(field); + QualType fieldType = + field->getType().withCVRQualifiers(base.getVRQualifiers()); + return LValue::MakeBitfield(base.getAddress(), Info, fieldType, + base.getAlignment()); + } const RecordDecl *rec = field->getParent(); QualType type = field->getType(); @@ -2144,7 +2178,10 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ llvm::Value *GlobalPtr = CGM.GetAddrOfConstantCompoundLiteral(E); return MakeAddrLValue(GlobalPtr, E->getType()); } - + if (E->getType()->isVariablyModifiedType()) + // make sure to emit the VLA size. + EmitVariablyModifiedType(E->getType()); + llvm::Value *DeclPtr = CreateMemTemp(E->getType(), ".compoundliteral"); const Expr *InitExpr = E->getInitializer(); LValue Result = MakeAddrLValue(DeclPtr, E->getType()); @@ -2155,6 +2192,16 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ return Result; } +LValue CodeGenFunction::EmitInitListLValue(const InitListExpr *E) { + if (!E->isGLValue()) + // Initializing an aggregate temporary in C++11: T{...}. + return EmitAggExprToLValue(E); + + // An lvalue initializer list must be initializing a reference. + assert(E->getNumInits() == 1 && "reference init with multiple values"); + return EmitLValue(E->getInit(0)); +} + LValue CodeGenFunction:: EmitConditionalOperatorLValue(const AbstractConditionalOperator *expr) { if (!expr->isGLValue()) { @@ -2214,11 +2261,11 @@ EmitConditionalOperatorLValue(const AbstractConditionalOperator *expr) { return MakeAddrLValue(phi, expr->getType()); } -/// EmitCastLValue - Casts are never lvalues unless that cast is a dynamic_cast. -/// If the cast is a dynamic_cast, we can have the usual lvalue result, +/// EmitCastLValue - Casts are never lvalues unless that cast is to a reference +/// type. If the cast is to a reference, we can have the usual lvalue result, /// otherwise if a cast is needed by the code generator in an lvalue context, /// then it must mean that we need the address of an aggregate in order to -/// access one of its fields. This can happen for all the reasons that casts +/// access one of its members. This can happen for all the reasons that casts /// are permitted with aggregate result, including noop aggregate casts, and /// cast from scalar to union. LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { @@ -2648,7 +2695,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee, EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), ArgBeg, ArgEnd); const CGFunctionInfo &FnInfo = - CGM.getTypes().arrangeFunctionCall(Args, FnType); + CGM.getTypes().arrangeFreeFunctionCall(Args, FnType); // C99 6.5.2.2p6: // If the expression that denotes the called function has a type @@ -3038,7 +3085,7 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) { getContext().IntTy); const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFunctionCall(RetTy, Args, + CGM.getTypes().arrangeFreeFunctionCall(RetTy, Args, FunctionType::ExtInfo(), RequiredArgs::All); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName); diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 7b0e0f5157cf..61f7362ecaef 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -34,7 +34,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { CodeGenFunction &CGF; CGBuilderTy &Builder; AggValueSlot Dest; - bool IgnoreResult; /// We want to use 'dest' as the return slot except under two /// conditions: @@ -56,12 +55,14 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { if (!Dest.isIgnored()) return Dest; return CGF.CreateAggTemp(T, "agg.tmp.ensured"); } + void EnsureDest(QualType T) { + if (!Dest.isIgnored()) return; + Dest = CGF.CreateAggTemp(T, "agg.tmp.ensured"); + } public: - AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest, - bool ignore) - : CGF(cgf), Builder(CGF.Builder), Dest(Dest), - IgnoreResult(ignore) { + AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest) + : CGF(cgf), Builder(CGF.Builder), Dest(Dest) { } //===--------------------------------------------------------------------===// @@ -74,9 +75,11 @@ public: void EmitAggLoadOfLValue(const Expr *E); /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. - void EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore = false); - void EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore = false, - unsigned Alignment = 0); + void EmitFinalDestCopy(QualType type, const LValue &src); + void EmitFinalDestCopy(QualType type, RValue src, + CharUnits srcAlignment = CharUnits::Zero()); + void EmitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src); void EmitMoveFromReturnSlot(const Expr *E, RValue Src); @@ -119,7 +122,7 @@ public: if (E->getDecl()->getType()->isReferenceType()) { if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) { - EmitFinalDestCopy(E, result.getReferenceLValue(CGF, E)); + EmitFinalDestCopy(E->getType(), result.getReferenceLValue(CGF, E)); return; } } @@ -171,7 +174,7 @@ public: void VisitPseudoObjectExpr(PseudoObjectExpr *E) { if (E->isGLValue()) { LValue LV = CGF.EmitPseudoObjectLValue(E); - return EmitFinalDestCopy(E, LV); + return EmitFinalDestCopy(E->getType(), LV); } CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType())); @@ -198,7 +201,7 @@ public: /// then loads the result into DestPtr. void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { LValue LV = CGF.EmitLValue(E); - EmitFinalDestCopy(E, LV); + EmitFinalDestCopy(E->getType(), LV); } /// \brief True if the given aggregate type requires special GC API calls. @@ -228,7 +231,7 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) { /// If nothing interferes, this will cause the result to be emitted /// directly into the return value slot. Otherwise, a final move /// will be performed. -void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) { +void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue src) { if (shouldUseDestForReturnSlot()) { // Logically, Dest.getAddr() should equal Src.getAggregateAddr(). // The possibility of undef rvalues complicates that a lot, @@ -236,61 +239,58 @@ void AggExprEmitter::EmitMoveFromReturnSlot(const Expr *E, RValue Src) { return; } - // Otherwise, do a final copy, - assert(Dest.getAddr() != Src.getAggregateAddr()); - std::pair<CharUnits, CharUnits> TypeInfo = + // Otherwise, copy from there to the destination. + assert(Dest.getAddr() != src.getAggregateAddr()); + std::pair<CharUnits, CharUnits> typeInfo = CGF.getContext().getTypeInfoInChars(E->getType()); - CharUnits Alignment = std::min(TypeInfo.second, Dest.getAlignment()); - EmitFinalDestCopy(E, Src, /*Ignore*/ true, Alignment.getQuantity()); + EmitFinalDestCopy(E->getType(), src, typeInfo.second); } /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. -void AggExprEmitter::EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore, - unsigned Alignment) { - assert(Src.isAggregate() && "value must be aggregate value!"); +void AggExprEmitter::EmitFinalDestCopy(QualType type, RValue src, + CharUnits srcAlign) { + assert(src.isAggregate() && "value must be aggregate value!"); + LValue srcLV = CGF.MakeAddrLValue(src.getAggregateAddr(), type, srcAlign); + EmitFinalDestCopy(type, srcLV); +} +/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. +void AggExprEmitter::EmitFinalDestCopy(QualType type, const LValue &src) { // If Dest is ignored, then we're evaluating an aggregate expression - // in a context (like an expression statement) that doesn't care - // about the result. C says that an lvalue-to-rvalue conversion is - // performed in these cases; C++ says that it is not. In either - // case, we don't actually need to do anything unless the value is - // volatile. - if (Dest.isIgnored()) { - if (!Src.isVolatileQualified() || - CGF.CGM.getLangOpts().CPlusPlus || - (IgnoreResult && Ignore)) - return; + // in a context that doesn't care about the result. Note that loads + // from volatile l-values force the existence of a non-ignored + // destination. + if (Dest.isIgnored()) + return; - // If the source is volatile, we must read from it; to do that, we need - // some place to put it. - Dest = CGF.CreateAggTemp(E->getType(), "agg.tmp"); - } + AggValueSlot srcAgg = + AggValueSlot::forLValue(src, AggValueSlot::IsDestructed, + needsGC(type), AggValueSlot::IsAliased); + EmitCopy(type, Dest, srcAgg); +} - if (Dest.requiresGCollection()) { - CharUnits size = CGF.getContext().getTypeSizeInChars(E->getType()); - llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); - llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size.getQuantity()); +/// Perform a copy from the source into the destination. +/// +/// \param type - the type of the aggregate being copied; qualifiers are +/// ignored +void AggExprEmitter::EmitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src) { + if (dest.requiresGCollection()) { + CharUnits sz = CGF.getContext().getTypeSizeInChars(type); + llvm::Value *size = llvm::ConstantInt::get(CGF.SizeTy, sz.getQuantity()); CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF, - Dest.getAddr(), - Src.getAggregateAddr(), - SizeVal); + dest.getAddr(), + src.getAddr(), + size); return; } - // If the result of the assignment is used, copy the LHS there also. - // FIXME: Pass VolatileDest as well. I think we also need to merge volatile - // from the source as well, as we can't eliminate it if either operand - // is volatile, unless copy has volatile for both source and destination.. - CGF.EmitAggregateCopy(Dest.getAddr(), Src.getAggregateAddr(), E->getType(), - Dest.isVolatile()|Src.isVolatileQualified(), - Alignment); -} -/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. -void AggExprEmitter::EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore) { - assert(Src.isSimple() && "Can't have aggregate bitfield, vector, etc"); - - CharUnits Alignment = std::min(Src.getAlignment(), Dest.getAlignment()); - EmitFinalDestCopy(E, Src.asAggregateRValue(), Ignore, Alignment.getQuantity()); + // If the result of the assignment is used, copy the LHS there also. + // It's volatile if either side is. Use the minimum alignment of + // the two sides. + CGF.EmitAggregateCopy(dest.getAddr(), src.getAddr(), type, + dest.isVolatile() || src.isVolatile(), + std::min(dest.getAlignment(), src.getAlignment())); } static QualType GetStdInitializerListElementType(QualType T) { @@ -526,7 +526,7 @@ void AggExprEmitter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E){ } void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) { - EmitFinalDestCopy(e, CGF.getOpaqueLValueMapping(e)); + EmitFinalDestCopy(e->getType(), CGF.getOpaqueLValueMapping(e)); } void @@ -582,7 +582,15 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { "should have been unpacked before we got here"); } - case CK_LValueToRValue: // hope for downstream optimization + case CK_LValueToRValue: + // If we're loading from a volatile type, force the destination + // into existence. + if (E->getSubExpr()->getType().isVolatileQualified()) { + EnsureDest(E->getType()); + return Visit(E->getSubExpr()); + } + // fallthrough + case CK_NoOp: case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: @@ -676,7 +684,73 @@ void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) { void AggExprEmitter::VisitPointerToDataMemberBinaryOperator( const BinaryOperator *E) { LValue LV = CGF.EmitPointerToDataMemberBinaryExpr(E); - EmitFinalDestCopy(E, LV); + EmitFinalDestCopy(E->getType(), LV); +} + +/// Is the value of the given expression possibly a reference to or +/// into a __block variable? +static bool isBlockVarRef(const Expr *E) { + // Make sure we look through parens. + E = E->IgnoreParens(); + + // Check for a direct reference to a __block variable. + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + const VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl()); + return (var && var->hasAttr<BlocksAttr>()); + } + + // More complicated stuff. + + // Binary operators. + if (const BinaryOperator *op = dyn_cast<BinaryOperator>(E)) { + // For an assignment or pointer-to-member operation, just care + // about the LHS. + if (op->isAssignmentOp() || op->isPtrMemOp()) + return isBlockVarRef(op->getLHS()); + + // For a comma, just care about the RHS. + if (op->getOpcode() == BO_Comma) + return isBlockVarRef(op->getRHS()); + + // FIXME: pointer arithmetic? + return false; + + // Check both sides of a conditional operator. + } else if (const AbstractConditionalOperator *op + = dyn_cast<AbstractConditionalOperator>(E)) { + return isBlockVarRef(op->getTrueExpr()) + || isBlockVarRef(op->getFalseExpr()); + + // OVEs are required to support BinaryConditionalOperators. + } else if (const OpaqueValueExpr *op + = dyn_cast<OpaqueValueExpr>(E)) { + if (const Expr *src = op->getSourceExpr()) + return isBlockVarRef(src); + + // Casts are necessary to get things like (*(int*)&var) = foo(). + // We don't really care about the kind of cast here, except + // we don't want to look through l2r casts, because it's okay + // to get the *value* in a __block variable. + } else if (const CastExpr *cast = dyn_cast<CastExpr>(E)) { + if (cast->getCastKind() == CK_LValueToRValue) + return false; + return isBlockVarRef(cast->getSubExpr()); + + // Handle unary operators. Again, just aggressively look through + // it, ignoring the operation. + } else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(E)) { + return isBlockVarRef(uop->getSubExpr()); + + // Look into the base of a field access. + } else if (const MemberExpr *mem = dyn_cast<MemberExpr>(E)) { + return isBlockVarRef(mem->getBase()); + + // Look into the base of a subscript. + } else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(E)) { + return isBlockVarRef(sub->getBase()); + } + + return false; } void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { @@ -686,20 +760,26 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { E->getRHS()->getType()) && "Invalid assignment"); - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->getLHS())) - if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) - if (VD->hasAttr<BlocksAttr>() && - E->getRHS()->HasSideEffects(CGF.getContext())) { - // When __block variable on LHS, the RHS must be evaluated first - // as it may change the 'forwarding' field via call to Block_copy. - LValue RHS = CGF.EmitLValue(E->getRHS()); - LValue LHS = CGF.EmitLValue(E->getLHS()); - Dest = AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, - needsGC(E->getLHS()->getType()), - AggValueSlot::IsAliased); - EmitFinalDestCopy(E, RHS, true); - return; - } + // If the LHS might be a __block variable, and the RHS can + // potentially cause a block copy, we need to evaluate the RHS first + // so that the assignment goes the right place. + // This is pretty semantically fragile. + if (isBlockVarRef(E->getLHS()) && + E->getRHS()->HasSideEffects(CGF.getContext())) { + // Ensure that we have a destination, and evaluate the RHS into that. + EnsureDest(E->getRHS()->getType()); + Visit(E->getRHS()); + + // Now emit the LHS and copy into it. + LValue LHS = CGF.EmitLValue(E->getLHS()); + + EmitCopy(E->getLHS()->getType(), + AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, + needsGC(E->getLHS()->getType()), + AggValueSlot::IsAliased), + Dest); + return; + } LValue LHS = CGF.EmitLValue(E->getLHS()); @@ -708,8 +788,10 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { AggValueSlot::forLValue(LHS, AggValueSlot::IsDestructed, needsGC(E->getLHS()->getType()), AggValueSlot::IsAliased); - CGF.EmitAggExpr(E->getRHS(), LHSSlot, false); - EmitFinalDestCopy(E, LHS, true); + CGF.EmitAggExpr(E->getRHS(), LHSSlot); + + // Copy into the destination if the assignment isn't ignored. + EmitFinalDestCopy(E->getType(), LHS); } void AggExprEmitter:: @@ -762,14 +844,14 @@ void AggExprEmitter::VisitVAArgExpr(VAArgExpr *VE) { return; } - EmitFinalDestCopy(VE, CGF.MakeAddrLValue(ArgPtr, VE->getType())); + EmitFinalDestCopy(VE->getType(), CGF.MakeAddrLValue(ArgPtr, VE->getType())); } void AggExprEmitter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { // Ensure that we have a slot, but if we already do, remember // whether it was externally destructed. bool wasExternallyDestructed = Dest.isExternallyDestructed(); - Dest = EnsureSlot(E->getType()); + EnsureDest(E->getType()); // We're going to push a destructor if there isn't already one. Dest.setExternallyDestructed(); @@ -904,7 +986,7 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { llvm::GlobalVariable* GV = new llvm::GlobalVariable(CGF.CGM.getModule(), C->getType(), true, llvm::GlobalValue::InternalLinkage, C, ""); - EmitFinalDestCopy(E, CGF.MakeAddrLValue(GV, E->getType())); + EmitFinalDestCopy(E->getType(), CGF.MakeAddrLValue(GV, E->getType())); return; } #endif @@ -1164,11 +1246,7 @@ static void CheckAggExprForMemSetUse(AggValueSlot &Slot, const Expr *E, /// type. The result is computed into DestPtr. Note that if DestPtr is null, /// the value of the aggregate expression is not needed. If VolatileDest is /// true, DestPtr cannot be 0. -/// -/// \param IsInitializer - true if this evaluation is initializing an -/// object whose lifetime is already being managed. -void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, - bool IgnoreResult) { +void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot) { assert(E && hasAggregateLLVMType(E->getType()) && "Invalid aggregate expression to emit"); assert((Slot.getAddr() != 0 || Slot.isIgnored()) && @@ -1177,7 +1255,7 @@ void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, // Optimize the slot if possible. CheckAggExprForMemSetUse(Slot, E, *this); - AggExprEmitter(*this, Slot, IgnoreResult).Visit(const_cast<Expr*>(E)); + AggExprEmitter(*this, Slot).Visit(const_cast<Expr*>(E)); } LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { @@ -1192,7 +1270,8 @@ LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty, - bool isVolatile, unsigned Alignment) { + bool isVolatile, + CharUnits alignment) { assert(!Ty->isAnyComplexType() && "Shouldn't happen for complex"); if (getContext().getLangOpts().CPlusPlus) { @@ -1225,8 +1304,8 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, std::pair<CharUnits, CharUnits> TypeInfo = getContext().getTypeInfoInChars(Ty); - if (!Alignment) - Alignment = TypeInfo.second.getQuantity(); + if (alignment.isZero()) + alignment = TypeInfo.second; // FIXME: Handle variable sized types. @@ -1284,7 +1363,7 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, Builder.CreateMemCpy(DestPtr, SrcPtr, llvm::ConstantInt::get(IntPtrTy, TypeInfo.first.getQuantity()), - Alignment, isVolatile); + alignment.getQuantity(), isVolatile); } void CodeGenFunction::MaybeEmitStdInitializerListCleanup(llvm::Value *loc, diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index c69c8830c1e8..7c2c9f1ecb48 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -50,36 +50,10 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD, // And the rest of the call args. EmitCallArgs(Args, FPT, ArgBeg, ArgEnd); - return EmitCall(CGM.getTypes().arrangeFunctionCall(FPT->getResultType(), Args, - FPT->getExtInfo(), - required), + return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args, MD); } -static const CXXRecordDecl *getMostDerivedClassDecl(const Expr *Base) { - const Expr *E = Base; - - while (true) { - E = E->IgnoreParens(); - if (const CastExpr *CE = dyn_cast<CastExpr>(E)) { - if (CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) { - E = CE->getSubExpr(); - continue; - } - } - - break; - } - - QualType DerivedType = E->getType(); - if (const PointerType *PTy = DerivedType->getAs<PointerType>()) - DerivedType = PTy->getPointeeType(); - - return cast<CXXRecordDecl>(DerivedType->castAs<RecordType>()->getDecl()); -} - // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do // quite what we want. static const Expr *skipNoOpCastsAndParens(const Expr *E) { @@ -126,7 +100,7 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context, // b->f(); // } // - const CXXRecordDecl *MostDerivedClassDecl = getMostDerivedClassDecl(Base); + const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType(); if (MostDerivedClassDecl->hasAttr<FinalAttr>()) return true; @@ -166,6 +140,14 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context, return false; } +static CXXRecordDecl *getCXXRecord(const Expr *E) { + QualType T = E->getType(); + if (const PointerType *PTy = T->getAs<PointerType>()) + T = PTy->getPointeeType(); + const RecordType *Ty = T->castAs<RecordType>(); + return cast<CXXRecordDecl>(Ty->getDecl()); +} + // Note: This function also emit constructor calls to support a MSVC // extensions allowing explicit constructor function call. RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, @@ -179,7 +161,7 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, const CXXMethodDecl *MD = cast<CXXMethodDecl>(ME->getMemberDecl()); CGDebugInfo *DI = getDebugInfo(); - if (DI && CGM.getCodeGenOpts().LimitDebugInfo + if (DI && CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo && !isa<CallExpr>(ME->getBase())) { QualType PQTy = ME->getBase()->IgnoreParenImpCasts()->getType(); if (const PointerType * PTy = dyn_cast<PointerType>(PQTy)) { @@ -196,11 +178,45 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, } // Compute the object pointer. + const Expr *Base = ME->getBase(); + bool CanUseVirtualCall = MD->isVirtual() && !ME->hasQualifier(); + + const CXXMethodDecl *DevirtualizedMethod = NULL; + if (CanUseVirtualCall && + canDevirtualizeMemberFunctionCalls(getContext(), Base, MD)) { + const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType(); + DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl); + assert(DevirtualizedMethod); + const CXXRecordDecl *DevirtualizedClass = DevirtualizedMethod->getParent(); + const Expr *Inner = Base->ignoreParenBaseCasts(); + if (getCXXRecord(Inner) == DevirtualizedClass) + // If the class of the Inner expression is where the dynamic method + // is defined, build the this pointer from it. + Base = Inner; + else if (getCXXRecord(Base) != DevirtualizedClass) { + // If the method is defined in a class that is not the best dynamic + // one or the one of the full expression, we would have to build + // a derived-to-base cast to compute the correct this pointer, but + // we don't have support for that yet, so do a virtual call. + DevirtualizedMethod = NULL; + } + // If the return types are not the same, this might be a case where more + // code needs to run to compensate for it. For example, the derived + // method might return a type that inherits form from the return + // type of MD and has a prefix. + // For now we just avoid devirtualizing these covariant cases. + if (DevirtualizedMethod && + DevirtualizedMethod->getResultType().getCanonicalType() != + MD->getResultType().getCanonicalType()) + DevirtualizedMethod = NULL; + } + llvm::Value *This; if (ME->isArrow()) - This = EmitScalarExpr(ME->getBase()); + This = EmitScalarExpr(Base); else - This = EmitLValue(ME->getBase()).getAddress(); + This = EmitLValue(Base).getAddress(); + if (MD->isTrivial()) { if (isa<CXXDestructorDecl>(MD)) return RValue::get(0); @@ -247,10 +263,8 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, // // We also don't emit a virtual call if the base expression has a record type // because then we know what the type is. - bool UseVirtualCall; - UseVirtualCall = MD->isVirtual() && !ME->hasQualifier() - && !canDevirtualizeMemberFunctionCalls(getContext(), - ME->getBase(), MD); + bool UseVirtualCall = CanUseVirtualCall && !DevirtualizedMethod; + llvm::Value *Callee; if (const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(MD)) { if (UseVirtualCall) { @@ -260,8 +274,13 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, MD->isVirtual() && ME->hasQualifier()) Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty); - else + else if (!DevirtualizedMethod) Callee = CGM.GetAddrOfFunction(GlobalDecl(Dtor, Dtor_Complete), Ty); + else { + const CXXDestructorDecl *DDtor = + cast<CXXDestructorDecl>(DevirtualizedMethod); + Callee = CGM.GetAddrOfFunction(GlobalDecl(DDtor, Dtor_Complete), Ty); + } } } else if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) { @@ -273,8 +292,11 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, MD->isVirtual() && ME->hasQualifier()) Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty); - else + else if (!DevirtualizedMethod) Callee = CGM.GetAddrOfFunction(MD, Ty); + else { + Callee = CGM.GetAddrOfFunction(DevirtualizedMethod, Ty); + } } return EmitCXXMemberCall(MD, Callee, ReturnValue, This, /*VTT=*/0, @@ -319,10 +341,12 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, // Push the this ptr. Args.add(RValue::get(This), ThisType); + + RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, 1); // And the rest of the call args EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end()); - return EmitCall(CGM.getTypes().arrangeFunctionCall(Args, FPT), Callee, + return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args); } @@ -409,7 +433,6 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E, if (E->requiresZeroInitialization() && !Dest.isZeroed()) { switch (E->getConstructionKind()) { case CXXConstructExpr::CK_Delegating: - assert(0 && "Delegating constructor should not need zeroing"); case CXXConstructExpr::CK_Complete: EmitNullInitialization(Dest.getAddr(), E->getType()); break; @@ -1006,7 +1029,7 @@ namespace { DeleteArgs.add(getPlacementArgs()[I], *AI++); // Call 'operator delete'. - CGF.EmitCall(CGF.CGM.getTypes().arrangeFunctionCall(DeleteArgs, FPT), + CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT), CGF.CGM.GetAddrOfFunction(OperatorDelete), ReturnValueSlot(), DeleteArgs, OperatorDelete); } @@ -1067,7 +1090,7 @@ namespace { } // Call 'operator delete'. - CGF.EmitCall(CGF.CGM.getTypes().arrangeFunctionCall(DeleteArgs, FPT), + CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT), CGF.CGM.GetAddrOfFunction(OperatorDelete), ReturnValueSlot(), DeleteArgs, OperatorDelete); } @@ -1182,8 +1205,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // TODO: kill any unnecessary computations done for the size // argument. } else { - RV = EmitCall(CGM.getTypes().arrangeFunctionCall(allocatorArgs, - allocatorType), + RV = EmitCall(CGM.getTypes().arrangeFreeFunctionCall(allocatorArgs, + allocatorType), CGM.GetAddrOfFunction(allocator), ReturnValueSlot(), allocatorArgs, allocator); } @@ -1306,7 +1329,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD, DeleteArgs.add(RValue::get(Size), SizeTy); // Emit the call to delete. - EmitCall(CGM.getTypes().arrangeFunctionCall(DeleteArgs, DeleteFTy), + EmitCall(CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, DeleteFTy), CGM.GetAddrOfFunction(DeleteFD), ReturnValueSlot(), DeleteArgs, DeleteFD); } @@ -1462,7 +1485,7 @@ namespace { } // Emit the call to delete. - CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(Args, DeleteFTy), + CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Args, DeleteFTy), CGF.CGM.GetAddrOfFunction(OperatorDelete), ReturnValueSlot(), Args, OperatorDelete); } @@ -1510,18 +1533,7 @@ static void EmitArrayDelete(CodeGenFunction &CGF, } void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { - - // Get at the argument before we performed the implicit conversion - // to void*. const Expr *Arg = E->getArgument(); - while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { - if (ICE->getCastKind() != CK_UserDefinedConversion && - ICE->getType()->isVoidPointerType()) - Arg = ICE->getSubExpr(); - else - break; - } - llvm::Value *Ptr = EmitScalarExpr(Arg); // Null check the pointer. @@ -1631,15 +1643,9 @@ llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) { // polymorphic class type, the result refers to a std::type_info object // representing the type of the most derived object (that is, the dynamic // type) to which the glvalue refers. - if (E->getExprOperand()->isGLValue()) { - if (const RecordType *RT = - E->getExprOperand()->getType()->getAs<RecordType>()) { - const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); - if (RD->isPolymorphic()) - return EmitTypeidFromVTable(*this, E->getExprOperand(), - StdTypeInfoPtrTy); - } - } + if (E->isPotentiallyEvaluated()) + return EmitTypeidFromVTable(*this, E->getExprOperand(), + StdTypeInfoPtrTy); QualType OperandTy = E->getExprOperand()->getType(); return Builder.CreateBitCast(CGM.GetAddrOfRTTIDescriptor(OperandTy), diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index bc9f9ef07b28..a17a43639ad7 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -386,11 +386,11 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) { if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are // ignored: - if (CGM.getContext().ZeroBitfieldFollowsNonBitfield((*Field), LastFD)) { + if (CGM.getContext().ZeroBitfieldFollowsNonBitfield(*Field, LastFD)) { --FieldNo; continue; } - LastFD = (*Field); + LastFD = *Field; } // If this is a union, skip all the fields that aren't being initialized. @@ -399,7 +399,7 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) { // Don't emit anonymous bitfields, they just affect layout. if (Field->isUnnamedBitfield()) { - LastFD = (*Field); + LastFD = *Field; continue; } @@ -486,11 +486,11 @@ void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are // ignored: - if (CGM.getContext().ZeroBitfieldFollowsNonBitfield((*Field), LastFD)) { + if (CGM.getContext().ZeroBitfieldFollowsNonBitfield(*Field, LastFD)) { --FieldNo; continue; } - LastFD = (*Field); + LastFD = *Field; } // If this is a union, skip all the fields that aren't being initialized. @@ -499,7 +499,7 @@ void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, // Don't emit anonymous bitfields, they just affect layout. if (Field->isUnnamedBitfield()) { - LastFD = (*Field); + LastFD = *Field; continue; } @@ -932,7 +932,8 @@ public: C = new llvm::GlobalVariable(CGM.getModule(), C->getType(), E->getType().isConstant(CGM.getContext()), llvm::GlobalValue::InternalLinkage, - C, ".compoundliteral", 0, false, + C, ".compoundliteral", 0, + llvm::GlobalVariable::NotThreadLocal, CGM.getContext().getTargetAddressSpace(E->getType())); return C; } @@ -1300,7 +1301,8 @@ FillInNullDataMemberPointers(CodeGenModule &CGM, QualType T, if (CGM.getTypes().isZeroInitializable(BaseDecl)) continue; - uint64_t BaseOffset = Layout.getBaseClassOffsetInBits(BaseDecl); + uint64_t BaseOffset = + CGM.getContext().toBits(Layout.getBaseClassOffset(BaseDecl)); FillInNullDataMemberPointers(CGM, I->getType(), Elements, StartOffset + BaseOffset); } diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 18891f7492a2..1cccafe0d787 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -498,8 +498,8 @@ public: Value *VisitObjCStringLiteral(const ObjCStringLiteral *E) { return CGF.EmitObjCStringLiteral(E); } - Value *VisitObjCNumericLiteral(ObjCNumericLiteral *E) { - return CGF.EmitObjCNumericLiteral(E); + Value *VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + return CGF.EmitObjCBoxedExpr(E); } Value *VisitObjCArrayLiteral(ObjCArrayLiteral *E) { return CGF.EmitObjCArrayLiteral(E); @@ -798,14 +798,15 @@ Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) { return Builder.getInt(Value); } - // Emit debug info for aggregate now, if it was delayed to reduce + // Emit debug info for aggregate now, if it was delayed to reduce // debug info size. CGDebugInfo *DI = CGF.getDebugInfo(); - if (DI && CGF.CGM.getCodeGenOpts().LimitDebugInfo) { + if (DI && + CGF.CGM.getCodeGenOpts().DebugInfo == CodeGenOptions::LimitedDebugInfo) { QualType PQTy = E->getBase()->IgnoreParenImpCasts()->getType(); if (const PointerType * PTy = dyn_cast<PointerType>(PQTy)) if (FieldDecl *M = dyn_cast<FieldDecl>(E->getMemberDecl())) - DI->getOrCreateRecordType(PTy->getPointeeType(), + DI->getOrCreateRecordType(PTy->getPointeeType(), M->getParent()->getLocation()); } return EmitLoadOfLValue(E); @@ -1520,7 +1521,7 @@ Value *ScalarExprEmitter::VisitOffsetOfExpr(OffsetOfExpr *E) { // FIXME: It would be nice if we didn't have to loop here! for (RecordDecl::field_iterator Field = RD->field_begin(), FieldEnd = RD->field_end(); - Field != FieldEnd; (void)++Field, ++i) { + Field != FieldEnd; ++Field, ++i) { if (*Field == MemberDecl) break; } @@ -1554,9 +1555,8 @@ Value *ScalarExprEmitter::VisitOffsetOfExpr(OffsetOfExpr *E) { // Compute the offset to the base. const RecordType *BaseRT = CurrentType->getAs<RecordType>(); CXXRecordDecl *BaseRD = cast<CXXRecordDecl>(BaseRT->getDecl()); - int64_t OffsetInt = RL.getBaseClassOffsetInBits(BaseRD) / - CGF.getContext().getCharWidth(); - Offset = llvm::ConstantInt::get(ResultType, OffsetInt); + CharUnits OffsetInt = RL.getBaseClassOffset(BaseRD); + Offset = llvm::ConstantInt::get(ResultType, OffsetInt.getQuantity()); break; } } @@ -1682,11 +1682,9 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( // Load/convert the LHS. LValue LHSLV = EmitCheckedLValue(E->getLHS()); OpInfo.LHS = EmitLoadOfLValue(LHSLV); - OpInfo.LHS = EmitScalarConversion(OpInfo.LHS, LHSTy, - E->getComputationLHSType()); llvm::PHINode *atomicPHI = 0; - if (const AtomicType *atomicTy = OpInfo.Ty->getAs<AtomicType>()) { + if (LHSTy->isAtomicType()) { // FIXME: For floating point types, we should be saving and restoring the // floating point environment in the loop. llvm::BasicBlock *startBB = Builder.GetInsertBlock(); @@ -1695,10 +1693,12 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( Builder.SetInsertPoint(opBB); atomicPHI = Builder.CreatePHI(OpInfo.LHS->getType(), 2); atomicPHI->addIncoming(OpInfo.LHS, startBB); - OpInfo.Ty = atomicTy->getValueType(); OpInfo.LHS = atomicPHI; } - + + OpInfo.LHS = EmitScalarConversion(OpInfo.LHS, LHSTy, + E->getComputationLHSType()); + // Expand the binary operator. Result = (this->*Func)(OpInfo); @@ -2592,7 +2592,7 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { llvm::Value *LHSTmp = LHS; bool wasCast = false; llvm::VectorType *rhsVTy = cast<llvm::VectorType>(RHS->getType()); - if (rhsVTy->getElementType()->isFloatTy()) { + if (rhsVTy->getElementType()->isFloatingPointTy()) { RHSTmp = Builder.CreateBitCast(RHS, tmp2->getType()); LHSTmp = Builder.CreateBitCast(LHS, tmp->getType()); wasCast = true; diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index d0aa0f5567a5..4ac172d1cbb5 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -30,7 +30,7 @@ typedef llvm::PointerIntPair<llvm::Value*,1,bool> TryEmitResult; static TryEmitResult tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e); static RValue AdjustRelatedResultType(CodeGenFunction &CGF, - const Expr *E, + QualType ET, const ObjCMethodDecl *Method, RValue Result); @@ -51,36 +51,36 @@ llvm::Value *CodeGenFunction::EmitObjCStringLiteral(const ObjCStringLiteral *E) return llvm::ConstantExpr::getBitCast(C, ConvertType(E->getType())); } -/// EmitObjCNumericLiteral - This routine generates code for -/// the appropriate +[NSNumber numberWith<Type>:] method. +/// EmitObjCBoxedExpr - This routine generates code to call +/// the appropriate expression boxing method. This will either be +/// one of +[NSNumber numberWith<Type>:], or +[NSString stringWithUTF8String:]. /// llvm::Value * -CodeGenFunction::EmitObjCNumericLiteral(const ObjCNumericLiteral *E) { +CodeGenFunction::EmitObjCBoxedExpr(const ObjCBoxedExpr *E) { // Generate the correct selector for this literal's concrete type. - const Expr *NL = E->getNumber(); + const Expr *SubExpr = E->getSubExpr(); // Get the method. - const ObjCMethodDecl *Method = E->getObjCNumericLiteralMethod(); - assert(Method && "NSNumber method is null"); - Selector Sel = Method->getSelector(); + const ObjCMethodDecl *BoxingMethod = E->getBoxingMethod(); + assert(BoxingMethod && "BoxingMethod is null"); + assert(BoxingMethod->isClassMethod() && "BoxingMethod must be a class method"); + Selector Sel = BoxingMethod->getSelector(); // Generate a reference to the class pointer, which will be the receiver. - QualType ResultType = E->getType(); // should be NSNumber * - const ObjCObjectPointerType *InterfacePointerType = - ResultType->getAsObjCInterfacePointerType(); - ObjCInterfaceDecl *NSNumberDecl = - InterfacePointerType->getObjectType()->getInterface(); + // Assumes that the method was introduced in the class that should be + // messaged (avoids pulling it out of the result type). CGObjCRuntime &Runtime = CGM.getObjCRuntime(); - llvm::Value *Receiver = Runtime.GetClass(Builder, NSNumberDecl); - - const ParmVarDecl *argDecl = *Method->param_begin(); + const ObjCInterfaceDecl *ClassDecl = BoxingMethod->getClassInterface(); + llvm::Value *Receiver = Runtime.GetClass(Builder, ClassDecl); + + const ParmVarDecl *argDecl = *BoxingMethod->param_begin(); QualType ArgQT = argDecl->getType().getUnqualifiedType(); - RValue RV = EmitAnyExpr(NL); + RValue RV = EmitAnyExpr(SubExpr); CallArgList Args; Args.add(RV, ArgQT); - + RValue result = Runtime.GenerateMessageSend(*this, ReturnValueSlot(), - ResultType, Sel, Receiver, Args, - NSNumberDecl, Method); + BoxingMethod->getResultType(), Sel, Receiver, Args, + ClassDecl, BoxingMethod); return Builder.CreateBitCast(result.getScalarVal(), ConvertType(E->getType())); } @@ -202,20 +202,20 @@ llvm::Value *CodeGenFunction::EmitObjCProtocolExpr(const ObjCProtocolExpr *E) { /// \brief Adjust the type of the result of an Objective-C message send /// expression when the method has a related result type. static RValue AdjustRelatedResultType(CodeGenFunction &CGF, - const Expr *E, + QualType ExpT, const ObjCMethodDecl *Method, RValue Result) { if (!Method) return Result; if (!Method->hasRelatedResultType() || - CGF.getContext().hasSameType(E->getType(), Method->getResultType()) || + CGF.getContext().hasSameType(ExpT, Method->getResultType()) || !Result.isScalar()) return Result; // We have applied a related result type. Cast the rvalue appropriately. return RValue::get(CGF.Builder.CreateBitCast(Result.getScalarVal(), - CGF.ConvertType(E->getType()))); + CGF.ConvertType(ExpT))); } /// Decide whether to extend the lifetime of the receiver of a @@ -401,7 +401,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Builder.CreateStore(newSelf, selfAddr); } - return AdjustRelatedResultType(*this, E, method, result); + return AdjustRelatedResultType(*this, E->getType(), method, result); } namespace { @@ -507,9 +507,9 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar, args.add(RValue::get(CGF.Builder.getInt1(hasStrong)), Context.BoolTy); llvm::Value *fn = CGF.CGM.getObjCRuntime().GetGetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(Context.VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Context.VoidTy, args, + FunctionType::ExtInfo(), + RequiredArgs::All), fn, ReturnValueSlot(), args); } @@ -580,7 +580,7 @@ namespace { }; } -/// Pick an implementation strategy for the the given property synthesis. +/// Pick an implementation strategy for the given property synthesis. PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, const ObjCPropertyImplDecl *propImpl) { const ObjCPropertyDecl *prop = propImpl->getPropertyDecl(); @@ -698,8 +698,9 @@ PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, Kind = Native; } -/// GenerateObjCGetter - Generate an Objective-C property getter -/// function. The given Decl must be an ObjCImplementationDecl. @synthesize +/// \brief Generate an Objective-C property getter function. +/// +/// The given Decl must be an ObjCImplementationDecl. \@synthesize /// is illegal within a category. void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { @@ -710,7 +711,7 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, assert(OMD && "Invalid call to generate getter (empty method)"); StartObjCMethod(OMD, IMP->getClassInterface(), OMD->getLocStart()); - generateObjCGetterBody(IMP, PID, AtomicHelperFn); + generateObjCGetterBody(IMP, PID, OMD, AtomicHelperFn); FinishFunction(); } @@ -763,15 +764,17 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, + args, + FunctionType::ExtInfo(), + RequiredArgs::All), copyCppAtomicObjectFn, ReturnValueSlot(), args); } void CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, + const ObjCMethodDecl *GetterMethodDecl, llvm::Constant *AtomicHelperFn) { // If there's a non-trivial 'get' expression, we just have to emit that. if (!hasTrivialGetExpr(propImpl)) { @@ -850,16 +853,16 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // FIXME: We shouldn't need to get the function info here, the // runtime already should have computed it to build the function. - RValue RV = EmitCall(getTypes().arrangeFunctionCall(propType, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + RValue RV = EmitCall(getTypes().arrangeFreeFunctionCall(propType, args, + FunctionType::ExtInfo(), + RequiredArgs::All), getPropertyFn, ReturnValueSlot(), args); // We need to fix the type here. Ivars with copy & retain are // always objects so we don't need to worry about complex or // aggregates. RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(), - getTypes().ConvertType(propType))); + getTypes().ConvertType(getterMethod->getResultType()))); EmitReturnOfRValue(RV, propType); @@ -905,6 +908,8 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, } value = Builder.CreateBitCast(value, ConvertType(propType)); + value = Builder.CreateBitCast(value, + ConvertType(GetterMethodDecl->getResultType())); } EmitReturnOfRValue(RValue::get(value), propType); @@ -952,9 +957,10 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD, args.add(RValue::get(CGF.Builder.getFalse()), CGF.getContext().BoolTy); llvm::Value *copyStructFn = CGF.CGM.getObjCRuntime().GetSetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, + args, + FunctionType::ExtInfo(), + RequiredArgs::All), copyStructFn, ReturnValueSlot(), args); } @@ -989,9 +995,10 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFunctionCall(CGF.getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, + args, + FunctionType::ExtInfo(), + RequiredArgs::All), copyCppAtomicObjectFn, ReturnValueSlot(), args); @@ -1125,9 +1132,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, if (setOptimizedPropertyFn) { args.add(RValue::get(arg), getContext().getObjCIdType()); args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); - EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, + FunctionType::ExtInfo(), + RequiredArgs::All), setOptimizedPropertyFn, ReturnValueSlot(), args); } else { args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); @@ -1138,9 +1145,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, getContext().BoolTy); // FIXME: We shouldn't need to get the function info here, the runtime // already should have computed it to build the function. - EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, + FunctionType::ExtInfo(), + RequiredArgs::All), setPropertyFn, ReturnValueSlot(), args); } @@ -1206,8 +1213,9 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, EmitStmt(&assign); } -/// GenerateObjCSetter - Generate an Objective-C property setter -/// function. The given Decl must be an ObjCImplementationDecl. @synthesize +/// \brief Generate an Objective-C property setter function. +/// +/// The given Decl must be an ObjCImplementationDecl. \@synthesize /// is illegal within a category. void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { @@ -1502,9 +1510,9 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ Args2.add(RValue::get(V), getContext().getObjCIdType()); // FIXME: We shouldn't need to get the function info here, the runtime already // should have computed it to build the function. - EmitCall(CGM.getTypes().arrangeFunctionCall(getContext().VoidTy, Args2, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(CGM.getTypes().arrangeFreeFunctionCall(getContext().VoidTy, Args2, + FunctionType::ExtInfo(), + RequiredArgs::All), EnumerationMutationFn, ReturnValueSlot(), Args2); // Otherwise, or if the mutation function returns, just continue. @@ -1685,11 +1693,16 @@ static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM, StringRef fnName) { llvm::Constant *fn = CGM.CreateRuntimeFunction(type, fnName); - // In -fobjc-no-arc-runtime, emit weak references to the runtime - // support library. - if (!CGM.getCodeGenOpts().ObjCRuntimeHasARC) - if (llvm::Function *f = dyn_cast<llvm::Function>(fn)) + // If the target runtime doesn't naturally support ARC, emit weak + // references to the runtime support library. We don't really + // permit this to fail, but we need a particular relocation style. + if (llvm::Function *f = dyn_cast<llvm::Function>(fn)) { + if (!CGM.getLangOpts().ObjCRuntime.hasARC()) f->setLinkage(llvm::Function::ExternalWeakLinkage); + // set nonlazybind attribute for these APIs for performance. + if (fnName == "objc_retain" || fnName == "objc_release") + f->addFnAttr(llvm::Attribute::NonLazyBind); + } return fn; } @@ -1808,8 +1821,8 @@ static void emitARCCopyOperation(CodeGenFunction &CGF, } /// Produce the code to do a retain. Based on the type, calls one of: -/// call i8* @objc_retain(i8* %value) -/// call i8* @objc_retainBlock(i8* %value) +/// call i8* \@objc_retain(i8* %value) +/// call i8* \@objc_retainBlock(i8* %value) llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) { if (type->isBlockPointerType()) return EmitARCRetainBlock(value, /*mandatory*/ false); @@ -1818,7 +1831,7 @@ llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) { } /// Retain the given object, with normal retain semantics. -/// call i8* @objc_retain(i8* %value) +/// call i8* \@objc_retain(i8* %value) llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) { return emitARCValueOperation(*this, value, CGM.getARCEntrypoints().objc_retain, @@ -1826,7 +1839,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) { } /// Retain the given block, with _Block_copy semantics. -/// call i8* @objc_retainBlock(i8* %value) +/// call i8* \@objc_retainBlock(i8* %value) /// /// \param mandatory - If false, emit the call with metadata /// indicating that it's okay for the optimizer to eliminate this call @@ -1856,7 +1869,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value, } /// Retain the given object which is the result of a function call. -/// call i8* @objc_retainAutoreleasedReturnValue(i8* %value) +/// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value) /// /// Yes, this function name is one character away from a different /// call with completely different semantics. @@ -1906,7 +1919,7 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { } /// Release the given object. -/// call void @objc_release(i8* %value) +/// call void \@objc_release(i8* %value) void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) { if (isa<llvm::ConstantPointerNull>(value)) return; @@ -1933,7 +1946,7 @@ void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) { } /// Store into a strong object. Always calls this: -/// call void @objc_storeStrong(i8** %addr, i8* %value) +/// call void \@objc_storeStrong(i8** %addr, i8* %value) llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr, llvm::Value *value, bool ignored) { @@ -1958,7 +1971,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr, } /// Store into a strong object. Sometimes calls this: -/// call void @objc_storeStrong(i8** %addr, i8* %value) +/// call void \@objc_storeStrong(i8** %addr, i8* %value) /// Other times, breaks it down into components. llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst, llvm::Value *newValue, @@ -1994,7 +2007,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst, } /// Autorelease the given object. -/// call i8* @objc_autorelease(i8* %value) +/// call i8* \@objc_autorelease(i8* %value) llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) { return emitARCValueOperation(*this, value, CGM.getARCEntrypoints().objc_autorelease, @@ -2002,7 +2015,7 @@ llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) { } /// Autorelease the given object. -/// call i8* @objc_autoreleaseReturnValue(i8* %value) +/// call i8* \@objc_autoreleaseReturnValue(i8* %value) llvm::Value * CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) { return emitARCValueOperation(*this, value, @@ -2011,7 +2024,7 @@ CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) { } /// Do a fused retain/autorelease of the given object. -/// call i8* @objc_retainAutoreleaseReturnValue(i8* %value) +/// call i8* \@objc_retainAutoreleaseReturnValue(i8* %value) llvm::Value * CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) { return emitARCValueOperation(*this, value, @@ -2020,10 +2033,10 @@ CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) { } /// Do a fused retain/autorelease of the given object. -/// call i8* @objc_retainAutorelease(i8* %value) +/// call i8* \@objc_retainAutorelease(i8* %value) /// or -/// %retain = call i8* @objc_retainBlock(i8* %value) -/// call i8* @objc_autorelease(i8* %retain) +/// %retain = call i8* \@objc_retainBlock(i8* %value) +/// call i8* \@objc_autorelease(i8* %retain) llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type, llvm::Value *value) { if (!type->isBlockPointerType()) @@ -2039,7 +2052,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type, } /// Do a fused retain/autorelease of the given object. -/// call i8* @objc_retainAutorelease(i8* %value) +/// call i8* \@objc_retainAutorelease(i8* %value) llvm::Value * CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) { return emitARCValueOperation(*this, value, @@ -2047,7 +2060,7 @@ CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) { "objc_retainAutorelease"); } -/// i8* @objc_loadWeak(i8** %addr) +/// i8* \@objc_loadWeak(i8** %addr) /// Essentially objc_autorelease(objc_loadWeakRetained(addr)). llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) { return emitARCLoadOperation(*this, addr, @@ -2055,14 +2068,14 @@ llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) { "objc_loadWeak"); } -/// i8* @objc_loadWeakRetained(i8** %addr) +/// i8* \@objc_loadWeakRetained(i8** %addr) llvm::Value *CodeGenFunction::EmitARCLoadWeakRetained(llvm::Value *addr) { return emitARCLoadOperation(*this, addr, CGM.getARCEntrypoints().objc_loadWeakRetained, "objc_loadWeakRetained"); } -/// i8* @objc_storeWeak(i8** %addr, i8* %value) +/// i8* \@objc_storeWeak(i8** %addr, i8* %value) /// Returns %value. llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr, llvm::Value *value, @@ -2072,7 +2085,7 @@ llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr, "objc_storeWeak", ignored); } -/// i8* @objc_initWeak(i8** %addr, i8* %value) +/// i8* \@objc_initWeak(i8** %addr, i8* %value) /// Returns %value. %addr is known to not have a current weak entry. /// Essentially equivalent to: /// *addr = nil; objc_storeWeak(addr, value); @@ -2092,7 +2105,7 @@ void CodeGenFunction::EmitARCInitWeak(llvm::Value *addr, llvm::Value *value) { "objc_initWeak", /*ignored*/ true); } -/// void @objc_destroyWeak(i8** %addr) +/// void \@objc_destroyWeak(i8** %addr) /// Essentially objc_storeWeak(addr, nil). void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) { llvm::Constant *&fn = CGM.getARCEntrypoints().objc_destroyWeak; @@ -2110,7 +2123,7 @@ void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) { call->setDoesNotThrow(); } -/// void @objc_moveWeak(i8** %dest, i8** %src) +/// void \@objc_moveWeak(i8** %dest, i8** %src) /// Disregards the current value in %dest. Leaves %src pointing to nothing. /// Essentially (objc_copyWeak(dest, src), objc_destroyWeak(src)). void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) { @@ -2119,7 +2132,7 @@ void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) { "objc_moveWeak"); } -/// void @objc_copyWeak(i8** %dest, i8** %src) +/// void \@objc_copyWeak(i8** %dest, i8** %src) /// Disregards the current value in %dest. Essentially /// objc_release(objc_initWeak(dest, objc_readWeakRetained(src))) void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) { @@ -2129,7 +2142,7 @@ void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) { } /// Produce the code to do a objc_autoreleasepool_push. -/// call i8* @objc_autoreleasePoolPush(void) +/// call i8* \@objc_autoreleasePoolPush(void) llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() { llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPush; if (!fn) { @@ -2145,7 +2158,7 @@ llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() { } /// Produce the code to do a primitive release. -/// call void @objc_autoreleasePoolPop(i8* %ptr) +/// call void \@objc_autoreleasePoolPop(i8* %ptr) void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) { assert(value->getType() == Int8PtrTy); @@ -2717,7 +2730,7 @@ void CodeGenFunction::EmitObjCAutoreleasePoolStmt( // Keep track of the current cleanup stack depth. RunCleanupsScope Scope(*this); - if (CGM.getCodeGenOpts().ObjCRuntimeHasARC) { + if (CGM.getLangOpts().ObjCRuntime.hasARC()) { llvm::Value *token = EmitObjCAutoreleasePoolPush(); EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, token); } else { @@ -2749,6 +2762,11 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { Builder.CreateCall(extender, object)->setDoesNotThrow(); } +static bool hasAtomicCopyHelperAPI(const ObjCRuntime &runtime) { + // For now, only NeXT has these APIs. + return runtime.isNeXTFamily(); +} + /// GenerateObjCAtomicSetterCopyHelperFunction - Given a c++ object type with /// non-trivial copy assignment function, produce following helper function. /// static void copyHelper(Ty *dest, const Ty *source) { *dest = *source; } @@ -2757,7 +2775,8 @@ llvm::Constant * CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( const ObjCPropertyImplDecl *PID) { // FIXME. This api is for NeXt runtime only for now. - if (!getLangOpts().CPlusPlus || !getLangOpts().NeXTRuntime) + if (!getLangOpts().CPlusPlus || + !hasAtomicCopyHelperAPI(getLangOpts().ObjCRuntime)) return 0; QualType Ty = PID->getPropertyIvarDecl()->getType(); if (!Ty->isRecordType()) @@ -2841,7 +2860,8 @@ llvm::Constant * CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( const ObjCPropertyImplDecl *PID) { // FIXME. This api is for NeXt runtime only for now. - if (!getLangOpts().CPlusPlus || !getLangOpts().NeXTRuntime) + if (!getLangOpts().CPlusPlus || + !hasAtomicCopyHelperAPI(getLangOpts().ObjCRuntime)) return 0; const ObjCPropertyDecl *PD = PID->getPropertyDecl(); QualType Ty = PD->getType(); diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index db0bd951c1e8..6d129d02a561 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -99,8 +99,8 @@ class LazyRuntimeFunction { /// GNU Objective-C runtime code generation. This class implements the parts of -/// Objective-C support that are specific to the GNU family of runtimes (GCC and -/// GNUstep). +/// Objective-C support that are specific to the GNU family of runtimes (GCC, +/// GNUstep and ObjFW). class CGObjCGNU : public CGObjCRuntime { protected: /// The LLVM module into which output is inserted @@ -292,8 +292,8 @@ private: protected: /// Function used for throwing Objective-C exceptions. LazyRuntimeFunction ExceptionThrowFn; - /// Function used for rethrowing exceptions, used at the end of @finally or - /// @synchronize blocks. + /// Function used for rethrowing exceptions, used at the end of \@finally or + /// \@synchronize blocks. LazyRuntimeFunction ExceptionReThrowFn; /// Function called when entering a catch function. This is required for /// differentiating Objective-C exceptions and foreign exceptions. @@ -301,9 +301,9 @@ protected: /// Function called when exiting from a catch block. Used to do exception /// cleanup. LazyRuntimeFunction ExitCatchFn; - /// Function called when entering an @synchronize block. Acquires the lock. + /// Function called when entering an \@synchronize block. Acquires the lock. LazyRuntimeFunction SyncEnterFn; - /// Function called when exiting an @synchronize block. Releases the lock. + /// Function called when exiting an \@synchronize block. Releases the lock. LazyRuntimeFunction SyncExitFn; private: @@ -350,7 +350,7 @@ private: ArrayRef<Selector> MethodSels, ArrayRef<llvm::Constant *> MethodTypes, bool isClassMethodList); - /// Emits an empty protocol. This is used for @protocol() where no protocol + /// Emits an empty protocol. This is used for \@protocol() where no protocol /// is found. The runtime will (hopefully) fix up the pointer to refer to the /// real protocol. llvm::Constant *GenerateEmptyProtocol(const std::string &ProtocolName); @@ -397,11 +397,11 @@ private: const ObjCIvarDecl *Ivar); /// Emits a reference to a class. This allows the linker to object if there /// is no class of the matching name. +protected: void EmitClassRef(const std::string &className); /// Emits a pointer to the named class - llvm::Value *GetClassNamed(CGBuilderTy &Builder, const std::string &Name, - bool isWeak); -protected: + virtual llvm::Value *GetClassNamed(CGBuilderTy &Builder, + const std::string &Name, bool isWeak); /// Looks up the method for sending a message to the specified object. This /// mechanism differs between the GCC and GNU runtimes, so this method must be /// overridden in subclasses. @@ -653,6 +653,33 @@ class CGObjCGNUstep : public CGObjCGNU { } }; +/// The ObjFW runtime, which closely follows the GCC runtime's +/// compiler ABI. Support here is due to Jonathan Schleifer, the +/// ObjFW maintainer. +class CGObjCObjFW : public CGObjCGCC { + /// Emit class references unconditionally as direct symbol references. + virtual llvm::Value *GetClassNamed(CGBuilderTy &Builder, + const std::string &Name, bool isWeak) { + if (isWeak) + return CGObjCGNU::GetClassNamed(Builder, Name, isWeak); + + EmitClassRef(Name); + + std::string SymbolName = "_OBJC_CLASS_" + Name; + + llvm::GlobalVariable *ClassSymbol = TheModule.getGlobalVariable(SymbolName); + + if (!ClassSymbol) + ClassSymbol = new llvm::GlobalVariable(TheModule, LongTy, false, + llvm::GlobalValue::ExternalLinkage, + 0, SymbolName); + + return ClassSymbol; + } + +public: + CGObjCObjFW(CodeGenModule &Mod): CGObjCGCC(Mod) {} +}; } // end anonymous namespace @@ -889,7 +916,7 @@ llvm::Constant *CGObjCGNU::GetEHType(QualType T) { // foreign exceptions. With the new ABI, we use __objc_id_typeinfo as // a pointer indicating object catchalls, and NULL to indicate real // catchalls - if (CGM.getLangOpts().ObjCNonFragileABI) { + if (CGM.getLangOpts().ObjCRuntime.isNonFragile()) { return MakeConstantString("@id"); } else { return 0; @@ -1627,7 +1654,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { iter = PD->prop_begin(), endIter = PD->prop_end(); iter != endIter ; iter++) { std::vector<llvm::Constant*> Fields; - ObjCPropertyDecl *property = (*iter); + ObjCPropertyDecl *property = *iter; Fields.push_back(MakeConstantString(property->getNameAsString())); Fields.push_back(llvm::ConstantInt::get(Int8Ty, @@ -1877,7 +1904,7 @@ llvm::Constant *CGObjCGNU::GeneratePropertyList(const ObjCImplementationDecl *OI iter = OID->propimpl_begin(), endIter = OID->propimpl_end(); iter != endIter ; iter++) { std::vector<llvm::Constant*> Fields; - ObjCPropertyDecl *property = (*iter)->getPropertyDecl(); + ObjCPropertyDecl *property = iter->getPropertyDecl(); ObjCPropertyImplDecl *propertyImpl = *iter; bool isSynthesized = (propertyImpl->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize); @@ -1984,7 +2011,7 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) { Context.getASTObjCInterfaceLayout(SuperClassDecl).getSize().getQuantity(); // For non-fragile ivars, set the instance size to 0 - {the size of just this // class}. The runtime will then set this to the correct value on load. - if (CGM.getContext().getLangOpts().ObjCNonFragileABI) { + if (CGM.getContext().getLangOpts().ObjCRuntime.isNonFragile()) { instanceSize = 0 - (instanceSize - superInstanceSize); } @@ -1999,7 +2026,7 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) { // Get the offset uint64_t BaseOffset = ComputeIvarBaseOffset(CGM, OID, IVD); uint64_t Offset = BaseOffset; - if (CGM.getContext().getLangOpts().ObjCNonFragileABI) { + if (CGM.getContext().getLangOpts().ObjCRuntime.isNonFragile()) { Offset = BaseOffset - superInstanceSize; } llvm::Constant *OffsetValue = llvm::ConstantInt::get(IntTy, Offset); @@ -2486,25 +2513,8 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF, ExceptionAsObject = CGF.ObjCEHValueStack.back(); } ExceptionAsObject = CGF.Builder.CreateBitCast(ExceptionAsObject, IdTy); - - // Note: This may have to be an invoke, if we want to support constructs like: - // @try { - // @throw(obj); - // } - // @catch(id) ... - // - // This is effectively turning @throw into an incredibly-expensive goto, but - // it may happen as a result of inlining followed by missed optimizations, or - // as a result of stupidity. - llvm::BasicBlock *UnwindBB = CGF.getInvokeDest(); - if (!UnwindBB) { - CGF.Builder.CreateCall(ExceptionThrowFn, ExceptionAsObject); - CGF.Builder.CreateUnreachable(); - } else { - CGF.Builder.CreateInvoke(ExceptionThrowFn, UnwindBB, UnwindBB, - ExceptionAsObject); - } - // Clear the insertion point to indicate we are in unreachable code. + CGF.EmitCallOrInvoke(ExceptionThrowFn, ExceptionAsObject); + CGF.Builder.CreateUnreachable(); CGF.Builder.ClearInsertionPoint(); } @@ -2640,7 +2650,7 @@ static const ObjCInterfaceDecl *FindIvarInterface(ASTContext &Context, llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar) { - if (CGM.getLangOpts().ObjCNonFragileABI) { + if (CGM.getLangOpts().ObjCRuntime.isNonFragile()) { Interface = FindIvarInterface(CGM.getContext(), Interface, Ivar); if (RuntimeVersion < 10) return CGF.Builder.CreateZExtOrBitCast( @@ -2665,7 +2675,20 @@ llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF, CGObjCRuntime * clang::CodeGen::CreateGNUObjCRuntime(CodeGenModule &CGM) { - if (CGM.getLangOpts().ObjCNonFragileABI) + switch (CGM.getLangOpts().ObjCRuntime.getKind()) { + case ObjCRuntime::GNUstep: return new CGObjCGNUstep(CGM); - return new CGObjCGCC(CGM); + + case ObjCRuntime::GCC: + return new CGObjCGCC(CGM); + + case ObjCRuntime::ObjFW: + return new CGObjCObjFW(CGM); + + case ObjCRuntime::FragileMacOSX: + case ObjCRuntime::MacOSX: + case ObjCRuntime::iOS: + llvm_unreachable("these runtimes are not GNU runtimes"); + } + llvm_unreachable("bad runtime"); } diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index e5246f101f95..ef802a3ed0ca 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -241,9 +241,9 @@ public: Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(IdType, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(IdType, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_getProperty"); } @@ -261,9 +261,9 @@ public: Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } @@ -287,9 +287,9 @@ public: Params.push_back(IdType); Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); const char *name; if (atomic && copy) name = "objc_setProperty_atomic_copy"; @@ -314,9 +314,9 @@ public: Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct"); } @@ -333,9 +333,9 @@ public: Params.push_back(Ctx.VoidPtrTy); Params.push_back(Ctx.VoidPtrTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_copyCppObjectAtomic"); } @@ -346,7 +346,7 @@ public: SmallVector<CanQualType,1> Params; Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType())); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, Params, FunctionType::ExtInfo(), RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); @@ -2515,7 +2515,7 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, Values); std::string Name("\01L_OBJC_METACLASS_"); - Name += ID->getNameAsCString(); + Name += ID->getName(); // Check for a forward reference. llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); @@ -3612,7 +3612,8 @@ enum ImageInfoFlags { // A flag indicating that the module has no instances of a @synthesize of a // superclass variable. <rdar://problem/6803242> - eImageInfo_CorrectedSynthesize = (1 << 4) + eImageInfo_CorrectedSynthesize = (1 << 4), + eImageInfo_ImageIsSimulated = (1 << 5) }; void CGObjCCommonMac::EmitImageInfo() { @@ -3657,6 +3658,14 @@ void CGObjCCommonMac::EmitImageInfo() { llvm::MDNode::get(VMContext, Ops)); } } + + // Indicate whether we're compiling this to run on a simulator. + const llvm::Triple &Triple = CGM.getTarget().getTriple(); + if (Triple.getOS() == llvm::Triple::IOS && + (Triple.getArch() == llvm::Triple::x86 || + Triple.getArch() == llvm::Triple::x86_64)) + Mod.addModuleFlag(llvm::Module::Error, "Objective-C Is Simulated", + eImageInfo_ImageIsSimulated); } // struct objc_module { @@ -3809,7 +3818,10 @@ void CGObjCCommonMac::BuildAggrIvarRecordLayout(const RecordType *RT, bool &HasUnion) { const RecordDecl *RD = RT->getDecl(); // FIXME - Use iterator. - SmallVector<const FieldDecl*, 16> Fields(RD->field_begin(), RD->field_end()); + SmallVector<const FieldDecl*, 16> Fields; + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) + Fields.push_back(*i); llvm::Type *Ty = CGM.getTypes().ConvertType(QualType(RT, 0)); const llvm::StructLayout *RecLayout = CGM.getTargetData().getStructLayout(cast<llvm::StructType>(Ty)); @@ -4374,9 +4386,10 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) SourceLocation(), SourceLocation(), &Ctx.Idents.get("_objc_super")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCIdType(), 0, 0, false, false)); + Ctx.getObjCIdType(), 0, 0, false, ICIS_NoInit)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCClassType(), 0, 0, false, false)); + Ctx.getObjCClassType(), 0, 0, false, + ICIS_NoInit)); RD->completeDefinition(); SuperCTy = Ctx.getTagDeclType(RD); @@ -4755,9 +4768,10 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul SourceLocation(), SourceLocation(), &Ctx.Idents.get("_message_ref_t")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.VoidPtrTy, 0, 0, false, false)); + Ctx.VoidPtrTy, 0, 0, false, ICIS_NoInit)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(), 0, - Ctx.getObjCSelType(), 0, 0, false, false)); + Ctx.getObjCSelType(), 0, 0, false, + ICIS_NoInit)); RD->completeDefinition(); MessageRefCTy = Ctx.getTagDeclType(RD); @@ -6367,7 +6381,18 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, CodeGen::CGObjCRuntime * CodeGen::CreateMacObjCRuntime(CodeGen::CodeGenModule &CGM) { - if (CGM.getLangOpts().ObjCNonFragileABI) - return new CGObjCNonFragileABIMac(CGM); + switch (CGM.getLangOpts().ObjCRuntime.getKind()) { + case ObjCRuntime::FragileMacOSX: return new CGObjCMac(CGM); + + case ObjCRuntime::MacOSX: + case ObjCRuntime::iOS: + return new CGObjCNonFragileABIMac(CGM); + + case ObjCRuntime::GNUstep: + case ObjCRuntime::GCC: + case ObjCRuntime::ObjFW: + llvm_unreachable("these runtimes are not Mac runtimes"); + } + llvm_unreachable("bad runtime"); } diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index 93700960df23..9aa68376bcd0 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -120,6 +120,8 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, uint64_t ContainingTypeAlign = CGF.CGM.getContext().getTargetInfo().getCharAlign(); uint64_t ContainingTypeSize = TypeSizeInBits - (FieldBitOffset - BitOffset); uint64_t BitFieldSize = Ivar->getBitWidthValue(CGF.getContext()); + CharUnits ContainingTypeAlignCharUnits = + CGF.CGM.getContext().toCharUnitsFromBits(ContainingTypeAlign); // Allocate a new CGBitFieldInfo object to describe this access. // @@ -132,7 +134,8 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, ContainingTypeSize, ContainingTypeAlign)); return LValue::MakeBitfield(V, *Info, - IvarTy.withCVRQualifiers(CVRQualifiers)); + IvarTy.withCVRQualifiers(CVRQualifiers), + ContainingTypeAlignCharUnits); } namespace { @@ -334,7 +337,7 @@ void CGObjCRuntime::EmitAtSynchronizedStmt(CodeGenFunction &CGF, /// /// \param method - may be null /// \param resultType - the result type to use if there's no method -/// \param argInfo - the actual arguments, including implicit ones +/// \param callArgs - the actual arguments, including implicit ones CGObjCRuntime::MessageSendInfo CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, QualType resultType, @@ -355,17 +358,17 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, // Otherwise, there is. FunctionType::ExtInfo einfo = signature.getExtInfo(); const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFunctionCall(resultType, callArgs, einfo, - signature.getRequiredArgs()); + CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, einfo, + signature.getRequiredArgs()); return MessageSendInfo(argsInfo, signatureType); } // There's no method; just use a default CC. const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFunctionCall(resultType, callArgs, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, + FunctionType::ExtInfo(), + RequiredArgs::All); // Derive the signature to call from that. llvm::PointerType *signatureType = diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index ccf4d4dfca56..219a3e4df0d4 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -91,20 +91,20 @@ protected: llvm::Value *Offset); /// Emits a try / catch statement. This function is intended to be called by /// subclasses, and provides a generic mechanism for generating these, which - /// should be usable by all runtimes. The caller must provide the functions to - /// call when entering and exiting a @catch() block, and the function used to - /// rethrow exceptions. If the begin and end catch functions are NULL, then - /// the function assumes that the EH personality function provides the - /// thrown object directly. + /// should be usable by all runtimes. The caller must provide the functions + /// to call when entering and exiting a \@catch() block, and the function + /// used to rethrow exceptions. If the begin and end catch functions are + /// NULL, then the function assumes that the EH personality function provides + /// the thrown object directly. void EmitTryCatchStmt(CodeGenFunction &CGF, const ObjCAtTryStmt &S, llvm::Constant *beginCatchFn, llvm::Constant *endCatchFn, llvm::Constant *exceptionRethrowFn); - /// Emits an @synchronize() statement, using the syncEnterFn and syncExitFn - /// arguments as the functions called to lock and unlock the object. This - /// function can be called by subclasses that use zero-cost exception - /// handling. + /// Emits an \@synchronize() statement, using the \p syncEnterFn and + /// \p syncExitFn arguments as the functions called to lock and unlock + /// the object. This function can be called by subclasses that use + /// zero-cost exception handling. void EmitAtSynchronizedStmt(CodeGenFunction &CGF, const ObjCAtSynchronizedStmt &S, llvm::Function *syncEnterFn, @@ -179,7 +179,7 @@ public: const ObjCMethodDecl *Method = 0) = 0; /// Emit the code to return the named protocol as an object, as in a - /// @protocol expression. + /// \@protocol expression. virtual llvm::Value *GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *OPD) = 0; diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index 19973b46b7cd..d1b370a1f728 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -985,7 +985,8 @@ llvm::Constant *CodeGenModule::GetAddrOfRTTIDescriptor(QualType Ty, if (!ForEH && !getContext().getLangOpts().RTTI) return llvm::Constant::getNullValue(Int8PtrTy); - if (ForEH && Ty->isObjCObjectPointerType() && !LangOpts.NeXTRuntime) + if (ForEH && Ty->isObjCObjectPointerType() && + LangOpts.ObjCRuntime.isGNUFamily()) return ObjCRuntime->GetEHType(Ty); return RTTIBuilder(*this).BuildTypeInfo(Ty); diff --git a/lib/CodeGen/CGRecordLayout.h b/lib/CodeGen/CGRecordLayout.h index 25a0a508f188..94c822f49f17 100644 --- a/lib/CodeGen/CGRecordLayout.h +++ b/lib/CodeGen/CGRecordLayout.h @@ -64,12 +64,7 @@ public: /// Bit width of the memory access to perform. unsigned AccessWidth; - /// The alignment of the memory access, or 0 if the default alignment should - /// be used. - // - // FIXME: Remove use of 0 to encode default, instead have IRgen do the right - // thing when it generates the code, if avoiding align directives is - // desired. + /// The alignment of the memory access, assuming the parent is aligned. CharUnits AccessAlignment; /// Offset for the target value. diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index 1193e974e195..d642ef8533c5 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -235,6 +235,8 @@ CGBitFieldInfo CGBitFieldInfo::MakeInfo(CodeGenTypes &Types, uint64_t FieldSize, uint64_t ContainingTypeSizeInBits, unsigned ContainingTypeAlign) { + assert(ContainingTypeAlign && "Expected alignment to be specified"); + llvm::Type *Ty = Types.ConvertTypeForMem(FD->getType()); CharUnits TypeSizeInBytes = CharUnits::fromQuantity(Types.getTargetData().getTypeAllocSize(Ty)); @@ -714,14 +716,18 @@ CGRecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD, } // Otherwise, add a vtable / vf-table if the layout says to do so. - } else if (Types.getContext().getTargetInfo().getCXXABI() == CXXABI_Microsoft - ? Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1) - : RD->isDynamicClass()) { + } else if (Layout.hasOwnVFPtr()) { llvm::Type *FunctionType = llvm::FunctionType::get(llvm::Type::getInt32Ty(Types.getLLVMContext()), /*isVarArg=*/true); llvm::Type *VTableTy = FunctionType->getPointerTo(); - + + if (getTypeAlignment(VTableTy) > Alignment) { + // FIXME: Should we allow this to happen in Sema? + assert(!Packed && "Alignment is wrong even with packed struct!"); + return false; + } + assert(NextFieldOffset.isZero() && "VTable pointer must come first!"); AppendField(CharUnits::Zero(), VTableTy->getPointerTo()); @@ -814,7 +820,7 @@ bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) { if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are // ignored: - const FieldDecl *FD = (*Field); + const FieldDecl *FD = *Field; if (Types.getContext().ZeroBitfieldFollowsNonBitfield(FD, LastFD)) { --FieldNo; continue; diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index a1d078957302..467c77945e60 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -133,6 +133,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::SwitchStmtClass: EmitSwitchStmt(cast<SwitchStmt>(*S)); break; case Stmt::AsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break; + case Stmt::MSAsmStmtClass: EmitMSAsmStmt(cast<MSAsmStmt>(*S)); break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast<ObjCAtTryStmt>(*S)); @@ -155,7 +156,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::ObjCAutoreleasePoolStmtClass: EmitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(*S)); break; - + case Stmt::CXXTryStmtClass: EmitCXXTryStmt(cast<CXXTryStmt>(*S)); break; @@ -360,15 +361,14 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) { llvm::Value *V = Builder.CreateBitCast(EmitScalarExpr(S.getTarget()), Int8PtrTy, "addr"); llvm::BasicBlock *CurBB = Builder.GetInsertBlock(); - // Get the basic block for the indirect goto. llvm::BasicBlock *IndGotoBB = GetIndirectGotoBlock(); - + // The first instruction in the block has to be the PHI for the switch dest, // add an entry for this branch. cast<llvm::PHINode>(IndGotoBB->begin())->addIncoming(V, CurBB); - + EmitBranch(IndGotoBB); } @@ -462,12 +462,12 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) { if (S.getConditionVariable()) EmitAutoVarDecl(*S.getConditionVariable()); - + // Evaluate the conditional in the while header. C99 6.8.5.1: The // evaluation of the controlling expression takes place before each // execution of the loop body. llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); - + // while(1) is common, avoid extra exit blocks. Be sure // to correctly handle break/continue though. bool EmitBoolCondBranch = true; @@ -489,7 +489,7 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S) { EmitBranchThroughCleanup(LoopExit); } } - + // Emit the loop body. We have to emit this in a cleanup scope // because it might be a singleton DeclStmt. { @@ -584,7 +584,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) { // Create a cleanup scope for the condition variable cleanups. RunCleanupsScope ConditionScope(*this); - + llvm::Value *BoolCondVal = 0; if (S.getCond()) { // If the for statement has a condition scope, emit the local variable @@ -598,7 +598,7 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S) { // create a block to stage a loop exit along. if (ForScope.requiresCleanups()) ExitBlock = createBasicBlock("for.cond.cleanup"); - + // As long as the condition is true, iterate the loop. llvm::BasicBlock *ForBody = createBasicBlock("for.body"); @@ -679,7 +679,7 @@ void CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S) { llvm::BasicBlock *ExitBlock = LoopExit.getBlock(); if (ForScope.requiresCleanups()) ExitBlock = createBasicBlock("for.cond.cleanup"); - + // The loop body, consisting of the specified body and the loop variable. llvm::BasicBlock *ForBody = createBasicBlock("for.body"); @@ -750,7 +750,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) { // Apply the named return value optimization for this return statement, // which means doing nothing: the appropriate result has already been // constructed into the NRVO variable. - + // If there is an NRVO flag for this variable, set it to 1 into indicate // that the cleanup code should not destroy the variable. if (llvm::Value *NRVOFlag = NRVOFlags[S.getNRVOCandidate()]) @@ -901,7 +901,7 @@ void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) { // try to not emit an empty block. if ((CGM.getCodeGenOpts().OptimizationLevel > 0) && isa<BreakStmt>(S.getSubStmt())) { JumpDest Block = BreakContinueStack.back().BreakBlock; - + // Only do this optimization if there are no cleanups that need emitting. if (isObviouslyBranchWithoutCleanups(Block)) { SwitchInsn->addCase(CaseVal, Block.getBlock()); @@ -915,7 +915,7 @@ void CodeGenFunction::EmitCaseStmt(const CaseStmt &S) { return; } } - + EmitBlock(createBasicBlock("sw.bb")); llvm::BasicBlock *CaseDest = Builder.GetInsertBlock(); SwitchInsn->addCase(CaseVal, CaseDest); @@ -984,7 +984,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, // If this is a null statement, just succeed. if (S == 0) return Case ? CSFC_Success : CSFC_FallThrough; - + // If this is the switchcase (case 4: or default) that we're looking for, then // we're in business. Just add the substatement. if (const SwitchCase *SC = dyn_cast<SwitchCase>(S)) { @@ -993,7 +993,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, return CollectStatementsForCase(SC->getSubStmt(), 0, FoundCase, ResultStmts); } - + // Otherwise, this is some other case or default statement, just ignore it. return CollectStatementsForCase(SC->getSubStmt(), Case, FoundCase, ResultStmts); @@ -1003,7 +1003,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, // return a success! if (Case == 0 && isa<BreakStmt>(S)) return CSFC_Success; - + // If this is a switch statement, then it might contain the SwitchCase, the // break, or neither. if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) { @@ -1015,12 +1015,12 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, // using the declaration even if it is skipped, so we can't optimize out // the decl if the kept statements might refer to it. bool HadSkippedDecl = false; - + // If we're looking for the case, just see if we can skip each of the // substatements. for (; Case && I != E; ++I) { HadSkippedDecl |= isa<DeclStmt>(*I); - + switch (CollectStatementsForCase(*I, Case, FoundCase, ResultStmts)) { case CSFC_Failure: return CSFC_Failure; case CSFC_Success: @@ -1033,7 +1033,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, // optimization. if (HadSkippedDecl) return CSFC_Failure; - + for (++I; I != E; ++I) if (CodeGenFunction::ContainsLabel(*I, true)) return CSFC_Failure; @@ -1047,7 +1047,7 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, assert(FoundCase && "Didn't find case but returned fallthrough?"); // We recursively found Case, so we're not looking for it anymore. Case = 0; - + // If we found the case and skipped declarations, we can't do the // optimization. if (HadSkippedDecl) @@ -1074,9 +1074,9 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, if (CodeGenFunction::ContainsLabel(*I, true)) return CSFC_Failure; return CSFC_Success; - } + } } - + return Case ? CSFC_Success : CSFC_FallThrough; } @@ -1088,11 +1088,11 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, return CSFC_Failure; return CSFC_Success; } - + // Otherwise, we want to include this statement. Everything is cool with that // so long as it doesn't contain a break out of the switch we're in. if (CodeGenFunction::containsBreak(S)) return CSFC_Failure; - + // Otherwise, everything is great. Include the statement and tell the caller // that we fall through and include the next statement as well. ResultStmts.push_back(S); @@ -1104,14 +1104,14 @@ static CSFC_Result CollectStatementsForCase(const Stmt *S, /// for a switch on constant. See the comment above CollectStatementsForCase /// for more details. static bool FindCaseStatementsForValue(const SwitchStmt &S, - const llvm::APInt &ConstantCondValue, + const llvm::APSInt &ConstantCondValue, SmallVectorImpl<const Stmt*> &ResultStmts, ASTContext &C) { // First step, find the switch case that is being branched to. We can do this // efficiently by scanning the SwitchCase list. const SwitchCase *Case = S.getSwitchCaseList(); const DefaultStmt *DefaultCase = 0; - + for (; Case; Case = Case->getNextSwitchCase()) { // It's either a default or case. Just remember the default statement in // case we're not jumping to any numbered cases. @@ -1119,17 +1119,17 @@ static bool FindCaseStatementsForValue(const SwitchStmt &S, DefaultCase = DS; continue; } - + // Check to see if this case is the one we're looking for. const CaseStmt *CS = cast<CaseStmt>(Case); // Don't handle case ranges yet. if (CS->getRHS()) return false; - + // If we found our case, remember it as 'case'. if (CS->getLHS()->EvaluateKnownConstInt(C) == ConstantCondValue) break; } - + // If we didn't find a matching case, we use a default if it exists, or we // elide the whole switch body! if (Case == 0) { @@ -1168,7 +1168,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { // See if we can constant fold the condition of the switch and therefore only // emit the live case statement (if any) of the switch. - llvm::APInt ConstantCondValue; + llvm::APSInt ConstantCondValue; if (ConstantFoldsToSimpleInteger(S.getCond(), ConstantCondValue)) { SmallVector<const Stmt*, 4> CaseStmts; if (FindCaseStatementsForValue(S, ConstantCondValue, CaseStmts, @@ -1192,7 +1192,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { return; } } - + llvm::Value *CondV = EmitScalarExpr(S.getCond()); // Create basic block to hold stuff that comes after switch @@ -1380,7 +1380,7 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str, if (!StrVal.empty()) { const SourceManager &SM = CGF.CGM.getContext().getSourceManager(); const LangOptions &LangOpts = CGF.CGM.getLangOpts(); - + // Add the location of the start of each subsequent line of the asm to the // MDNode. for (unsigned i = 0, e = StrVal.size()-1; i != e; ++i) { @@ -1390,8 +1390,8 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str, Locs.push_back(llvm::ConstantInt::get(CGF.Int32Ty, LineLoc.getRawEncoding())); } - } - + } + return llvm::MDNode::get(CGF.getLLVMContext(), Locs); } @@ -1441,7 +1441,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { std::vector<QualType> ResultRegQualTys; std::vector<llvm::Type *> ResultRegTypes; std::vector<llvm::Type *> ResultTruncRegTypes; - std::vector<llvm::Type*> ArgTypes; + std::vector<llvm::Type *> ArgTypes; std::vector<llvm::Value*> Args; // Keep track of inout constraints. @@ -1656,7 +1656,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // the expression, do the conversion. if (ResultRegTypes[i] != ResultTruncRegTypes[i]) { llvm::Type *TruncTy = ResultTruncRegTypes[i]; - + // Truncate the integer result to the right size, note that TruncTy can be // a pointer. if (TruncTy->isFloatingPointTy()) @@ -1681,3 +1681,25 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { EmitStoreThroughLValue(RValue::get(Tmp), ResultRegDests[i]); } } + +void CodeGenFunction::EmitMSAsmStmt(const MSAsmStmt &S) { + // MS-style inline assembly is not fully supported, so sema emits a warning. + if (!CGM.getCodeGenOpts().EmitMicrosoftInlineAsm) + return; + + assert (S.isSimple() && "CodeGen can only handle simple MSAsmStmts."); + + std::vector<llvm::Value*> Args; + std::vector<llvm::Type *> ArgTypes; + + std::string MachineClobbers = Target.getClobbers(); + + llvm::FunctionType *FTy = + llvm::FunctionType::get(VoidTy, ArgTypes, false); + + llvm::InlineAsm *IA = + llvm::InlineAsm::get(FTy, *S.getAsmString(), MachineClobbers, true); + llvm::CallInst *Result = Builder.CreateCall(IA, Args); + Result->addAttribute(~0, llvm::Attribute::NoUnwind); + Result->addAttribute(~0, llvm::Attribute::IANSDialect); +} diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 17a053757e2b..cdaa26a2bad6 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -355,13 +355,14 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn, llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); #ifndef NDEBUG - const CGFunctionInfo &CallFnInfo = - CGM.getTypes().arrangeFunctionCall(ResultType, CallArgs, FPT->getExtInfo(), + const CGFunctionInfo &CallFnInfo = + CGM.getTypes().arrangeCXXMethodCall(CallArgs, FPT, RequiredArgs::forPrototypePlus(FPT, 1)); assert(CallFnInfo.getRegParm() == FnInfo.getRegParm() && CallFnInfo.isNoReturn() == FnInfo.isNoReturn() && CallFnInfo.getCallingConvention() == FnInfo.getCallingConvention()); - assert(similar(CallFnInfo.getReturnInfo(), CallFnInfo.getReturnType(), + assert(isa<CXXDestructorDecl>(MD) || // ignore dtor return types + similar(CallFnInfo.getReturnInfo(), CallFnInfo.getReturnType(), FnInfo.getReturnInfo(), FnInfo.getReturnType())); assert(CallFnInfo.arg_size() == FnInfo.arg_size()); for (unsigned i = 0, e = FnInfo.arg_size(); i != e; ++i) @@ -386,6 +387,9 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn, if (!ResultType->isVoidType() && Slot.isNull()) CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType); + // Disable the final ARC autorelease. + AutoreleaseResult = false; + FinishFunction(); // Set the right linkage. @@ -569,14 +573,13 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD, if (cast<CXXMethodDecl>(GD.getDecl())->isPure()) { // We have a pure virtual member function. if (!PureVirtualFn) { - llvm::FunctionType *Ty = - llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); - PureVirtualFn = - CGM.CreateRuntimeFunction(Ty, "__cxa_pure_virtual"); - PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn, - Int8PtrTy); + llvm::FunctionType *Ty =
+ llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
+ StringRef PureCallName = CGM.getCXXABI().GetPureVirtualCallName();
+ PureVirtualFn = CGM.CreateRuntimeFunction(Ty, PureCallName);
+ PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn,
+ CGM.Int8PtrTy); } - Init = PureVirtualFn; } else { // Check if we should use a thunk. diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index ac704e7dca40..a46f313f1f7a 100644 --- a/lib/CodeGen/CGValue.h +++ b/lib/CodeGen/CGValue.h @@ -153,7 +153,7 @@ class LValue { private: void Initialize(QualType Type, Qualifiers Quals, - CharUnits Alignment = CharUnits(), + CharUnits Alignment, llvm::MDNode *TBAAInfo = 0) { this->Type = Type; this->Quals = Quals; @@ -295,12 +295,12 @@ public: /// access. static LValue MakeBitfield(llvm::Value *BaseValue, const CGBitFieldInfo &Info, - QualType type) { + QualType type, CharUnits Alignment) { LValue R; R.LVType = BitField; R.V = BaseValue; R.BitFieldInfo = &Info; - R.Initialize(type, type.getQualifiers()); + R.Initialize(type, type.getQualifiers(), Alignment); return R; } @@ -389,7 +389,8 @@ public: return AV; } - static AggValueSlot forLValue(LValue LV, IsDestructed_t isDestructed, + static AggValueSlot forLValue(const LValue &LV, + IsDestructed_t isDestructed, NeedsGCBarriers_t needsGC, IsAliased_t isAliased, IsZeroed_t isZeroed = IsNotZeroed) { diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt index 7b1dbce1a85e..76be85f939a3 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/lib/CodeGen/CMakeLists.txt @@ -8,8 +8,6 @@ set(LLVM_LINK_COMPONENTS vectorize ) -set(LLVM_USED_LIBS clangBasic clangAST clangFrontend) - add_clang_library(clangCodeGen BackendUtil.cpp CGBlocks.cpp @@ -52,5 +50,19 @@ add_clang_library(clangCodeGen TargetInfo.cpp ) -add_dependencies(clangCodeGen ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) +add_dependencies(clangCodeGen + ClangARMNeon + ClangAttrClasses + ClangAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangStmtNodes + ) + +target_link_libraries(clangCodeGen + clangBasic + clangAST + clangFrontend + ) diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 2939062c9bbb..1d02861ed786 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -23,12 +23,12 @@ #include "clang/AST/StmtCXX.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/Intrinsics.h" -#include "llvm/Support/MDBuilder.h" +#include "llvm/MDBuilder.h" #include "llvm/Target/TargetData.h" using namespace clang; using namespace CodeGen; -CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) +CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) : CodeGenTypeCache(cgm), CGM(cgm), Target(CGM.getContext().getTargetInfo()), Builder(cgm.getModule().getContext()), @@ -42,7 +42,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) TerminateHandler(0), TrapBB(0) { CatchUndefined = getContext().getLangOpts().CatchUndefined; - CGM.getCXXABI().getMangleContext().startNewFunction(); + if (!suppressNewContext) + CGM.getCXXABI().getMangleContext().startNewFunction(); } CodeGenFunction::~CodeGenFunction() { @@ -251,6 +252,81 @@ void CodeGenFunction::EmitMCountInstrumentation() { Builder.CreateCall(MCountFn); } +// OpenCL v1.2 s5.6.4.6 allows the compiler to store kernel argument +// information in the program executable. The argument information stored +// includes the argument name, its type, the address and access qualifiers used. +// FIXME: Add type, address, and access qualifiers. +static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn, + CodeGenModule &CGM,llvm::LLVMContext &Context, + llvm::SmallVector <llvm::Value*, 5> &kernelMDArgs) { + + // Create MDNodes that represents the kernel arg metadata. + // Each MDNode is a list in the form of "key", N number of values which is + // the same number of values as their are kernel arguments. + + // MDNode for the kernel argument names. + SmallVector<llvm::Value*, 8> argNames; + argNames.push_back(llvm::MDString::get(Context, "kernel_arg_name")); + + for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) { + const ParmVarDecl *parm = FD->getParamDecl(i); + + // Get argument name. + argNames.push_back(llvm::MDString::get(Context, parm->getName())); + + } + // Add MDNode to the list of all metadata. + kernelMDArgs.push_back(llvm::MDNode::get(Context, argNames)); +} + +void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD, + llvm::Function *Fn) +{ + if (!FD->hasAttr<OpenCLKernelAttr>()) + return; + + llvm::LLVMContext &Context = getLLVMContext(); + + llvm::SmallVector <llvm::Value*, 5> kernelMDArgs; + kernelMDArgs.push_back(Fn); + + if (CGM.getCodeGenOpts().EmitOpenCLArgMetadata) + GenOpenCLArgMetadata(FD, Fn, CGM, Context, kernelMDArgs); + + if (FD->hasAttr<WorkGroupSizeHintAttr>()) { + llvm::SmallVector <llvm::Value*, 5> attrMDArgs; + attrMDArgs.push_back(llvm::MDString::get(Context, "work_group_size_hint")); + WorkGroupSizeHintAttr *attr = FD->getAttr<WorkGroupSizeHintAttr>(); + llvm::Type *iTy = llvm::IntegerType::get(Context, 32); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getXDim()))); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getYDim()))); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getZDim()))); + kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs)); + } + + if (FD->hasAttr<ReqdWorkGroupSizeAttr>()) { + llvm::SmallVector <llvm::Value*, 5> attrMDArgs; + attrMDArgs.push_back(llvm::MDString::get(Context, "reqd_work_group_size")); + ReqdWorkGroupSizeAttr *attr = FD->getAttr<ReqdWorkGroupSizeAttr>(); + llvm::Type *iTy = llvm::IntegerType::get(Context, 32); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getXDim()))); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getYDim()))); + attrMDArgs.push_back(llvm::ConstantInt::get(iTy, + llvm::APInt(32, (uint64_t)attr->getZDim()))); + kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs)); + } + + llvm::MDNode *kernelMDNode = llvm::MDNode::get(Context, kernelMDArgs); + llvm::NamedMDNode *OpenCLKernelMetadata = + CGM.getModule().getOrInsertNamedMetadata("opencl.kernels"); + OpenCLKernelMetadata->addOperand(kernelMDNode); +} + void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, llvm::Function *Fn, const CGFunctionInfo &FnInfo, @@ -279,14 +355,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, if (getContext().getLangOpts().OpenCL) { // Add metadata for a kernel function. if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) - if (FD->hasAttr<OpenCLKernelAttr>()) { - llvm::LLVMContext &Context = getLLVMContext(); - llvm::NamedMDNode *OpenCLMetadata = - CGM.getModule().getOrInsertNamedMetadata("opencl.kernels"); - - llvm::Value *Op = Fn; - OpenCLMetadata->addOperand(llvm::MDNode::get(Context, Op)); - } + EmitOpenCLKernelMetadata(FD, Fn); } llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn); @@ -537,7 +606,7 @@ bool CodeGenFunction::containsBreak(const Stmt *S) { /// constant folds return true and set the boolean result in Result. bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond, bool &ResultBool) { - llvm::APInt ResultInt; + llvm::APSInt ResultInt; if (!ConstantFoldsToSimpleInteger(Cond, ResultInt)) return false; @@ -549,7 +618,7 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond, /// to a constant, or if it does but contains a label, return false. If it /// constant folds return true and set the folded value. bool CodeGenFunction:: -ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APInt &ResultInt) { +ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &ResultInt) { // FIXME: Rename and handle conversion of other evaluatable things // to bool. llvm::APSInt Int; @@ -687,10 +756,10 @@ void CodeGenFunction::ErrorUnsupported(const Stmt *S, const char *Type, /// emitNonZeroVLAInit - Emit the "zero" initialization of a /// variable-length array whose elements have a non-zero bit-pattern. /// +/// \param baseType the inner-most element type of the array /// \param src - a char* pointing to the bit-pattern for a single /// base element of the array /// \param sizeInChars - the total size of the VLA, in chars -/// \param align - the total alignment of the VLA static void emitNonZeroVLAInit(CodeGenFunction &CGF, QualType baseType, llvm::Value *dest, llvm::Value *src, llvm::Value *sizeInChars) { @@ -881,33 +950,49 @@ llvm::Value *CodeGenFunction::emitArrayLength(const ArrayType *origArrayType, llvm::ConstantInt *zero = Builder.getInt32(0); gepIndices.push_back(zero); - // It's more efficient to calculate the count from the LLVM - // constant-length arrays than to re-evaluate the array bounds. uint64_t countFromCLAs = 1; + QualType eltType; llvm::ArrayType *llvmArrayType = - cast<llvm::ArrayType>( + dyn_cast<llvm::ArrayType>( cast<llvm::PointerType>(addr->getType())->getElementType()); - while (true) { + while (llvmArrayType) { assert(isa<ConstantArrayType>(arrayType)); assert(cast<ConstantArrayType>(arrayType)->getSize().getZExtValue() == llvmArrayType->getNumElements()); gepIndices.push_back(zero); countFromCLAs *= llvmArrayType->getNumElements(); + eltType = arrayType->getElementType(); llvmArrayType = dyn_cast<llvm::ArrayType>(llvmArrayType->getElementType()); - if (!llvmArrayType) break; - arrayType = getContext().getAsArrayType(arrayType->getElementType()); - assert(arrayType && "LLVM and Clang types are out-of-synch"); + assert((!llvmArrayType || arrayType) && + "LLVM and Clang types are out-of-synch"); } - baseType = arrayType->getElementType(); + if (arrayType) { + // From this point onwards, the Clang array type has been emitted + // as some other type (probably a packed struct). Compute the array + // size, and just emit the 'begin' expression as a bitcast. + while (arrayType) { + countFromCLAs *= + cast<ConstantArrayType>(arrayType)->getSize().getZExtValue(); + eltType = arrayType->getElementType(); + arrayType = getContext().getAsArrayType(eltType); + } + + unsigned AddressSpace = + cast<llvm::PointerType>(addr->getType())->getAddressSpace(); + llvm::Type *BaseType = ConvertType(eltType)->getPointerTo(AddressSpace); + addr = Builder.CreateBitCast(addr, BaseType, "array.begin"); + } else { + // Create the actual GEP. + addr = Builder.CreateInBoundsGEP(addr, gepIndices, "array.begin"); + } - // Create the actual GEP. - addr = Builder.CreateInBoundsGEP(addr, gepIndices, "array.begin"); + baseType = eltType; llvm::Value *numElements = llvm::ConstantInt::get(SizeTy, countFromCLAs); @@ -1071,7 +1156,8 @@ void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E, llvm::Constant *Init) { assert (Init && "Invalid DeclRefExpr initializer!"); if (CGDebugInfo *Dbg = getDebugInfo()) - Dbg->EmitGlobalVariable(E->getDecl(), Init); + if (CGM.getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) + Dbg->EmitGlobalVariable(E->getDecl(), Init); } CodeGenFunction::PeepholeProtection diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 83f1e2df9f45..ed3e43beb0f8 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -591,6 +591,11 @@ public: /// we prefer to insert allocas. llvm::AssertingVH<llvm::Instruction> AllocaInsertPt; + /// BoundsChecking - Emit run-time bounds checks. Higher values mean + /// potentially higher performance penalties. + unsigned char BoundsChecking; + + /// CatchUndefined - Emit run-time checks to catch undefined behaviors. bool CatchUndefined; /// In ARC, whether we should autorelease the return value. @@ -1192,8 +1197,18 @@ private: llvm::BasicBlock *TerminateHandler; llvm::BasicBlock *TrapBB; + /// Add a kernel metadata node to the named metadata node 'opencl.kernels'. + /// In the kernel metadata node, reference the kernel function and metadata + /// nodes for its optional attribute qualifiers (OpenCL 1.1 6.7.2): + /// - A node for the work_group_size_hint(X,Y,Z) qualifier contains string + /// "work_group_size_hint", and three 32-bit integers X, Y and Z. + /// - A node for the reqd_work_group_size(X,Y,Z) qualifier contains string + /// "reqd_work_group_size", and three 32-bit integers X, Y and Z. + void EmitOpenCLKernelMetadata(const FunctionDecl *FD, + llvm::Function *Fn); + public: - CodeGenFunction(CodeGenModule &cgm); + CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext=false); ~CodeGenFunction(); CodeGenTypes &getTypes() const { return CGM.getTypes(); } @@ -1305,6 +1320,7 @@ public: const ObjCPropertyImplDecl *PID); void generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, + const ObjCMethodDecl *GetterMothodDecl, llvm::Constant *AtomicHelperFn); void GenerateObjCCtorDtorMethod(ObjCImplementationDecl *IMP, @@ -1560,6 +1576,7 @@ public: return LValue::MakeAddr(V, T, Alignment, getContext(), CGM.getTBAAInfo(T)); } + LValue MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T) { CharUnits Alignment; if (!T->isIncompleteType()) @@ -1616,8 +1633,8 @@ public: /// /// \param IgnoreResult - True if the resulting value isn't used. RValue EmitAnyExpr(const Expr *E, - AggValueSlot AggSlot = AggValueSlot::ignored(), - bool IgnoreResult = false); + AggValueSlot aggSlot = AggValueSlot::ignored(), + bool ignoreResult = false); // EmitVAListRef - Emit a "reference" to a va_list; this is either the address // or the value of the expression, depending on how va_list is defined. @@ -1643,7 +1660,7 @@ public: /// volatile. void EmitAggregateCopy(llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType EltTy, bool isVolatile=false, - unsigned Alignment = 0); + CharUnits Alignment = CharUnits::Zero()); /// StartBlock - Start new block named N. If insert block is a dummy block /// then reuse it. @@ -1964,6 +1981,7 @@ public: void EmitCaseStmt(const CaseStmt &S); void EmitCaseStmtRange(const CaseStmt &S); void EmitAsmStmt(const AsmStmt &S); + void EmitMSAsmStmt(const MSAsmStmt &S); void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S); void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); @@ -2099,6 +2117,7 @@ public: LValue EmitMemberExpr(const MemberExpr *E); LValue EmitObjCIsaExpr(const ObjCIsaExpr *E); LValue EmitCompoundLiteralLValue(const CompoundLiteralExpr *E); + LValue EmitInitListLValue(const InitListExpr *E); LValue EmitConditionalOperatorLValue(const AbstractConditionalOperator *E); LValue EmitCastLValue(const CastExpr *E); LValue EmitNullInitializationLValue(const CXXScalarValueInitExpr *E); @@ -2143,9 +2162,6 @@ public: llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); - LValue EmitLValueForAnonRecordField(llvm::Value* Base, - const IndirectFieldDecl* Field, - unsigned CVRQualifiers); LValue EmitLValueForField(LValue Base, const FieldDecl* Field); /// EmitLValueForFieldInitialization - Like EmitLValueForField, except that @@ -2158,9 +2174,6 @@ public: llvm::Value* Base, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers); - LValue EmitLValueForBitfield(llvm::Value* Base, const FieldDecl* Field, - unsigned CVRQualifiers); - LValue EmitCXXConstructLValue(const CXXConstructExpr *E); LValue EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E); LValue EmitLambdaLValue(const LambdaExpr *E); @@ -2259,12 +2272,11 @@ public: llvm::Value *BuildVector(ArrayRef<llvm::Value*> Ops); llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E); - llvm::Value *EmitHexagonBuiltinExpr(unsigned BuiltinID, const CallExpr *E); llvm::Value *EmitPPCBuiltinExpr(unsigned BuiltinID, const CallExpr *E); llvm::Value *EmitObjCProtocolExpr(const ObjCProtocolExpr *E); llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E); - llvm::Value *EmitObjCNumericLiteral(const ObjCNumericLiteral *E); + llvm::Value *EmitObjCBoxedExpr(const ObjCBoxedExpr *E); llvm::Value *EmitObjCArrayLiteral(const ObjCArrayLiteral *E); llvm::Value *EmitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E); llvm::Value *EmitObjCCollectionLiteral(const Expr *E, @@ -2359,7 +2371,7 @@ public: /// EmitAggExpr - Emit the computation of the specified expression /// of aggregate type. The result is computed into the given slot, /// which may be null to indicate that the value is not needed. - void EmitAggExpr(const Expr *E, AggValueSlot AS, bool IgnoreResult = false); + void EmitAggExpr(const Expr *E, AggValueSlot AS); /// EmitAggExprToLValue - Emit the computation of the specified expression of /// aggregate type into a temporary LValue. @@ -2411,10 +2423,9 @@ public: void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr, bool PerformInit); - /// EmitCXXGlobalDtorRegistration - Emits a call to register the global ptr - /// with the C++ runtime so that its destructor will be called at exit. - void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, - llvm::Constant *DeclPtr); + /// Call atexit() with a function that passes the given argument to + /// the given function. + void registerGlobalDtorWithAtExit(llvm::Constant *fn, llvm::Constant *addr); /// Emit code in this function to perform a guarded variable /// initialization. Guarded initializations are used when it's not @@ -2497,7 +2508,7 @@ public: /// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// to a constant, or if it does but contains a label, return false. If it /// constant folds return true and set the folded value. - bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APInt &Result); + bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result); /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an /// if statement) to the specified blocks. Based on the condition, this might diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 9a55c0848006..3ae3c521300b 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -102,14 +102,16 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, if (LangOpts.CUDA) createCUDARuntime(); - // Enable TBAA unless it's suppressed. - if (!CodeGenOpts.RelaxedAliasing && CodeGenOpts.OptimizationLevel > 0) - TBAA = new CodeGenTBAA(Context, VMContext, getLangOpts(), + // Enable TBAA unless it's suppressed. ThreadSanitizer needs TBAA even at O0. + if (LangOpts.ThreadSanitizer || + (!CodeGenOpts.RelaxedAliasing && CodeGenOpts.OptimizationLevel > 0)) + TBAA = new CodeGenTBAA(Context, VMContext, CodeGenOpts, getLangOpts(), ABI.getMangleContext()); // If debug info or coverage generation is enabled, create the CGDebugInfo // object. - if (CodeGenOpts.DebugInfo || CodeGenOpts.EmitGcovArcs || + if (CodeGenOpts.DebugInfo != CodeGenOptions::NoDebugInfo || + CodeGenOpts.EmitGcovArcs || CodeGenOpts.EmitGcovNotes) DebugInfo = new CGDebugInfo(*this); @@ -133,10 +135,22 @@ CodeGenModule::~CodeGenModule() { } void CodeGenModule::createObjCRuntime() { - if (!LangOpts.NeXTRuntime) + // This is just isGNUFamily(), but we want to force implementors of + // new ABIs to decide how best to do this. + switch (LangOpts.ObjCRuntime.getKind()) { + case ObjCRuntime::GNUstep: + case ObjCRuntime::GCC: + case ObjCRuntime::ObjFW: ObjCRuntime = CreateGNUObjCRuntime(*this); - else + return; + + case ObjCRuntime::FragileMacOSX: + case ObjCRuntime::MacOSX: + case ObjCRuntime::iOS: ObjCRuntime = CreateMacObjCRuntime(*this); + return; + } + llvm_unreachable("bad runtime kind"); } void CodeGenModule::createOpenCLRuntime() { @@ -245,6 +259,45 @@ void CodeGenModule::setGlobalVisibility(llvm::GlobalValue *GV, GV->setVisibility(GetLLVMVisibility(LV.visibility())); } +static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(StringRef S) { + return llvm::StringSwitch<llvm::GlobalVariable::ThreadLocalMode>(S) + .Case("global-dynamic", llvm::GlobalVariable::GeneralDynamicTLSModel) + .Case("local-dynamic", llvm::GlobalVariable::LocalDynamicTLSModel) + .Case("initial-exec", llvm::GlobalVariable::InitialExecTLSModel) + .Case("local-exec", llvm::GlobalVariable::LocalExecTLSModel); +} + +static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel( + CodeGenOptions::TLSModel M) { + switch (M) { + case CodeGenOptions::GeneralDynamicTLSModel: + return llvm::GlobalVariable::GeneralDynamicTLSModel; + case CodeGenOptions::LocalDynamicTLSModel: + return llvm::GlobalVariable::LocalDynamicTLSModel; + case CodeGenOptions::InitialExecTLSModel: + return llvm::GlobalVariable::InitialExecTLSModel; + case CodeGenOptions::LocalExecTLSModel: + return llvm::GlobalVariable::LocalExecTLSModel; + } + llvm_unreachable("Invalid TLS model!"); +} + +void CodeGenModule::setTLSMode(llvm::GlobalVariable *GV, + const VarDecl &D) const { + assert(D.isThreadSpecified() && "setting TLS mode on non-TLS var!"); + + llvm::GlobalVariable::ThreadLocalMode TLM; + TLM = GetLLVMTLSModel(CodeGenOpts.DefaultTLSModel); + + // Override the TLS model if it is explicitly specified. + if (D.hasAttr<TLSModelAttr>()) { + const TLSModelAttr *Attr = D.getAttr<TLSModelAttr>(); + TLM = GetLLVMTLSModel(Attr->getModel()); + } + + GV->setThreadLocalMode(TLM); +} + /// Set the symbol visibility of type information (vtable and RTTI) /// associated with the given type. void CodeGenModule::setTypeVisibility(llvm::GlobalValue *GV, @@ -334,7 +387,8 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) { else if (const CXXDestructorDecl *D = dyn_cast<CXXDestructorDecl>(ND)) getCXXABI().getMangleContext().mangleCXXDtor(D, GD.getDtorType(), Out); else if (const BlockDecl *BD = dyn_cast<BlockDecl>(ND)) - getCXXABI().getMangleContext().mangleBlock(BD, Out); + getCXXABI().getMangleContext().mangleBlock(BD, Out, + dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl())); else getCXXABI().getMangleContext().mangleName(ND, Out); @@ -355,7 +409,8 @@ void CodeGenModule::getBlockMangledName(GlobalDecl GD, MangleBuffer &Buffer, const Decl *D = GD.getDecl(); llvm::raw_svector_ostream Out(Buffer.getBuffer()); if (D == 0) - MangleCtx.mangleGlobalBlock(BD, Out); + MangleCtx.mangleGlobalBlock(BD, + dyn_cast_or_null<VarDecl>(initializedGlobalDecl.getDecl()), Out); else if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(D)) MangleCtx.mangleCtorBlock(CD, GD.getCtorType(), BD, Out); else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D)) @@ -474,8 +529,7 @@ void CodeGenModule::SetLLVMFunctionAttributes(const Decl *D, unsigned CallingConv; AttributeListType AttributeList; ConstructAttributeList(Info, D, AttributeList, CallingConv); - F->setAttributes(llvm::AttrListPtr::get(AttributeList.begin(), - AttributeList.size())); + F->setAttributes(llvm::AttrListPtr::get(AttributeList)); F->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv)); } @@ -493,7 +547,7 @@ static bool hasUnwindExceptions(const LangOptions &LangOpts) { // If ObjC exceptions are enabled, this depends on the ABI. if (LangOpts.ObjCExceptions) { - if (!LangOpts.ObjCNonFragileABI) return false; + return LangOpts.ObjCRuntime.hasUnwindExceptions(); } return true; @@ -517,10 +571,14 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, F->addFnAttr(llvm::Attribute::NoInline); // (noinline wins over always_inline, and we can't specify both in IR) - if (D->hasAttr<AlwaysInlineAttr>() && + if ((D->hasAttr<AlwaysInlineAttr>() || D->hasAttr<ForceInlineAttr>()) && !F->hasFnAttr(llvm::Attribute::NoInline)) F->addFnAttr(llvm::Attribute::AlwaysInline); + // FIXME: Communicate hot and cold attributes to LLVM more directly. + if (D->hasAttr<ColdAttr>()) + F->addFnAttr(llvm::Attribute::OptimizeForSize); + if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D)) F->setUnnamedAddr(true); @@ -652,7 +710,7 @@ void CodeGenModule::EmitDeferred() { if (!DeferredVTables.empty()) { const CXXRecordDecl *RD = DeferredVTables.back(); DeferredVTables.pop_back(); - getVTables().GenerateClassData(getVTableLinkage(RD), RD); + getCXXABI().EmitVTables(RD); continue; } @@ -930,7 +988,7 @@ CodeGenModule::shouldEmitFunction(const FunctionDecl *F) { if (getFunctionLinkage(F) != llvm::Function::AvailableExternallyLinkage) return true; if (CodeGenOpts.OptimizationLevel == 0 && - !F->hasAttr<AlwaysInlineAttr>()) + !F->hasAttr<AlwaysInlineAttr>() && !F->hasAttr<ForceInlineAttr>()) return false; // PR9614. Avoid cases where the source code is lying to us. An available // externally function should have an equivalent function somewhere else, @@ -1054,6 +1112,7 @@ CodeGenModule::GetOrCreateLLVMFunction(StringRef MangledName, } else if (getLangOpts().CPlusPlus && D.getDecl()) { // Look for a declaration that's lexically in a record. const FunctionDecl *FD = cast<FunctionDecl>(D.getDecl()); + FD = FD->getMostRecentDecl(); do { if (isa<CXXRecordDecl>(FD->getLexicalDeclContext())) { if (FD->isImplicit() && !ForVTable) { @@ -1166,11 +1225,12 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, DeferredDecls.erase(DDI); } + unsigned AddrSpace = GetGlobalVarAddressSpace(D, Ty->getAddressSpace()); llvm::GlobalVariable *GV = new llvm::GlobalVariable(getModule(), Ty->getElementType(), false, llvm::GlobalValue::ExternalLinkage, 0, MangledName, 0, - false, Ty->getAddressSpace()); + llvm::GlobalVariable::NotThreadLocal, AddrSpace); // Handle things which are present even on external declarations. if (D) { @@ -1193,10 +1253,14 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, GV->setVisibility(GetLLVMVisibility(LV.visibility())); } - GV->setThreadLocal(D->isThreadSpecified()); + if (D->isThreadSpecified()) + setTLSMode(GV, *D); } - return GV; + if (AddrSpace != Ty->getAddressSpace()) + return llvm::ConstantExpr::getBitCast(GV, Ty); + else + return GV; } @@ -1286,7 +1350,7 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { void CodeGenModule::EmitVTable(CXXRecordDecl *Class, bool DefinitionRequired) { if (DefinitionRequired) - getVTables().GenerateClassData(getVTableLinkage(Class), Class); + getCXXABI().EmitVTables(Class); } llvm::GlobalVariable::LinkageTypes @@ -1481,6 +1545,20 @@ CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D, return llvmInit; } +unsigned CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D, + unsigned AddrSpace) { + if (LangOpts.CUDA && CodeGenOpts.CUDAIsDevice) { + if (D->hasAttr<CUDAConstantAttr>()) + AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_constant); + else if (D->hasAttr<CUDASharedAttr>()) + AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_shared); + else + AddrSpace = getContext().getTargetAddressSpace(LangAS::cuda_device); + } + + return AddrSpace; +} + void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { llvm::Constant *Init = 0; QualType ASTTy = D->getType(); @@ -1511,8 +1589,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { // FIXME: It does so in a global constructor, which is *not* what we // want. - if (!Init) + if (!Init) { + initializedGlobalDecl = GlobalDecl(D); Init = EmitConstantInit(*InitDecl); + } if (!Init) { QualType T = InitExpr->getType(); if (D->getType()->isReferenceType()) @@ -1560,7 +1640,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { if (GV == 0 || GV->getType()->getElementType() != InitType || GV->getType()->getAddressSpace() != - getContext().getTargetAddressSpace(ASTTy)) { + GetGlobalVarAddressSpace(D, getContext().getTargetAddressSpace(ASTTy))) { // Move the old entry aside so that we'll create a new one. Entry->setName(StringRef()); @@ -1604,7 +1684,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) - DI->EmitGlobalVariable(GV, D); + if (getCodeGenOpts().DebugInfo >= CodeGenOptions::LimitedDebugInfo) + DI->EmitGlobalVariable(GV, D); } llvm::GlobalValue::LinkageTypes @@ -1710,8 +1791,7 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old, ArgList.clear(); if (!NewCall->getType()->isVoidTy()) NewCall->takeName(CI); - NewCall->setAttributes(llvm::AttrListPtr::get(AttrVec.begin(), - AttrVec.end())); + NewCall->setAttributes(llvm::AttrListPtr::get(AttrVec)); NewCall->setCallingConv(CI->getCallingConv()); // Finally, remove the old call, replacing any uses with the new one. @@ -2059,7 +2139,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { std::string StringClass(getLangOpts().ObjCConstantStringClass); llvm::Type *Ty = getTypes().ConvertType(getContext().IntTy); llvm::Constant *GV; - if (LangOpts.ObjCNonFragileABI) { + if (LangOpts.ObjCRuntime.isNonFragile()) { std::string str = StringClass.empty() ? "OBJC_CLASS_$_NSConstantString" : "OBJC_CLASS_$_" + StringClass; @@ -2104,7 +2184,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); D->addDecl(Field); } @@ -2147,7 +2227,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { "_unnamed_nsstring_"); // FIXME. Fix section. if (const char *Sect = - LangOpts.ObjCNonFragileABI + LangOpts.ObjCRuntime.isNonFragile() ? getContext().getTargetInfo().getNSStringNonFragileABISection() : getContext().getTargetInfo().getNSStringSection()) GV->setSection(Sect); @@ -2179,7 +2259,7 @@ QualType CodeGenModule::getObjCFastEnumerationStateType() { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); D->addDecl(Field); } @@ -2506,14 +2586,8 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { // Forward declarations, no (immediate) code generation. case Decl::ObjCInterface: + case Decl::ObjCCategory: break; - - case Decl::ObjCCategory: { - ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D); - if (CD->IsClassExtension() && CD->hasSynthBitfield()) - Context.ResetObjCLayout(CD->getClassInterface()); - break; - } case Decl::ObjCProtocol: { ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(D); @@ -2530,8 +2604,6 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::ObjCImplementation: { ObjCImplementationDecl *OMD = cast<ObjCImplementationDecl>(D); - if (LangOpts.ObjCNonFragileABI2 && OMD->hasSynthBitfield()) - Context.ResetObjCLayout(OMD->getClassInterface()); EmitObjCPropertyImplementations(OMD); EmitObjCIvarInitializations(OMD); ObjCRuntime->GenerateClass(OMD); @@ -2564,7 +2636,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { const std::string &S = getModule().getModuleInlineAsm(); if (S.empty()) getModule().setModuleInlineAsm(AsmString); - else if (*--S.end() == '\n') + else if (S.end()[-1] == '\n') getModule().setModuleInlineAsm(S + AsmString.str()); else getModule().setModuleInlineAsm(S + '\n' + AsmString.str()); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 38f5008d89a8..d6ff50d5ad79 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -138,6 +138,7 @@ namespace CodeGen { union { unsigned char PointerAlignInBytes; unsigned char PointerSizeInBytes; + unsigned char SizeSizeInBytes; // sizeof(size_t) }; }; @@ -350,6 +351,8 @@ class CodeGenModule : public CodeGenTypeCache { struct { int GlobalUniqueCount; } Block; + + GlobalDecl initializedGlobalDecl; /// @} public: @@ -471,6 +474,10 @@ public: /// GlobalValue. void setGlobalVisibility(llvm::GlobalValue *GV, const NamedDecl *D) const; + /// setTLSMode - Set the TLS mode for the given LLVM GlobalVariable + /// for the thread-local variable declaration D. + void setTLSMode(llvm::GlobalVariable *GV, const VarDecl &D) const; + /// TypeVisibilityKind - The kind of global variable that is passed to /// setTypeVisibility enum TypeVisibilityKind { @@ -516,6 +523,12 @@ public: CreateOrReplaceCXXRuntimeVariable(StringRef Name, llvm::Type *Ty, llvm::GlobalValue::LinkageTypes Linkage); + /// GetGlobalVarAddressSpace - Return the address space of the underlying + /// global variable for D, as determined by its declaration. Normally this + /// is the same as the address space of D's type, but in CUDA, address spaces + /// are associated with declarations, not types. + unsigned GetGlobalVarAddressSpace(const VarDecl *D, unsigned AddrSpace); + /// GetAddrOfGlobalVar - Return the llvm::Constant for the address of the /// given global variable. If Ty is non-null and if the global doesn't exist, /// then it will be greated with the specified type instead of whatever the @@ -580,7 +593,7 @@ public: /// getUniqueBlockCount - Fetches the global unique block count. int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; } - + /// getBlockDescriptorType - Fetches the type of a generic block /// descriptor. llvm::Type *getBlockDescriptorType(); diff --git a/lib/CodeGen/CodeGenTBAA.cpp b/lib/CodeGen/CodeGenTBAA.cpp index a3cadcf39248..bab60afbb7f2 100644 --- a/lib/CodeGen/CodeGenTBAA.cpp +++ b/lib/CodeGen/CodeGenTBAA.cpp @@ -18,6 +18,7 @@ #include "CodeGenTBAA.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Mangle.h" +#include "clang/Frontend/CodeGenOptions.h" #include "llvm/LLVMContext.h" #include "llvm/Metadata.h" #include "llvm/Constants.h" @@ -26,8 +27,9 @@ using namespace clang; using namespace CodeGen; CodeGenTBAA::CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext& VMContext, + const CodeGenOptions &CGO, const LangOptions &Features, MangleContext &MContext) - : Context(Ctx), VMContext(VMContext), Features(Features), MContext(MContext), + : Context(Ctx), CodeGenOpts(CGO), Features(Features), MContext(MContext), MDHelper(VMContext), Root(0), Char(0) { } @@ -74,6 +76,10 @@ static bool TypeHasMayAlias(QualType QTy) { llvm::MDNode * CodeGenTBAA::getTBAAInfo(QualType QTy) { + // At -O0 TBAA is not emitted for regular types. + if (CodeGenOpts.OptimizationLevel == 0 || CodeGenOpts.RelaxedAliasing) + return NULL; + // If the type has the may_alias attribute (even on a typedef), it is // effectively in the general char alias class. if (TypeHasMayAlias(QTy)) diff --git a/lib/CodeGen/CodeGenTBAA.h b/lib/CodeGen/CodeGenTBAA.h index 4a9785287d01..c17a5cf03c60 100644 --- a/lib/CodeGen/CodeGenTBAA.h +++ b/lib/CodeGen/CodeGenTBAA.h @@ -16,8 +16,8 @@ #define CLANG_CODEGEN_CODEGENTBAA_H #include "clang/Basic/LLVM.h" +#include "llvm/MDBuilder.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/Support/MDBuilder.h" namespace llvm { class LLVMContext; @@ -26,6 +26,7 @@ namespace llvm { namespace clang { class ASTContext; + class CodeGenOptions; class LangOptions; class MangleContext; class QualType; @@ -38,7 +39,7 @@ namespace CodeGen { /// while lowering AST types to LLVM types. class CodeGenTBAA { ASTContext &Context; - llvm::LLVMContext& VMContext; + const CodeGenOptions &CodeGenOpts; const LangOptions &Features; MangleContext &MContext; @@ -61,6 +62,7 @@ class CodeGenTBAA { public: CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext &VMContext, + const CodeGenOptions &CGO, const LangOptions &Features, MangleContext &MContext); ~CodeGenTBAA(); diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 41fd536b5cc2..9a78daefc71c 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -474,11 +474,11 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { // build it. const CGFunctionInfo *FI; if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FT)) { - FI = &arrangeFunctionType( + FI = &arrangeFreeFunctionType( CanQual<FunctionProtoType>::CreateUnsafe(QualType(FPT, 0))); } else { const FunctionNoProtoType *FNPT = cast<FunctionNoProtoType>(FT); - FI = &arrangeFunctionType( + FI = &arrangeFreeFunctionType( CanQual<FunctionNoProtoType>::CreateUnsafe(QualType(FNPT, 0))); } diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index ba2b3ae54902..3c29d2d74626 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -189,26 +189,32 @@ public: const CGFunctionInfo &arrangeCXXDestructor(const CXXDestructorDecl *D, CXXDtorType Type); - const CGFunctionInfo &arrangeFunctionCall(const CallArgList &Args, - const FunctionType *Ty); - const CGFunctionInfo &arrangeFunctionCall(QualType ResTy, - const CallArgList &args, - const FunctionType::ExtInfo &info, - RequiredArgs required); - - const CGFunctionInfo &arrangeFunctionType(CanQual<FunctionProtoType> Ty); - const CGFunctionInfo &arrangeFunctionType(CanQual<FunctionNoProtoType> Ty); + const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args, + const FunctionType *Ty); + const CGFunctionInfo &arrangeFreeFunctionCall(QualType ResTy, + const CallArgList &args, + FunctionType::ExtInfo info, + RequiredArgs required); + + const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args, + const FunctionProtoType *type, + RequiredArgs required); + + const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionProtoType> Ty); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionNoProtoType> Ty); const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD, const FunctionProtoType *FTP); - /// Retrieves the ABI information for the given function signature. - /// This is the "core" routine to which all the others defer. + /// "Arrange" the LLVM information for a call or type with the given + /// signature. This is largely an internal method; other clients + /// should use one of the above routines, which ultimately defer to + /// this. /// /// \param argTypes - must all actually be canonical as params - const CGFunctionInfo &arrangeFunctionType(CanQualType returnType, - ArrayRef<CanQualType> argTypes, - const FunctionType::ExtInfo &info, - RequiredArgs args); + const CGFunctionInfo &arrangeLLVMFunctionInfo(CanQualType returnType, + ArrayRef<CanQualType> argTypes, + FunctionType::ExtInfo info, + RequiredArgs args); /// \brief Compute a new LLVM record layout object for the given record. CGRecordLayout *ComputeRecordLayout(const RecordDecl *D, diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 98f67f34dfe1..0b7ce36d5b94 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -20,6 +20,7 @@ #include "CGCXXABI.h" #include "CGRecordLayout.h" +#include "CGVTables.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include <clang/AST/Mangle.h> @@ -48,10 +49,6 @@ protected: return PtrDiffTy; } - bool NeedsArrayCookie(const CXXNewExpr *expr); - bool NeedsArrayCookie(const CXXDeleteExpr *expr, - QualType elementType); - public: ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) : CGCXXABI(CGM), PtrDiffTy(0), IsARM(IsARM) { } @@ -111,19 +108,24 @@ public: void EmitInstanceFunctionProlog(CodeGenFunction &CGF); - CharUnits GetArrayCookieSize(const CXXNewExpr *expr); + StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; } + + CharUnits getArrayCookieSizeImpl(QualType elementType); llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF, llvm::Value *NewPtr, llvm::Value *NumElements, const CXXNewExpr *expr, QualType ElementType); - void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, - const CXXDeleteExpr *expr, - QualType ElementType, llvm::Value *&NumElements, - llvm::Value *&AllocPtr, CharUnits &CookieSize); + llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *allocPtr, + CharUnits cookieSize); void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); + void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, + llvm::Constant *addr); + + void EmitVTables(const CXXRecordDecl *Class); }; class ARMCXXABI : public ItaniumCXXABI { @@ -148,16 +150,14 @@ public: void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResTy); - CharUnits GetArrayCookieSize(const CXXNewExpr *expr); + CharUnits getArrayCookieSizeImpl(QualType elementType); llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF, llvm::Value *NewPtr, llvm::Value *NumElements, const CXXNewExpr *expr, QualType ElementType); - void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, - const CXXDeleteExpr *expr, - QualType ElementType, llvm::Value *&NumElements, - llvm::Value *&AllocPtr, CharUnits &CookieSize); + llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, llvm::Value *allocPtr, + CharUnits cookieSize); private: /// \brief Returns true if the given instance method is one of the @@ -796,54 +796,11 @@ void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF, /************************** Array allocation cookies **************************/ -bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) { - // If the class's usual deallocation function takes two arguments, - // it needs a cookie. - if (expr->doesUsualArrayDeleteWantSize()) - return true; - - // Automatic Reference Counting: - // We need an array cookie for pointers with strong or weak lifetime. - QualType AllocatedType = expr->getAllocatedType(); - if (getContext().getLangOpts().ObjCAutoRefCount && - AllocatedType->isObjCLifetimeType()) { - switch (AllocatedType.getObjCLifetime()) { - case Qualifiers::OCL_None: - case Qualifiers::OCL_ExplicitNone: - case Qualifiers::OCL_Autoreleasing: - return false; - - case Qualifiers::OCL_Strong: - case Qualifiers::OCL_Weak: - return true; - } - } - - // Otherwise, if the class has a non-trivial destructor, it always - // needs a cookie. - const CXXRecordDecl *record = - AllocatedType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); - return (record && !record->hasTrivialDestructor()); -} - -bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr, - QualType elementType) { - // If the class's usual deallocation function takes two arguments, - // it needs a cookie. - if (expr->doesUsualArrayDeleteWantSize()) - return true; - - return elementType.isDestructedType(); -} - -CharUnits ItaniumCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) { - if (!NeedsArrayCookie(expr)) - return CharUnits::Zero(); - - // Padding is the maximum of sizeof(size_t) and alignof(elementType) - ASTContext &Ctx = getContext(); - return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()), - Ctx.getTypeAlignInChars(expr->getAllocatedType())); +CharUnits ItaniumCXXABI::getArrayCookieSizeImpl(QualType elementType) { + // The array cookie is a size_t; pad that up to the element alignment. + // The cookie is actually right-justified in that space. + return std::max(CharUnits::fromQuantity(CGM.SizeSizeInBytes), + CGM.getContext().getTypeAlignInChars(elementType)); } llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, @@ -851,7 +808,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, llvm::Value *NumElements, const CXXNewExpr *expr, QualType ElementType) { - assert(NeedsArrayCookie(expr)); + assert(requiresArrayCookie(expr)); unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace(); @@ -862,6 +819,7 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, // The size of the cookie. CharUnits CookieSize = std::max(SizeSize, Ctx.getTypeAlignInChars(ElementType)); + assert(CookieSize == getArrayCookieSizeImpl(ElementType)); // Compute an offset to the cookie. llvm::Value *CookiePtr = NewPtr; @@ -882,53 +840,25 @@ llvm::Value *ItaniumCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, CookieSize.getQuantity()); } -void ItaniumCXXABI::ReadArrayCookie(CodeGenFunction &CGF, - llvm::Value *Ptr, - const CXXDeleteExpr *expr, - QualType ElementType, - llvm::Value *&NumElements, - llvm::Value *&AllocPtr, - CharUnits &CookieSize) { - // Derive a char* in the same address space as the pointer. - unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace(); - llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS); - - // If we don't need an array cookie, bail out early. - if (!NeedsArrayCookie(expr, ElementType)) { - AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy); - NumElements = 0; - CookieSize = CharUnits::Zero(); - return; - } - - QualType SizeTy = getContext().getSizeType(); - CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy); - llvm::Type *SizeLTy = CGF.ConvertType(SizeTy); - - CookieSize - = std::max(SizeSize, getContext().getTypeAlignInChars(ElementType)); - - CharUnits NumElementsOffset = CookieSize - SizeSize; - - // Compute the allocated pointer. - AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy); - AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr, - -CookieSize.getQuantity()); - - llvm::Value *NumElementsPtr = AllocPtr; - if (!NumElementsOffset.isZero()) - NumElementsPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(NumElementsPtr, - NumElementsOffset.getQuantity()); - NumElementsPtr = - CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS)); - NumElements = CGF.Builder.CreateLoad(NumElementsPtr); +llvm::Value *ItaniumCXXABI::readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *allocPtr, + CharUnits cookieSize) { + // The element size is right-justified in the cookie. + llvm::Value *numElementsPtr = allocPtr; + CharUnits numElementsOffset = + cookieSize - CharUnits::fromQuantity(CGF.SizeSizeInBytes); + if (!numElementsOffset.isZero()) + numElementsPtr = + CGF.Builder.CreateConstInBoundsGEP1_64(numElementsPtr, + numElementsOffset.getQuantity()); + + unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace(); + numElementsPtr = + CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS)); + return CGF.Builder.CreateLoad(numElementsPtr); } -CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) { - if (!NeedsArrayCookie(expr)) - return CharUnits::Zero(); - +CharUnits ARMCXXABI::getArrayCookieSizeImpl(QualType elementType) { // On ARM, the cookie is always: // struct array_cookie { // std::size_t element_size; // element_size != 0 @@ -936,7 +866,7 @@ CharUnits ARMCXXABI::GetArrayCookieSize(const CXXNewExpr *expr) { // }; // TODO: what should we do if the allocated type actually wants // greater alignment? - return getContext().getTypeSizeInChars(getContext().getSizeType()) * 2; + return CharUnits::fromQuantity(2 * CGM.SizeSizeInBytes); } llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, @@ -944,7 +874,7 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, llvm::Value *NumElements, const CXXNewExpr *expr, QualType ElementType) { - assert(NeedsArrayCookie(expr)); + assert(requiresArrayCookie(expr)); // NewPtr is a char*. @@ -975,44 +905,18 @@ llvm::Value *ARMCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, CookieSize.getQuantity()); } -void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF, - llvm::Value *Ptr, - const CXXDeleteExpr *expr, - QualType ElementType, - llvm::Value *&NumElements, - llvm::Value *&AllocPtr, - CharUnits &CookieSize) { - // Derive a char* in the same address space as the pointer. - unsigned AS = cast<llvm::PointerType>(Ptr->getType())->getAddressSpace(); - llvm::Type *CharPtrTy = CGF.Builder.getInt8Ty()->getPointerTo(AS); - - // If we don't need an array cookie, bail out early. - if (!NeedsArrayCookie(expr, ElementType)) { - AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy); - NumElements = 0; - CookieSize = CharUnits::Zero(); - return; - } - - QualType SizeTy = getContext().getSizeType(); - CharUnits SizeSize = getContext().getTypeSizeInChars(SizeTy); - llvm::Type *SizeLTy = CGF.ConvertType(SizeTy); - - // The cookie size is always 2 * sizeof(size_t). - CookieSize = 2 * SizeSize; - - // The allocated pointer is the input ptr, minus that amount. - AllocPtr = CGF.Builder.CreateBitCast(Ptr, CharPtrTy); - AllocPtr = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr, - -CookieSize.getQuantity()); - - // The number of elements is at offset sizeof(size_t) relative to that. - llvm::Value *NumElementsPtr - = CGF.Builder.CreateConstInBoundsGEP1_64(AllocPtr, - SizeSize.getQuantity()); - NumElementsPtr = - CGF.Builder.CreateBitCast(NumElementsPtr, SizeLTy->getPointerTo(AS)); - NumElements = CGF.Builder.CreateLoad(NumElementsPtr); +llvm::Value *ARMCXXABI::readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *allocPtr, + CharUnits cookieSize) { + // The number of elements is at offset sizeof(size_t) relative to + // the allocated pointer. + llvm::Value *numElementsPtr + = CGF.Builder.CreateConstInBoundsGEP1_64(allocPtr, CGF.SizeSizeInBytes); + + unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace(); + numElementsPtr = + CGF.Builder.CreateBitCast(numElementsPtr, CGF.SizeTy->getPointerTo(AS)); + return CGF.Builder.CreateLoad(numElementsPtr); } /*********************** Static local initialization **************************/ @@ -1200,3 +1104,60 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, CGF.EmitBlock(EndBlock); } + +/// Register a global destructor using __cxa_atexit. +static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, + llvm::Constant *dtor, + llvm::Constant *addr) { + // We're assuming that the destructor function is something we can + // reasonably call with the default CC. Go ahead and cast it to the + // right prototype. + llvm::Type *dtorTy = + llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo(); + + // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); + llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy }; + llvm::FunctionType *atexitTy = + llvm::FunctionType::get(CGF.IntTy, paramTys, false); + + // Fetch the actual function. + llvm::Constant *atexit = + CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit"); + if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit)) + fn->setDoesNotThrow(); + + // Create a variable that binds the atexit to this shared object. + llvm::Constant *handle = + CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle"); + + llvm::Value *args[] = { + llvm::ConstantExpr::getBitCast(dtor, dtorTy), + llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy), + handle + }; + CGF.Builder.CreateCall(atexit, args)->setDoesNotThrow(); +} + +/// Register a global destructor as best as we know how. +void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + llvm::Constant *dtor, + llvm::Constant *addr) { + // Use __cxa_atexit if available. + if (CGM.getCodeGenOpts().CXAAtExit) { + return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr); + } + + // In Apple kexts, we want to add a global destructor entry. + // FIXME: shouldn't this be guarded by some variable? + if (CGM.getContext().getLangOpts().AppleKext) { + // Generate a global destructor entry. + return CGM.AddCXXDtorEntry(dtor, addr); + } + + CGF.registerGlobalDtorWithAtExit(dtor, addr); +} + +/// Generate and emit virtual tables for the given class. +void ItaniumCXXABI::EmitVTables(const CXXRecordDecl *Class) { + CGM.getVTables().GenerateClassData(CGM.getVTableLinkage(Class), Class); +} diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 825e0415227d..6a2925bbd953 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -28,6 +28,8 @@ class MicrosoftCXXABI : public CGCXXABI { public: MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {} + StringRef GetPureVirtualCallName() { return "_purecall"; } + void BuildConstructorSignature(const CXXConstructorDecl *Ctor, CXXCtorType Type, CanQualType &ResTy, @@ -56,6 +58,13 @@ public: // TODO: 'for base' flag } + void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, + llvm::GlobalVariable *DeclPtr, + bool PerformInit); + + void EmitVTables(const CXXRecordDecl *Class); + + // ==== Notes on array cookies ========= // // MSVC seems to only use cookies when the class has a destructor; a @@ -78,17 +87,92 @@ public: // delete[] p; // } // Whereas it prints "104" and "104" if you give A a destructor. - void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, - const CXXDeleteExpr *expr, - QualType ElementType, llvm::Value *&NumElements, - llvm::Value *&AllocPtr, CharUnits &CookieSize) { - CGF.CGM.ErrorUnsupported(expr, "don't know how to handle array cookies " - "in the Microsoft C++ ABI"); - } + + bool requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType); + bool requiresArrayCookie(const CXXNewExpr *expr); + CharUnits getArrayCookieSizeImpl(QualType type); + llvm::Value *InitializeArrayCookie(CodeGenFunction &CGF, + llvm::Value *NewPtr, + llvm::Value *NumElements, + const CXXNewExpr *expr, + QualType ElementType); + llvm::Value *readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *allocPtr, + CharUnits cookieSize); }; } +bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, + QualType elementType) { + // Microsoft seems to completely ignore the possibility of a + // two-argument usual deallocation function. + return elementType.isDestructedType(); +} + +bool MicrosoftCXXABI::requiresArrayCookie(const CXXNewExpr *expr) { + // Microsoft seems to completely ignore the possibility of a + // two-argument usual deallocation function. + return expr->getAllocatedType().isDestructedType(); +} + +CharUnits MicrosoftCXXABI::getArrayCookieSizeImpl(QualType type) { + // The array cookie is always a size_t; we then pad that out to the + // alignment of the element type. + ASTContext &Ctx = getContext(); + return std::max(Ctx.getTypeSizeInChars(Ctx.getSizeType()), + Ctx.getTypeAlignInChars(type)); +} + +llvm::Value *MicrosoftCXXABI::readArrayCookieImpl(CodeGenFunction &CGF, + llvm::Value *allocPtr, + CharUnits cookieSize) { + unsigned AS = cast<llvm::PointerType>(allocPtr->getType())->getAddressSpace(); + llvm::Value *numElementsPtr = + CGF.Builder.CreateBitCast(allocPtr, CGF.SizeTy->getPointerTo(AS)); + return CGF.Builder.CreateLoad(numElementsPtr); +} + +llvm::Value* MicrosoftCXXABI::InitializeArrayCookie(CodeGenFunction &CGF, + llvm::Value *newPtr, + llvm::Value *numElements, + const CXXNewExpr *expr, + QualType elementType) { + assert(requiresArrayCookie(expr)); + + // The size of the cookie. + CharUnits cookieSize = getArrayCookieSizeImpl(elementType); + + // Compute an offset to the cookie. + llvm::Value *cookiePtr = newPtr; + + // Write the number of elements into the appropriate slot. + unsigned AS = cast<llvm::PointerType>(newPtr->getType())->getAddressSpace(); + llvm::Value *numElementsPtr + = CGF.Builder.CreateBitCast(cookiePtr, CGF.SizeTy->getPointerTo(AS)); + CGF.Builder.CreateStore(numElements, numElementsPtr); + + // Finally, compute a pointer to the actual data buffer by skipping + // over the cookie completely. + return CGF.Builder.CreateConstInBoundsGEP1_64(newPtr, + cookieSize.getQuantity()); +} + +void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, + llvm::GlobalVariable *DeclPtr, + bool PerformInit) { + // FIXME: this code was only tested for global initialization. + // Not sure whether we want thread-safe static local variables as VS + // doesn't make them thread-safe. + + // Emit the initializer and add a global destructor if appropriate. + CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit); +} + +void MicrosoftCXXABI::EmitVTables(const CXXRecordDecl *Class) { + // FIXME: implement +} + CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { return new MicrosoftCXXABI(CGM); } diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 2b71fdd504e7..9c23ed9871da 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -413,12 +413,18 @@ static llvm::Type* X86AdjustInlineAsmType(CodeGen::CodeGenFunction &CGF, /// X86_32ABIInfo - The X86-32 ABI information. class X86_32ABIInfo : public ABIInfo { + enum Class { + Integer, + Float + }; + static const unsigned MinABIStackAlignInBytes = 4; bool IsDarwinVectorABI; bool IsSmallStructInRegABI; bool IsMMXDisabled; bool IsWin32FloatStructABI; + unsigned DefaultNumRegisterParameters; static bool isRegisterSize(unsigned Size) { return (Size == 8 || Size == 16 || Size == 32 || Size == 64); @@ -434,33 +440,31 @@ class X86_32ABIInfo : public ABIInfo { /// \brief Return the alignment to use for the given type on the stack. unsigned getTypeStackAlignInBytes(QualType Ty, unsigned Align) const; -public: - - ABIArgInfo classifyReturnType(QualType RetTy, + Class classify(QualType Ty) const; + ABIArgInfo classifyReturnType(QualType RetTy, unsigned callingConvention) const; + ABIArgInfo classifyArgumentTypeWithReg(QualType RetTy, + unsigned &FreeRegs) const; ABIArgInfo classifyArgumentType(QualType RetTy) const; - virtual void computeInfo(CGFunctionInfo &FI) const { - FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), - FI.getCallingConvention()); - for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); - it != ie; ++it) - it->info = classifyArgumentType(it->type); - } +public: + virtual void computeInfo(CGFunctionInfo &FI) const; virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, CodeGenFunction &CGF) const; - X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w) + X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w, + unsigned r) : ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p), - IsMMXDisabled(m), IsWin32FloatStructABI(w) {} + IsMMXDisabled(m), IsWin32FloatStructABI(w), + DefaultNumRegisterParameters(r) {} }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { public: X86_32TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, - bool d, bool p, bool m, bool w) - :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w)) {} + bool d, bool p, bool m, bool w, unsigned r) + :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w, r)) {} void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const; @@ -626,6 +630,10 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); } +static bool isSSEVectorType(ASTContext &Context, QualType Ty) { + return Ty->getAs<VectorType>() && Context.getTypeSize(Ty) == 128; +} + static bool isRecordWithSSEVectorType(ASTContext &Context, QualType Ty) { const RecordType *RT = Ty->getAs<RecordType>(); if (!RT) @@ -643,7 +651,7 @@ static bool isRecordWithSSEVectorType(ASTContext &Context, QualType Ty) { i != e; ++i) { QualType FT = i->getType(); - if (FT->getAs<VectorType>() && Context.getTypeSize(FT) == 128) + if (isSSEVectorType(Context, FT)) return true; if (isRecordWithSSEVectorType(Context, FT)) @@ -667,7 +675,8 @@ unsigned X86_32ABIInfo::getTypeStackAlignInBytes(QualType Ty, } // Otherwise, if the type contains an SSE vector type, the alignment is 16. - if (Align >= 16 && isRecordWithSSEVectorType(getContext(), Ty)) + if (Align >= 16 && (isSSEVectorType(getContext(), Ty) || + isRecordWithSSEVectorType(getContext(), Ty))) return 16; return MinABIStackAlignInBytes; @@ -692,6 +701,57 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, bool ByVal) const { return ABIArgInfo::getIndirect(StackAlign); } +X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const { + const Type *T = isSingleElementStruct(Ty, getContext()); + if (!T) + T = Ty.getTypePtr(); + + if (const BuiltinType *BT = T->getAs<BuiltinType>()) { + BuiltinType::Kind K = BT->getKind(); + if (K == BuiltinType::Float || K == BuiltinType::Double) + return Float; + } + return Integer; +} + +ABIArgInfo +X86_32ABIInfo::classifyArgumentTypeWithReg(QualType Ty, + unsigned &FreeRegs) const { + // Common case first. + if (FreeRegs == 0) + return classifyArgumentType(Ty); + + Class C = classify(Ty); + if (C == Float) + return classifyArgumentType(Ty); + + unsigned SizeInRegs = (getContext().getTypeSize(Ty) + 31) / 32; + if (SizeInRegs == 0) + return classifyArgumentType(Ty); + + if (SizeInRegs > FreeRegs) { + FreeRegs = 0; + return classifyArgumentType(Ty); + } + assert(SizeInRegs >= 1 && SizeInRegs <= 3); + FreeRegs -= SizeInRegs; + + // If it is a simple scalar, keep the type so that we produce a cleaner IR. + ABIArgInfo Foo = classifyArgumentType(Ty); + if (Foo.isDirect() && !Foo.getDirectOffset() && !Foo.getPaddingType()) + return ABIArgInfo::getDirectInReg(Foo.getCoerceToType()); + if (Foo.isExtend()) + return ABIArgInfo::getExtendInReg(Foo.getCoerceToType()); + + llvm::LLVMContext &LLVMContext = getVMContext(); + llvm::Type *Int32 = llvm::Type::getInt32Ty(LLVMContext); + SmallVector<llvm::Type*, 3> Elements; + for (unsigned I = 0; I < SizeInRegs; ++I) + Elements.push_back(Int32); + llvm::Type *Result = llvm::StructType::get(LLVMContext, Elements); + return ABIArgInfo::getDirectInReg(Result); +} + ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const { // FIXME: Set alignment on indirect arguments. if (isAggregateTypeForABI(Ty)) { @@ -753,6 +813,28 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const { ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); } +void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const { + FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), + FI.getCallingConvention()); + + unsigned FreeRegs = FI.getHasRegParm() ? FI.getRegParm() : + DefaultNumRegisterParameters; + + // If the return value is indirect, then the hidden argument is consuming one + // integer register. + if (FI.getReturnInfo().isIndirect() && FreeRegs) { + --FreeRegs; + ABIArgInfo &Old = FI.getReturnInfo(); + Old = ABIArgInfo::getIndirectInReg(Old.getIndirectAlign(), + Old.getIndirectByVal(), + Old.getIndirectRealign()); + } + + for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); + it != ie; ++it) + it->info = classifyArgumentTypeWithReg(it->type, FreeRegs); +} + llvm::Value *X86_32ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, CodeGenFunction &CGF) const { llvm::Type *BPP = CGF.Int8PtrPtrTy; @@ -1345,7 +1427,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, // single eightbyte, each is classified separately. Each eightbyte gets // initialized to class NO_CLASS. Class FieldLo, FieldHi; - uint64_t Offset = OffsetBase + Layout.getBaseClassOffsetInBits(Base); + uint64_t Offset = + OffsetBase + getContext().toBits(Layout.getBaseClassOffset(Base)); classify(i->getType(), Offset, FieldLo, FieldHi); Lo = merge(Lo, FieldLo); Hi = merge(Hi, FieldHi); @@ -1584,7 +1667,7 @@ static bool BitsContainNoUserData(QualType Ty, unsigned StartBit, cast<CXXRecordDecl>(i->getType()->getAs<RecordType>()->getDecl()); // If the base is after the span we care about, ignore it. - unsigned BaseOffset = (unsigned)Layout.getBaseClassOffsetInBits(Base); + unsigned BaseOffset = Context.toBits(Layout.getBaseClassOffset(Base)); if (BaseOffset >= EndBit) continue; unsigned BaseStart = BaseOffset < StartBit ? StartBit-BaseOffset :0; @@ -2411,6 +2494,64 @@ PPC32TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, return false; } +// PowerPC-64 + +namespace { +class PPC64TargetCodeGenInfo : public DefaultTargetCodeGenInfo { +public: + PPC64TargetCodeGenInfo(CodeGenTypes &CGT) : DefaultTargetCodeGenInfo(CGT) {} + + int getDwarfEHStackPointer(CodeGen::CodeGenModule &M) const { + // This is recovered from gcc output. + return 1; // r1 is the dedicated stack pointer + } + + bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, + llvm::Value *Address) const; +}; + +} + +bool +PPC64TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, + llvm::Value *Address) const { + // This is calculated from the LLVM and GCC tables and verified + // against gcc output. AFAIK all ABIs use the same encoding. + + CodeGen::CGBuilderTy &Builder = CGF.Builder; + + llvm::IntegerType *i8 = CGF.Int8Ty; + llvm::Value *Four8 = llvm::ConstantInt::get(i8, 4); + llvm::Value *Eight8 = llvm::ConstantInt::get(i8, 8); + llvm::Value *Sixteen8 = llvm::ConstantInt::get(i8, 16); + + // 0-31: r0-31, the 8-byte general-purpose registers + AssignToArrayRange(Builder, Address, Eight8, 0, 31); + + // 32-63: fp0-31, the 8-byte floating-point registers + AssignToArrayRange(Builder, Address, Eight8, 32, 63); + + // 64-76 are various 4-byte special-purpose registers: + // 64: mq + // 65: lr + // 66: ctr + // 67: ap + // 68-75 cr0-7 + // 76: xer + AssignToArrayRange(Builder, Address, Four8, 64, 76); + + // 77-108: v0-31, the 16-byte vector registers + AssignToArrayRange(Builder, Address, Sixteen8, 77, 108); + + // 109: vrsave + // 110: vscr + // 111: spe_acc + // 112: spefscr + // 113: sfp + AssignToArrayRange(Builder, Address, Four8, 109, 113); + + return false; +} //===----------------------------------------------------------------------===// // ARM ABI Implementation @@ -2559,7 +2700,8 @@ static bool isHomogeneousAggregate(QualType Ty, const Type *&Base, // double, or 64-bit or 128-bit vectors. if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) { if (BT->getKind() != BuiltinType::Float && - BT->getKind() != BuiltinType::Double) + BT->getKind() != BuiltinType::Double && + BT->getKind() != BuiltinType::LongDouble) return false; } else if (const VectorType *VT = Ty->getAs<VectorType>()) { unsigned VecSize = Context.getTypeSize(VT); @@ -2615,19 +2757,23 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty) const { } } + // Support byval for ARM. + if (getContext().getTypeSizeInChars(Ty) > CharUnits::fromQuantity(64) || + getContext().getTypeAlign(Ty) > 64) { + return ABIArgInfo::getIndirect(0, /*ByVal=*/true); + } + // Otherwise, pass by coercing to a structure of the appropriate size. - // - // FIXME: This is kind of nasty... but there isn't much choice because the ARM - // backend doesn't support byval. - // FIXME: This doesn't handle alignment > 64 bits. llvm::Type* ElemTy; unsigned SizeRegs; - if (getContext().getTypeAlign(Ty) > 32) { - ElemTy = llvm::Type::getInt64Ty(getVMContext()); - SizeRegs = (getContext().getTypeSize(Ty) + 63) / 64; - } else { + // FIXME: Try to match the types of the arguments more accurately where + // we can. + if (getContext().getTypeAlign(Ty) <= 32) { ElemTy = llvm::Type::getInt32Ty(getVMContext()); SizeRegs = (getContext().getTypeSize(Ty) + 31) / 32; + } else { + ElemTy = llvm::Type::getInt64Ty(getVMContext()); + SizeRegs = (getContext().getTypeSize(Ty) + 63) / 64; } llvm::Type *STy = @@ -2833,14 +2979,14 @@ llvm::Value *ARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, } //===----------------------------------------------------------------------===// -// PTX ABI Implementation +// NVPTX ABI Implementation //===----------------------------------------------------------------------===// namespace { -class PTXABIInfo : public ABIInfo { +class NVPTXABIInfo : public ABIInfo { public: - PTXABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {} + NVPTXABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {} ABIArgInfo classifyReturnType(QualType RetTy) const; ABIArgInfo classifyArgumentType(QualType Ty) const; @@ -2850,16 +2996,16 @@ public: CodeGenFunction &CFG) const; }; -class PTXTargetCodeGenInfo : public TargetCodeGenInfo { +class NVPTXTargetCodeGenInfo : public TargetCodeGenInfo { public: - PTXTargetCodeGenInfo(CodeGenTypes &CGT) - : TargetCodeGenInfo(new PTXABIInfo(CGT)) {} + NVPTXTargetCodeGenInfo(CodeGenTypes &CGT) + : TargetCodeGenInfo(new NVPTXABIInfo(CGT)) {} virtual void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const; }; -ABIArgInfo PTXABIInfo::classifyReturnType(QualType RetTy) const { +ABIArgInfo NVPTXABIInfo::classifyReturnType(QualType RetTy) const { if (RetTy->isVoidType()) return ABIArgInfo::getIgnore(); if (isAggregateTypeForABI(RetTy)) @@ -2867,14 +3013,14 @@ ABIArgInfo PTXABIInfo::classifyReturnType(QualType RetTy) const { return ABIArgInfo::getDirect(); } -ABIArgInfo PTXABIInfo::classifyArgumentType(QualType Ty) const { +ABIArgInfo NVPTXABIInfo::classifyArgumentType(QualType Ty) const { if (isAggregateTypeForABI(Ty)) return ABIArgInfo::getIndirect(0); return ABIArgInfo::getDirect(); } -void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const { +void NVPTXABIInfo::computeInfo(CGFunctionInfo &FI) const { FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) @@ -2885,6 +3031,8 @@ void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const { return; // Calling convention as default by an ABI. + // We're still using the PTX_Kernel/PTX_Device calling conventions here, + // but we should switch to NVVM metadata later on. llvm::CallingConv::ID DefaultCC; const LangOptions &LangOpts = getContext().getLangOpts(); if (LangOpts.OpenCL || LangOpts.CUDA) { @@ -2903,14 +3051,14 @@ void PTXABIInfo::computeInfo(CGFunctionInfo &FI) const { } -llvm::Value *PTXABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, - CodeGenFunction &CFG) const { - llvm_unreachable("PTX does not support varargs"); +llvm::Value *NVPTXABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, + CodeGenFunction &CFG) const { + llvm_unreachable("NVPTX does not support varargs"); } -void PTXTargetCodeGenInfo::SetTargetAttributes(const Decl *D, - llvm::GlobalValue *GV, - CodeGen::CodeGenModule &M) const{ +void NVPTXTargetCodeGenInfo:: +SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &M) const{ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); if (!FD) return; @@ -3097,13 +3245,16 @@ void MSP430TargetCodeGenInfo::SetTargetAttributes(const Decl *D, namespace { class MipsABIInfo : public ABIInfo { bool IsO32; - unsigned MinABIStackAlignInBytes; - llvm::Type* HandleAggregates(QualType Ty) const; + unsigned MinABIStackAlignInBytes, StackAlignInBytes; + void CoerceToIntArgs(uint64_t TySize, + SmallVector<llvm::Type*, 8> &ArgList) const; + llvm::Type* HandleAggregates(QualType Ty, uint64_t TySize) const; llvm::Type* returnAggregateInRegs(QualType RetTy, uint64_t Size) const; llvm::Type* getPaddingType(uint64_t Align, uint64_t Offset) const; public: MipsABIInfo(CodeGenTypes &CGT, bool _IsO32) : - ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8) {} + ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8), + StackAlignInBytes(IsO32 ? 8 : 16) {} ABIArgInfo classifyReturnType(QualType RetTy) const; ABIArgInfo classifyArgumentType(QualType RetTy, uint64_t &Offset) const; @@ -3132,36 +3283,56 @@ public: }; } +void MipsABIInfo::CoerceToIntArgs(uint64_t TySize, + SmallVector<llvm::Type*, 8> &ArgList) const { + llvm::IntegerType *IntTy = + llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8); + + // Add (TySize / MinABIStackAlignInBytes) args of IntTy. + for (unsigned N = TySize / (MinABIStackAlignInBytes * 8); N; --N) + ArgList.push_back(IntTy); + + // If necessary, add one more integer type to ArgList. + unsigned R = TySize % (MinABIStackAlignInBytes * 8); + + if (R) + ArgList.push_back(llvm::IntegerType::get(getVMContext(), R)); +} + // In N32/64, an aligned double precision floating point field is passed in // a register. -llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty) const { - if (IsO32) - return 0; +llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty, uint64_t TySize) const { + SmallVector<llvm::Type*, 8> ArgList, IntArgList; + + if (IsO32) { + CoerceToIntArgs(TySize, ArgList); + return llvm::StructType::get(getVMContext(), ArgList); + } if (Ty->isComplexType()) return CGT.ConvertType(Ty); const RecordType *RT = Ty->getAs<RecordType>(); - // Unions are passed in integer registers. - if (!RT || !RT->isStructureOrClassType()) - return 0; + // Unions/vectors are passed in integer registers. + if (!RT || !RT->isStructureOrClassType()) { + CoerceToIntArgs(TySize, ArgList); + return llvm::StructType::get(getVMContext(), ArgList); + } const RecordDecl *RD = RT->getDecl(); const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); - uint64_t StructSize = getContext().getTypeSize(Ty); - assert(!(StructSize % 8) && "Size of structure must be multiple of 8."); + assert(!(TySize % 8) && "Size of structure must be multiple of 8."); uint64_t LastOffset = 0; unsigned idx = 0; llvm::IntegerType *I64 = llvm::IntegerType::get(getVMContext(), 64); - SmallVector<llvm::Type*, 8> ArgList; // Iterate over fields in the struct/class and check if there are any aligned // double fields. for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i, ++idx) { - const QualType Ty = (*i)->getType(); + const QualType Ty = i->getType(); const BuiltinType *BT = Ty->getAs<BuiltinType>(); if (!BT || BT->getKind() != BuiltinType::Double) @@ -3180,43 +3351,33 @@ llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty) const { LastOffset = Offset + 64; } - // This struct/class doesn't have an aligned double field. - if (!LastOffset) - return 0; - - // Add ((StructSize - LastOffset) / 64) args of type i64. - for (unsigned N = (StructSize - LastOffset) / 64; N; --N) - ArgList.push_back(I64); - - // If the size of the remainder is not zero, add one more integer type to - // ArgList. - unsigned R = (StructSize - LastOffset) % 64; - if (R) - ArgList.push_back(llvm::IntegerType::get(getVMContext(), R)); + CoerceToIntArgs(TySize - LastOffset, IntArgList); + ArgList.append(IntArgList.begin(), IntArgList.end()); return llvm::StructType::get(getVMContext(), ArgList); } llvm::Type *MipsABIInfo::getPaddingType(uint64_t Align, uint64_t Offset) const { - // Padding is inserted only for N32/64. - if (IsO32) - return 0; + assert((Offset % MinABIStackAlignInBytes) == 0); + + if ((Align - 1) & Offset) + return llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8); - assert(Align <= 16 && "Alignment larger than 16 not handled."); - return (Align == 16 && Offset & 0xf) ? - llvm::IntegerType::get(getVMContext(), 64) : 0; + return 0; } ABIArgInfo MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { uint64_t OrigOffset = Offset; - uint64_t TySize = - llvm::RoundUpToAlignment(getContext().getTypeSize(Ty), 64) / 8; + uint64_t TySize = getContext().getTypeSize(Ty); uint64_t Align = getContext().getTypeAlign(Ty) / 8; - Offset = llvm::RoundUpToAlignment(Offset, std::max(Align, (uint64_t)8)); - Offset += TySize; - if (isAggregateTypeForABI(Ty)) { + Align = std::min(std::max(Align, (uint64_t)MinABIStackAlignInBytes), + (uint64_t)StackAlignInBytes); + Offset = llvm::RoundUpToAlignment(Offset, Align); + Offset += llvm::RoundUpToAlignment(TySize, Align * 8) / 8; + + if (isAggregateTypeForABI(Ty) || Ty->isVectorType()) { // Ignore empty aggregates. if (TySize == 0) return ABIArgInfo::getIgnore(); @@ -3224,20 +3385,15 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { // Records with non trivial destructors/constructors should not be passed // by value. if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) { - Offset = OrigOffset + 8; + Offset = OrigOffset + MinABIStackAlignInBytes; return ABIArgInfo::getIndirect(0, /*ByVal=*/false); } - // If we have reached here, aggregates are passed either indirectly via a - // byval pointer or directly by coercing to another structure type. In the - // latter case, padding is inserted if the offset of the aggregate is - // unaligned. - llvm::Type *ResType = HandleAggregates(Ty); - - if (!ResType) - return ABIArgInfo::getIndirect(0); - - return ABIArgInfo::getDirect(ResType, 0, getPaddingType(Align, OrigOffset)); + // If we have reached here, aggregates are passed directly by coercing to + // another structure type. Padding is inserted if the offset of the + // aggregate is unaligned. + return ABIArgInfo::getDirect(HandleAggregates(Ty, TySize), 0, + getPaddingType(Align, OrigOffset)); } // Treat an enum type as its underlying type. @@ -3253,7 +3409,7 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { llvm::Type* MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const { const RecordType *RT = RetTy->getAs<RecordType>(); - SmallVector<llvm::Type*, 2> RTList; + SmallVector<llvm::Type*, 8> RTList; if (RT && RT->isStructureOrClassType()) { const RecordDecl *RD = RT->getDecl(); @@ -3272,12 +3428,12 @@ MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const { if (FieldCnt && (FieldCnt <= 2) && !Layout.getFieldOffset(0)) { RecordDecl::field_iterator b = RD->field_begin(), e = RD->field_end(); for (; b != e; ++b) { - const BuiltinType *BT = (*b)->getType()->getAs<BuiltinType>(); + const BuiltinType *BT = b->getType()->getAs<BuiltinType>(); if (!BT || !BT->isFloatingPoint()) break; - RTList.push_back(CGT.ConvertType((*b)->getType())); + RTList.push_back(CGT.ConvertType(b->getType())); } if (b == e) @@ -3288,11 +3444,7 @@ MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const { } } - RTList.push_back(llvm::IntegerType::get(getVMContext(), - std::min(Size, (uint64_t)64))); - if (Size > 64) - RTList.push_back(llvm::IntegerType::get(getVMContext(), Size - 64)); - + CoerceToIntArgs(Size, RTList); return llvm::StructType::get(getVMContext(), RTList); } @@ -3302,11 +3454,15 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const { if (RetTy->isVoidType() || Size == 0) return ABIArgInfo::getIgnore(); - if (isAggregateTypeForABI(RetTy)) { + if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) { if (Size <= 128) { if (RetTy->isAnyComplexType()) return ABIArgInfo::getDirect(); + // O32 returns integer vectors in registers. + if (IsO32 && RetTy->isVectorType() && !RetTy->hasFloatingRepresentation()) + return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); + if (!IsO32 && !isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy)) return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); } @@ -3327,7 +3483,7 @@ void MipsABIInfo::computeInfo(CGFunctionInfo &FI) const { RetInfo = classifyReturnType(FI.getReturnType()); // Check if a pointer to an aggregate is passed as a hidden argument. - uint64_t Offset = RetInfo.isIndirect() ? 8 : 0; + uint64_t Offset = RetInfo.isIndirect() ? MinABIStackAlignInBytes : 0; for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) @@ -3634,10 +3790,12 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::ppc: return *(TheTargetCodeGenInfo = new PPC32TargetCodeGenInfo(Types)); + case llvm::Triple::ppc64: + return *(TheTargetCodeGenInfo = new PPC64TargetCodeGenInfo(Types)); - case llvm::Triple::ptx32: - case llvm::Triple::ptx64: - return *(TheTargetCodeGenInfo = new PTXTargetCodeGenInfo(Types)); + case llvm::Triple::nvptx: + case llvm::Triple::nvptx64: + return *(TheTargetCodeGenInfo = new NVPTXTargetCodeGenInfo(Types)); case llvm::Triple::mblaze: return *(TheTargetCodeGenInfo = new MBlazeTargetCodeGenInfo(Types)); @@ -3653,8 +3811,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { if (Triple.isOSDarwin()) return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, true, true, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, true, true, DisableMMX, false, + CodeGenOpts.NumRegisterParameters)); switch (Triple.getOS()) { case llvm::Triple::Cygwin: @@ -3663,19 +3821,22 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::DragonFly: case llvm::Triple::FreeBSD: case llvm::Triple::OpenBSD: + case llvm::Triple::Bitrig: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, true, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX, + false, + CodeGenOpts.NumRegisterParameters)); case llvm::Triple::Win32: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, true, DisableMMX, true)); + new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX, true, + CodeGenOpts.NumRegisterParameters)); default: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, false, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, false, false, DisableMMX, + false, + CodeGenOpts.NumRegisterParameters)); } } diff --git a/lib/Driver/ArgList.cpp b/lib/Driver/ArgList.cpp index 55a0ddfd272a..7fd439e63082 100644 --- a/lib/Driver/ArgList.cpp +++ b/lib/Driver/ArgList.cpp @@ -140,6 +140,68 @@ Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1, return Res; } +Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1, + OptSpecifier Id2, OptSpecifier Id3, + OptSpecifier Id4, OptSpecifier Id5) const { + Arg *Res = 0; + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + if ((*it)->getOption().matches(Id0) || + (*it)->getOption().matches(Id1) || + (*it)->getOption().matches(Id2) || + (*it)->getOption().matches(Id3) || + (*it)->getOption().matches(Id4) || + (*it)->getOption().matches(Id5)) { + Res = *it; + Res->claim(); + } + } + + return Res; +} + +Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1, + OptSpecifier Id2, OptSpecifier Id3, + OptSpecifier Id4, OptSpecifier Id5, + OptSpecifier Id6) const { + Arg *Res = 0; + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + if ((*it)->getOption().matches(Id0) || + (*it)->getOption().matches(Id1) || + (*it)->getOption().matches(Id2) || + (*it)->getOption().matches(Id3) || + (*it)->getOption().matches(Id4) || + (*it)->getOption().matches(Id5) || + (*it)->getOption().matches(Id6)) { + Res = *it; + Res->claim(); + } + } + + return Res; +} + +Arg *ArgList::getLastArg(OptSpecifier Id0, OptSpecifier Id1, + OptSpecifier Id2, OptSpecifier Id3, + OptSpecifier Id4, OptSpecifier Id5, + OptSpecifier Id6, OptSpecifier Id7) const { + Arg *Res = 0; + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + if ((*it)->getOption().matches(Id0) || + (*it)->getOption().matches(Id1) || + (*it)->getOption().matches(Id2) || + (*it)->getOption().matches(Id3) || + (*it)->getOption().matches(Id4) || + (*it)->getOption().matches(Id5) || + (*it)->getOption().matches(Id6) || + (*it)->getOption().matches(Id7)) { + Res = *it; + Res->claim(); + } + } + + return Res; +} + bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default) const { if (Arg *A = getLastArg(Pos, Neg)) return A->getOption().matches(Pos); diff --git a/lib/Driver/CC1Options.cpp b/lib/Driver/CC1Options.cpp deleted file mode 100644 index 884b363f6373..000000000000 --- a/lib/Driver/CC1Options.cpp +++ /dev/null @@ -1,38 +0,0 @@ -//===--- CC1Options.cpp - Clang CC1 Options Table -------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Driver/CC1Options.h" -#include "clang/Driver/Option.h" -#include "clang/Driver/OptTable.h" -using namespace clang; -using namespace clang::driver; -using namespace clang::driver::options; -using namespace clang::driver::cc1options; - -static const OptTable::Info CC1InfoTable[] = { -#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { NAME, HELPTEXT, METAVAR, Option::KIND##Class, PARAM, FLAGS, \ - OPT_##GROUP, OPT_##ALIAS }, -#include "clang/Driver/CC1Options.inc" -}; - -namespace { - -class CC1OptTable : public OptTable { -public: - CC1OptTable() - : OptTable(CC1InfoTable, sizeof(CC1InfoTable) / sizeof(CC1InfoTable[0])) {} -}; - -} - -OptTable *clang::driver::createCC1OptTable() { - return new CC1OptTable(); -} diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index a798e207503b..4ada7d92c3d1 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -1,10 +1,7 @@ -set(LLVM_USED_LIBS clangBasic clangAST clangParse) - add_clang_library(clangDriver Action.cpp Arg.cpp ArgList.cpp - CC1Options.cpp CC1AsOptions.cpp Compilation.cpp Driver.cpp @@ -21,13 +18,14 @@ add_clang_library(clangDriver Types.cpp ) -IF(MSVC) - get_target_property(NON_ANSI_COMPILE_FLAGS clangDriver COMPILE_FLAGS) - string(REPLACE /Za - "" NON_ANSI_COMPILE_FLAGS - ${NON_ANSI_COMPILE_FLAGS}) - set_target_properties(clangDriver PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) -ENDIF(MSVC) +add_dependencies(clangDriver + ClangAttrList + ClangCC1AsOptions + ClangDiagnosticCommon + ClangDiagnosticDriver + ClangDriverOptions + ) -add_dependencies(clangDriver ClangAttrList ClangDiagnosticDriver - ClangDriverOptions ClangCC1Options ClangCC1AsOptions) +target_link_libraries(clangDriver + clangBasic + ) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index 5553fc937e01..c962fca0bf9a 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -219,7 +219,7 @@ void Compilation::initCompilationForDiagnostics(void) { // to avoid emitting warnings about unused args. OptSpecifier OutputOpts[] = { options::OPT_o, options::OPT_MD, options::OPT_MMD }; - for (unsigned i = 0; i != sizeof(OutputOpts)/sizeof(OutputOpts[0]); ++i) { + for (unsigned i = 0, e = llvm::array_lengthof(OutputOpts); i != e; ++i) { if (TranslatedArgs->hasArg(OutputOpts[i])) TranslatedArgs->eraseArg(OutputOpts[i]); } diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 3ddac69e98cb..87d533dd2953 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -59,7 +59,7 @@ Driver::Driver(StringRef ClangExecutable, CCPrintOptions(false), CCPrintHeaders(false), CCLogDiagnostics(false), CCGenDiagnostics(false), CCCGenericGCCName(""), CheckInputsExist(true), CCCUseClang(true), CCCUseClangCXX(true), CCCUseClangCPP(true), - CCCUsePCH(true), SuppressMissingInputWarning(false) { + ForcedClangUse(false), CCCUsePCH(true), SuppressMissingInputWarning(false) { if (IsProduction) { // In a "production" build, only use clang on architectures we expect to // work. @@ -115,9 +115,10 @@ InputArgList *Driver::ParseArgStrings(ArrayRef<const char *> ArgList) { } // Warn about -mcpu= without an argument. - if (A->getOption().matches(options::OPT_mcpu_EQ) && + if (A->getOption().matches(options::OPT_mcpu_EQ) && A->containsValue("")) { - Diag(clang::diag::warn_drv_empty_joined_argument) << A->getAsString(*Args); + Diag(clang::diag::warn_drv_empty_joined_argument) << + A->getAsString(*Args); } } @@ -253,7 +254,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { if (char *env = ::getenv("COMPILER_PATH")) { StringRef CompilerPath = env; while (!CompilerPath.empty()) { - std::pair<StringRef, StringRef> Split = CompilerPath.split(':'); + std::pair<StringRef, StringRef> Split = CompilerPath.split(':'); PrefixDirs.push_back(Split.first); CompilerPath = Split.second; } @@ -376,24 +377,33 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { void Driver::generateCompilationDiagnostics(Compilation &C, const Command *FailingCommand) { if (C.getArgs().hasArg(options::OPT_fno_crash_diagnostics)) - return; + return; // Don't try to generate diagnostics for link jobs. - if (FailingCommand->getCreator().isLinkJob()) + if (FailingCommand && FailingCommand->getCreator().isLinkJob()) return; + // Print the version of the compiler. + PrintVersion(C, llvm::errs()); + Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Please submit a bug report to " BUG_REPORT_URL " and include command" - " line arguments and all diagnostic information."; + << "PLEASE submit a bug report to " BUG_REPORT_URL " and include the " + "crash backtrace, preprocessed source, and associated run script."; // Suppress driver output and emit preprocessor output to temp file. CCCIsCPP = true; CCGenDiagnostics = true; + C.getArgs().AddFlagArg(0, Opts->getOption(options::OPT_frewrite_includes)); // Save the original job command(s). std::string Cmd; llvm::raw_string_ostream OS(Cmd); - C.PrintJob(OS, C.getJobs(), "\n", false); + if (FailingCommand) + C.PrintJob(OS, *FailingCommand, "\n", false); + else + // Crash triggered by FORCE_CLANG_DIAGNOSTICS_CRASH, which doesn't have an + // associated FailingCommand, so just pass all jobs. + C.PrintJob(OS, C.getJobs(), "\n", false); OS.flush(); // Clear stale state and suppress tool output. @@ -473,7 +483,9 @@ void Driver::generateCompilationDiagnostics(Compilation &C, // If the command succeeded, we are done. if (Res == 0) { Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Preprocessed source(s) and associated run script(s) are located at:"; + << "\n********************\n\n" + "PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:\n" + "Preprocessed source(s) and associated run script(s) are located at:"; ArgStringList Files = C.getTempFiles(); for (ArgStringList::const_iterator it = Files.begin(), ie = Files.end(); it != ie; ++it) { @@ -489,10 +501,76 @@ void Driver::generateCompilationDiagnostics(Compilation &C, Diag(clang::diag::note_drv_command_failed_diag_msg) << "Error generating run script: " + Script + " " + Err; } else { + // Strip away options not necessary to reproduce the crash. + // FIXME: This doesn't work with quotes (e.g., -D "foo bar"). + SmallVector<std::string, 16> Flag; + Flag.push_back("-D "); + Flag.push_back("-F"); + Flag.push_back("-I "); + Flag.push_back("-M "); + Flag.push_back("-MD "); + Flag.push_back("-MF "); + Flag.push_back("-MG "); + Flag.push_back("-MM "); + Flag.push_back("-MMD "); + Flag.push_back("-MP "); + Flag.push_back("-MQ "); + Flag.push_back("-MT "); + Flag.push_back("-o "); + Flag.push_back("-coverage-file "); + Flag.push_back("-dependency-file "); + Flag.push_back("-fdebug-compilation-dir "); + Flag.push_back("-fmodule-cache-path "); + Flag.push_back("-idirafter "); + Flag.push_back("-include "); + Flag.push_back("-include-pch "); + Flag.push_back("-internal-isystem "); + Flag.push_back("-internal-externc-isystem "); + Flag.push_back("-iprefix "); + Flag.push_back("-iwithprefix "); + Flag.push_back("-iwithprefixbefore "); + Flag.push_back("-isysroot "); + Flag.push_back("-isystem "); + Flag.push_back("-iquote "); + Flag.push_back("-resource-dir "); + Flag.push_back("-serialize-diagnostic-file "); + for (unsigned i = 0, e = Flag.size(); i < e; ++i) { + size_t I = 0, E = 0; + do { + I = Cmd.find(Flag[i], I); + if (I == std::string::npos) break; + + E = Cmd.find(" ", I + Flag[i].length()); + if (E == std::string::npos) break; + // The -D option is not removed. Instead, the argument is quoted. + if (Flag[i] != "-D ") { + Cmd.erase(I, E - I + 1); + } else { + Cmd.insert(I+3, "\""); + Cmd.insert(++E, "\""); + I = E; + } + } while(1); + } + // Append the new filename with correct preprocessed suffix. + size_t I, E; + I = Cmd.find("-main-file-name "); + assert (I != std::string::npos && "Expected to find -main-file-name"); + I += 16; + E = Cmd.find(" ", I); + assert (E != std::string::npos && "-main-file-name missing argument?"); + StringRef OldFilename = StringRef(Cmd).slice(I, E); + StringRef NewFilename = llvm::sys::path::filename(*it); + I = StringRef(Cmd).rfind(OldFilename); + E = I + OldFilename.size(); + I = Cmd.rfind(" ", I) + 1; + Cmd.replace(I, E - I, NewFilename.data(), NewFilename.size()); ScriptOS << Cmd; Diag(clang::diag::note_drv_command_failed_diag_msg) << Script; } } + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "\n\n********************"; } else { // Failure, remove preprocessed files. if (!C.getArgs().hasArg(options::OPT_save_temps)) @@ -529,14 +607,8 @@ int Driver::ExecuteCompilation(const Compilation &C, C.CleanupFileList(C.getResultFiles(), true); // Failure result files are valid unless we crashed. - if (Res < 0) { + if (Res < 0) C.CleanupFileList(C.getFailureResultFiles(), true); -#ifdef _WIN32 - // Exit status should not be negative on Win32, - // unless abnormal termination. - Res = 1; -#endif - } } // Print extra information about abnormal failures, if possible. @@ -630,7 +702,7 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { return false; } - if (C.getArgs().hasArg(options::OPT__help) || + if (C.getArgs().hasArg(options::OPT_help) || C.getArgs().hasArg(options::OPT__help_hidden)) { PrintHelp(C.getArgs().hasArg(options::OPT__help_hidden)); return false; @@ -748,8 +820,7 @@ static unsigned PrintActions1(const Compilation &C, Action *A, if (InputAction *IA = dyn_cast<InputAction>(A)) { os << "\"" << IA->getInputArg().getValue(C.getArgs()) << "\""; } else if (BindArchAction *BIA = dyn_cast<BindArchAction>(A)) { - os << '"' << (BIA->getArchName() ? BIA->getArchName() : - C.getDefaultToolChain().getArchName()) << '"' + os << '"' << BIA->getArchName() << '"' << ", {" << PrintActions1(C, *BIA->begin(), Ids) << "}"; } else { os << "{"; @@ -823,7 +894,7 @@ void Driver::BuildUniversalActions(const ToolChain &TC, // When there is no explicit arch for this platform, make sure we still bind // the architecture (to the default) so that -Xarch_ is handled correctly. if (!Archs.size()) - Archs.push_back(0); + Archs.push_back(Args.MakeArgString(TC.getArchName())); // FIXME: We killed off some others but these aren't yet detected in a // functional manner. If we added information to jobs about which "auxiliary" @@ -873,7 +944,7 @@ void Driver::BuildUniversalActions(const ToolChain &TC, if (A && !A->getOption().matches(options::OPT_g0) && !A->getOption().matches(options::OPT_gstabs) && ContainsCompileOrAssembleAction(Actions.back())) { - + // Add a 'dsymutil' step if necessary, when debug info is enabled and we // have a compile input. We need to run 'dsymutil' ourselves in such cases // because the debug info will refer to a temporary object file which is @@ -1060,18 +1131,27 @@ void Driver::BuildActions(const ToolChain &TC, const DerivedArgList &Args, if (Args.hasArg(options::OPT_Qunused_arguments)) continue; + // Special case when final phase determined by binary name, rather than + // by a command-line argument with a corresponding Arg. + if (CCCIsCPP) + Diag(clang::diag::warn_drv_input_file_unused_by_cpp) + << InputArg->getAsString(Args) + << getPhaseName(InitialPhase); // Special case '-E' warning on a previously preprocessed file to make // more sense. - if (InitialPhase == phases::Compile && FinalPhase == phases::Preprocess && - getPreprocessedType(InputType) == types::TY_INVALID) + else if (InitialPhase == phases::Compile && + FinalPhase == phases::Preprocess && + getPreprocessedType(InputType) == types::TY_INVALID) Diag(clang::diag::warn_drv_preprocessed_input_file_unused) << InputArg->getAsString(Args) - << FinalPhaseArg->getOption().getName(); + << !!FinalPhaseArg + << FinalPhaseArg ? FinalPhaseArg->getOption().getName() : ""; else Diag(clang::diag::warn_drv_input_file_unused) << InputArg->getAsString(Args) << getPhaseName(InitialPhase) - << FinalPhaseArg->getOption().getName(); + << !!FinalPhaseArg + << FinalPhaseArg ? FinalPhaseArg->getOption().getName() : ""; continue; } @@ -1130,14 +1210,23 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, if (Args.hasArg(options::OPT_M, options::OPT_MM)) { OutputTy = types::TY_Dependencies; } else { - OutputTy = types::getPreprocessedType(Input->getType()); + OutputTy = Input->getType(); + if (!Args.hasFlag(options::OPT_frewrite_includes, + options::OPT_fno_rewrite_includes, false)) + OutputTy = types::getPreprocessedType(OutputTy); assert(OutputTy != types::TY_INVALID && "Cannot preprocess this input type!"); } return new PreprocessJobAction(Input, OutputTy); } - case phases::Precompile: - return new PrecompileJobAction(Input, types::TY_PCH); + case phases::Precompile: { + types::ID OutputTy = types::TY_PCH; + if (Args.hasArg(options::OPT_fsyntax_only)) { + // Syntax checks should not emit a PCH file + OutputTy = types::TY_Nothing; + } + return new PrecompileJobAction(Input, OutputTy); + } case phases::Compile: { if (Args.hasArg(options::OPT_fsyntax_only)) { return new CompileJobAction(Input, types::TY_Nothing); @@ -1332,10 +1421,13 @@ void Driver::BuildJobsForAction(Compilation &C, } if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) { - const ToolChain *TC = &C.getDefaultToolChain(); + const ToolChain *TC; + const char *ArchName = BAA->getArchName(); - if (BAA->getArchName()) - TC = &getToolChain(C.getArgs(), BAA->getArchName()); + if (ArchName) + TC = &getToolChain(C.getArgs(), ArchName); + else + TC = &C.getDefaultToolChain(); BuildJobsForAction(C, *BAA->begin(), TC, BAA->getArchName(), AtTopLevel, LinkingOutput, Result); @@ -1453,15 +1545,24 @@ const char *Driver::GetNamedOutputPath(Compilation &C, NamedOutput = C.getArgs().MakeArgString(Suffixed.c_str()); } - // If we're saving temps and the temp filename conflicts with the input - // filename, then avoid overwriting input file. + // If we're saving temps and the temp file conflicts with the input file, + // then avoid overwriting input file. if (!AtTopLevel && C.getArgs().hasArg(options::OPT_save_temps) && NamedOutput == BaseName) { - StringRef Name = llvm::sys::path::filename(BaseInput); - std::pair<StringRef, StringRef> Split = Name.split('.'); - std::string TmpName = - GetTemporaryPath(Split.first, types::getTypeTempSuffix(JA.getType())); - return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str())); + + bool SameFile = false; + SmallString<256> Result; + llvm::sys::fs::current_path(Result); + llvm::sys::path::append(Result, BaseName); + llvm::sys::fs::equivalent(BaseInput, Result.c_str(), SameFile); + // Must share the same path to conflict. + if (SameFile) { + StringRef Name = llvm::sys::path::filename(BaseInput); + std::pair<StringRef, StringRef> Split = Name.split('.'); + std::string TmpName = + GetTemporaryPath(Split.first, types::getTypeTempSuffix(JA.getType())); + return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str())); + } } // As an annoying special case, PCH generation doesn't strip the pathname. @@ -1564,7 +1665,7 @@ std::string Driver::GetProgramPath(const char *Name, const ToolChain &TC, return Name; } -std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix) +std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix) const { // FIXME: This is lame; sys::Path should provide this function (in particular, // it should know how to find the temporary files dir). @@ -1579,14 +1680,15 @@ std::string Driver::GetTemporaryPath(StringRef Prefix, const char *Suffix) llvm::sys::Path P(TmpDir); P.appendComponent(Prefix); if (P.makeUnique(false, &Error)) { - Diag(clang::diag::err_drv_unable_to_make_temp) << Error; + Diag(clang::diag::err_unable_to_make_temp) << Error; return ""; } // FIXME: Grumble, makeUnique sometimes leaves the file around!? PR3837. P.eraseFromDisk(false, 0); - P.appendSuffix(Suffix); + if (Suffix) + P.appendSuffix(Suffix); return P.str(); } @@ -1674,6 +1776,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::OpenBSD: TC = new toolchains::OpenBSD(*this, Target, Args); break; + case llvm::Triple::Bitrig: + TC = new toolchains::Bitrig(*this, Target, Args); + break; case llvm::Triple::NetBSD: TC = new toolchains::NetBSD(*this, Target, Args); break; diff --git a/lib/Driver/OptTable.cpp b/lib/Driver/OptTable.cpp index 4f5390b6e26b..a3e38b26f20f 100644 --- a/lib/Driver/OptTable.cpp +++ b/lib/Driver/OptTable.cpp @@ -181,6 +181,8 @@ Option *OptTable::CreateOption(unsigned id) const { } if (info.Flags & Unsupported) Opt->setUnsupported(true); + if (info.Flags & CC1Option) + Opt->setIsCC1Option(true); return Opt; } diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index db4d2a8a47b5..48ed044c8f5c 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -14,10 +14,10 @@ #include "clang/Driver/ArgList.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/Options.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" +#include "clang/Basic/ObjCRuntime.h" using namespace clang::driver; using namespace clang; @@ -49,25 +49,9 @@ bool ToolChain::HasNativeLLVMSupport() const { return false; } -void ToolChain::configureObjCRuntime(ObjCRuntime &runtime) const { - switch (runtime.getKind()) { - case ObjCRuntime::NeXT: - // Assume a minimal NeXT runtime. - runtime.HasARC = false; - runtime.HasWeak = false; - runtime.HasSubscripting = false; - runtime.HasTerminate = false; - return; - - case ObjCRuntime::GNU: - // Assume a maximal GNU runtime. - runtime.HasARC = true; - runtime.HasWeak = true; - runtime.HasSubscripting = false; // to be added - runtime.HasTerminate = false; // to be added - return; - } - llvm_unreachable("invalid runtime kind!"); +ObjCRuntime ToolChain::getDefaultObjCRuntime(bool isNonFragile) const { + return ObjCRuntime(isNonFragile ? ObjCRuntime::GNUstep : ObjCRuntime::GCC, + VersionTuple()); } /// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting. @@ -189,6 +173,9 @@ void ToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, // Each toolchain should provide the appropriate include flags. } +void ToolChain::addClangTargetOptions(ArgStringList &CC1Args) const { +} + ToolChain::RuntimeLibType ToolChain::GetRuntimeLibType( const ArgList &Args) const { diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 7f9ed9a75305..01c66239a121 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -14,10 +14,10 @@ #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/OptTable.h" #include "clang/Driver/Option.h" #include "clang/Driver/Options.h" +#include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Version.h" #include "llvm/ADT/SmallString.h" @@ -42,9 +42,7 @@ using namespace clang; /// Darwin - Darwin tool chain for i386 and x86_64. Darwin::Darwin(const Driver &D, const llvm::Triple& Triple) - : ToolChain(D, Triple), TargetInitialized(false), - ARCRuntimeForSimulator(ARCSimulator_None), - LibCXXForSimulator(LibCXXSimulator_None) + : ToolChain(D, Triple), TargetInitialized(false) { // Compute the initial Darwin version from the triple unsigned Major, Minor, Micro; @@ -59,6 +57,11 @@ Darwin::Darwin(const Driver &D, const llvm::Triple& Triple) DarwinVersion[0] = Minor + 4; DarwinVersion[1] = Micro; DarwinVersion[2] = 0; + + // Compute the initial iOS version from the triple + Triple.getiOSVersion(Major, Minor, Micro); + llvm::raw_string_ostream(iOSVersionMin) + << Major << '.' << Minor << '.' << Micro; } types::ID Darwin::LookupTypeForExtension(const char *Ext) const { @@ -75,42 +78,19 @@ bool Darwin::HasNativeLLVMSupport() const { return true; } -bool Darwin::hasARCRuntime() const { - // FIXME: Remove this once there is a proper way to detect an ARC runtime - // for the simulator. - switch (ARCRuntimeForSimulator) { - case ARCSimulator_None: - break; - case ARCSimulator_HasARCRuntime: - return true; - case ARCSimulator_NoARCRuntime: - return false; - } - - if (isTargetIPhoneOS()) - return !isIPhoneOSVersionLT(5); - else - return !isMacosxVersionLT(10, 7); -} - -bool Darwin::hasSubscriptingRuntime() const { - return !isTargetIPhoneOS() && !isMacosxVersionLT(10, 8); -} - /// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0. -void Darwin::configureObjCRuntime(ObjCRuntime &runtime) const { - if (runtime.getKind() != ObjCRuntime::NeXT) - return ToolChain::configureObjCRuntime(runtime); - - runtime.HasARC = runtime.HasWeak = hasARCRuntime(); - runtime.HasSubscripting = hasSubscriptingRuntime(); - - // So far, objc_terminate is only available in iOS 5. - // FIXME: do the simulator logic properly. - if (!ARCRuntimeForSimulator && isTargetIPhoneOS()) - runtime.HasTerminate = !isIPhoneOSVersionLT(5); - else - runtime.HasTerminate = false; +ObjCRuntime Darwin::getDefaultObjCRuntime(bool isNonFragile) const { + if (isTargetIPhoneOS()) { + return ObjCRuntime(ObjCRuntime::iOS, TargetVersion); + } else if (TargetSimulatorVersionFromDefines != VersionTuple()) { + return ObjCRuntime(ObjCRuntime::iOS, TargetSimulatorVersionFromDefines); + } else { + if (isNonFragile) { + return ObjCRuntime(ObjCRuntime::MacOSX, TargetVersion); + } else { + return ObjCRuntime(ObjCRuntime::FragileMacOSX, TargetVersion); + } + } } /// Darwin provides a blocks runtime starting in MacOS X 10.6 and iOS 3.2. @@ -194,21 +174,25 @@ void Generic_ELF::anchor() {} Tool &Darwin::SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const { - Action::ActionClass Key; + Action::ActionClass Key = JA.getKind(); + bool useClang = false; if (getDriver().ShouldUseClangCompiler(C, JA, getTriple())) { + useClang = true; // Fallback to llvm-gcc for i386 kext compiles, we don't support that ABI. - if (Inputs.size() == 1 && + if (!getDriver().shouldForceClangUse() && + Inputs.size() == 1 && types::isCXX(Inputs[0]->getType()) && getTriple().isOSDarwin() && getTriple().getArch() == llvm::Triple::x86 && (C.getArgs().getLastArg(options::OPT_fapple_kext) || C.getArgs().getLastArg(options::OPT_mkernel))) - Key = JA.getKind(); - else - Key = Action::AnalyzeJobClass; - } else - Key = JA.getKind(); + useClang = false; + } + + // FIXME: This seems like a hacky way to choose clang frontend. + if (useClang) + Key = Action::AnalyzeJobClass; bool UseIntegratedAs = C.getArgs().hasFlag(options::OPT_integrated_as, options::OPT_no_integrated_as, @@ -287,76 +271,6 @@ void DarwinClang::AddGCCLibexecPath(unsigned darwinVersion) { getProgramPaths().push_back(Path); } -void DarwinClang::AddLinkSearchPathArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { - // The Clang toolchain uses explicit paths for internal libraries. - - // Unfortunately, we still might depend on a few of the libraries that are - // only available in the gcc library directory (in particular - // libstdc++.dylib). For now, hardcode the path to the known install location. - // FIXME: This should get ripped out someday. However, when building on - // 10.6 (darwin10), we're still relying on this to find libstdc++.dylib. - llvm::sys::Path P(getDriver().Dir); - P.eraseComponent(); // .../usr/bin -> ../usr - P.appendComponent("llvm-gcc-4.2"); - P.appendComponent("lib"); - P.appendComponent("gcc"); - switch (getTriple().getArch()) { - default: - llvm_unreachable("Invalid Darwin arch!"); - case llvm::Triple::x86: - case llvm::Triple::x86_64: - P.appendComponent("i686-apple-darwin10"); - break; - case llvm::Triple::arm: - case llvm::Triple::thumb: - P.appendComponent("arm-apple-darwin10"); - break; - case llvm::Triple::ppc: - case llvm::Triple::ppc64: - P.appendComponent("powerpc-apple-darwin10"); - break; - } - P.appendComponent("4.2.1"); - - // Determine the arch specific GCC subdirectory. - const char *ArchSpecificDir = 0; - switch (getTriple().getArch()) { - default: - break; - case llvm::Triple::arm: - case llvm::Triple::thumb: { - std::string Triple = ComputeLLVMTriple(Args); - StringRef TripleStr = Triple; - if (TripleStr.startswith("armv5") || TripleStr.startswith("thumbv5")) - ArchSpecificDir = "v5"; - else if (TripleStr.startswith("armv6") || TripleStr.startswith("thumbv6")) - ArchSpecificDir = "v6"; - else if (TripleStr.startswith("armv7") || TripleStr.startswith("thumbv7")) - ArchSpecificDir = "v7"; - break; - } - case llvm::Triple::ppc64: - ArchSpecificDir = "ppc64"; - break; - case llvm::Triple::x86_64: - ArchSpecificDir = "x86_64"; - break; - } - - if (ArchSpecificDir) { - P.appendComponent(ArchSpecificDir); - bool Exists; - if (!llvm::sys::fs::exists(P.str(), Exists) && Exists) - CmdArgs.push_back(Args.MakeArgString("-L" + P.str())); - P.eraseComponent(); - } - - bool Exists; - if (!llvm::sys::fs::exists(P.str(), Exists) && Exists) - CmdArgs.push_back(Args.MakeArgString("-L" + P.str())); -} - void DarwinClang::AddLinkARCArgs(const ArgList &Args, ArgStringList &CmdArgs) const { @@ -374,7 +288,7 @@ void DarwinClang::AddLinkARCArgs(const ArgList &Args, else if (isTargetIPhoneOS()) s += "iphoneos"; // FIXME: Remove this once we depend fully on -mios-simulator-version-min. - else if (ARCRuntimeForSimulator != ARCSimulator_None) + else if (TargetSimulatorVersionFromDefines != VersionTuple()) s += "iphonesimulator"; else s += "macosx"; @@ -545,11 +459,13 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { unsigned Major = 0, Minor = 0, Micro = 0; if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && Major < 10 && Minor < 100 && Micro < 100) { - ARCRuntimeForSimulator = Major < 5 ? ARCSimulator_NoARCRuntime - : ARCSimulator_HasARCRuntime; - LibCXXForSimulator = Major < 5 ? LibCXXSimulator_NotAvailable - : LibCXXSimulator_Available; + TargetSimulatorVersionFromDefines = VersionTuple(Major, Minor, Micro); } + // When using the define to indicate the simulator, we force + // 10.6 macosx target. + const Option *O = Opts.getOption(options::OPT_mmacosx_version_min_EQ); + OSXVersion = Args.MakeJoinedArg(0, O, "10.6"); + Args.append(OSXVersion); break; } } @@ -593,9 +509,9 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { // If no OSX or iOS target has been specified and we're compiling for armv7, // go ahead as assume we're targeting iOS. - if (OSXTarget.empty() && iOSTarget.empty()) - if (getDarwinArchName(Args) == "armv7") - iOSTarget = "0.0"; + if (OSXTarget.empty() && iOSTarget.empty() && + getDarwinArchName(Args) == "armv7") + iOSTarget = iOSVersionMin; // Handle conflicting deployment targets // @@ -956,27 +872,27 @@ DerivedArgList *Darwin::TranslateArgs(const DerivedArgList &Args, // Add an explicit version min argument for the deployment target. We do this // after argument translation because -Xarch_ arguments may add a version min // argument. - AddDeploymentTarget(*DAL); + if (BoundArch) + AddDeploymentTarget(*DAL); // Validate the C++ standard library choice. CXXStdlibType Type = GetCXXStdlibType(*DAL); if (Type == ToolChain::CST_Libcxx) { - switch (LibCXXForSimulator) { - case LibCXXSimulator_None: - // Handle non-simulator cases. - if (isTargetIPhoneOS()) { - if (isIPhoneOSVersionLT(5, 0)) { - getDriver().Diag(clang::diag::err_drv_invalid_libcxx_deployment) - << "iOS 5.0"; - } - } - break; - case LibCXXSimulator_NotAvailable: + // Check whether the target provides libc++. + StringRef where; + + // Complain about targetting iOS < 5.0 in any way. + if (TargetSimulatorVersionFromDefines != VersionTuple()) { + if (TargetSimulatorVersionFromDefines < VersionTuple(5, 0)) + where = "iOS 5.0"; + } else if (isTargetIPhoneOS()) { + if (isIPhoneOSVersionLT(5, 0)) + where = "iOS 5.0"; + } + + if (where != StringRef()) { getDriver().Diag(clang::diag::err_drv_invalid_libcxx_deployment) - << "iOS 5.0"; - break; - case LibCXXSimulator_Available: - break; + << where; } } @@ -1187,6 +1103,9 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( "arm-linux-gnueabi", "arm-linux-androideabi" }; + static const char *const ARMHFTriples[] = { + "arm-linux-gnueabihf", + }; static const char *const X86_64LibDirs[] = { "/lib64", "/lib" }; static const char *const X86_64Triples[] = { @@ -1210,7 +1129,8 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( "i586-redhat-linux", "i386-redhat-linux", "i586-suse-linux", - "i486-slackware-linux" + "i486-slackware-linux", + "i686-montavista-linux" }; static const char *const MIPSLibDirs[] = { "/lib" }; @@ -1218,11 +1138,17 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( static const char *const MIPSELLibDirs[] = { "/lib" }; static const char *const MIPSELTriples[] = { "mipsel-linux-gnu" }; + static const char *const MIPS64LibDirs[] = { "/lib64", "/lib" }; + static const char *const MIPS64Triples[] = { "mips64-linux-gnu" }; + static const char *const MIPS64ELLibDirs[] = { "/lib64", "/lib" }; + static const char *const MIPS64ELTriples[] = { "mips64el-linux-gnu" }; + static const char *const PPCLibDirs[] = { "/lib32", "/lib" }; static const char *const PPCTriples[] = { "powerpc-linux-gnu", "powerpc-unknown-linux-gnu", - "powerpc-suse-linux" + "powerpc-suse-linux", + "powerpc-montavista-linuxspe" }; static const char *const PPC64LibDirs[] = { "/lib64", "/lib" }; static const char *const PPC64Triples[] = { @@ -1236,8 +1162,13 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( case llvm::Triple::arm: case llvm::Triple::thumb: LibDirs.append(ARMLibDirs, ARMLibDirs + llvm::array_lengthof(ARMLibDirs)); - TripleAliases.append( - ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples)); + if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF) { + TripleAliases.append( + ARMHFTriples, ARMHFTriples + llvm::array_lengthof(ARMHFTriples)); + } else { + TripleAliases.append( + ARMTriples, ARMTriples + llvm::array_lengthof(ARMTriples)); + } break; case llvm::Triple::x86_64: LibDirs.append( @@ -1263,12 +1194,40 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( MIPSLibDirs, MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs)); TripleAliases.append( MIPSTriples, MIPSTriples + llvm::array_lengthof(MIPSTriples)); + MultiarchLibDirs.append( + MIPS64LibDirs, MIPS64LibDirs + llvm::array_lengthof(MIPS64LibDirs)); + MultiarchTripleAliases.append( + MIPS64Triples, MIPS64Triples + llvm::array_lengthof(MIPS64Triples)); break; case llvm::Triple::mipsel: LibDirs.append( MIPSELLibDirs, MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs)); TripleAliases.append( MIPSELTriples, MIPSELTriples + llvm::array_lengthof(MIPSELTriples)); + MultiarchLibDirs.append( + MIPS64ELLibDirs, MIPS64ELLibDirs + llvm::array_lengthof(MIPS64ELLibDirs)); + MultiarchTripleAliases.append( + MIPS64ELTriples, MIPS64ELTriples + llvm::array_lengthof(MIPS64ELTriples)); + break; + case llvm::Triple::mips64: + LibDirs.append( + MIPS64LibDirs, MIPS64LibDirs + llvm::array_lengthof(MIPS64LibDirs)); + TripleAliases.append( + MIPS64Triples, MIPS64Triples + llvm::array_lengthof(MIPS64Triples)); + MultiarchLibDirs.append( + MIPSLibDirs, MIPSLibDirs + llvm::array_lengthof(MIPSLibDirs)); + MultiarchTripleAliases.append( + MIPSTriples, MIPSTriples + llvm::array_lengthof(MIPSTriples)); + break; + case llvm::Triple::mips64el: + LibDirs.append( + MIPS64ELLibDirs, MIPS64ELLibDirs + llvm::array_lengthof(MIPS64ELLibDirs)); + TripleAliases.append( + MIPS64ELTriples, MIPS64ELTriples + llvm::array_lengthof(MIPS64ELTriples)); + MultiarchLibDirs.append( + MIPSELLibDirs, MIPSELLibDirs + llvm::array_lengthof(MIPSELLibDirs)); + MultiarchTripleAliases.append( + MIPSELTriples, MIPSELTriples + llvm::array_lengthof(MIPSELTriples)); break; case llvm::Triple::ppc: LibDirs.append(PPCLibDirs, PPCLibDirs + llvm::array_lengthof(PPCLibDirs)); @@ -1350,7 +1309,9 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( // crtbegin.o without the subdirectory. StringRef MultiarchSuffix = (TargetArch == llvm::Triple::x86_64 || - TargetArch == llvm::Triple::ppc64) ? "/64" : "/32"; + TargetArch == llvm::Triple::ppc64 || + TargetArch == llvm::Triple::mips64 || + TargetArch == llvm::Triple::mips64el) ? "/64" : "/32"; if (llvm::sys::fs::exists(LI->path() + MultiarchSuffix + "/crtbegin.o")) { GCCMultiarchSuffix = MultiarchSuffix.str(); } else { @@ -1606,6 +1567,67 @@ Tool &OpenBSD::SelectTool(const Compilation &C, const JobAction &JA, return *T; } +/// Bitrig - Bitrig tool chain which can call as(1) and ld(1) directly. + +Bitrig::Bitrig(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) + : Generic_ELF(D, Triple, Args) { + getFilePaths().push_back(getDriver().Dir + "/../lib"); + getFilePaths().push_back("/usr/lib"); +} + +Tool &Bitrig::SelectTool(const Compilation &C, const JobAction &JA, + const ActionList &Inputs) const { + Action::ActionClass Key; + if (getDriver().ShouldUseClangCompiler(C, JA, getTriple())) + Key = Action::AnalyzeJobClass; + else + Key = JA.getKind(); + + bool UseIntegratedAs = C.getArgs().hasFlag(options::OPT_integrated_as, + options::OPT_no_integrated_as, + IsIntegratedAssemblerDefault()); + + Tool *&T = Tools[Key]; + if (!T) { + switch (Key) { + case Action::AssembleJobClass: { + if (UseIntegratedAs) + T = new tools::ClangAs(*this); + else + T = new tools::bitrig::Assemble(*this); + break; + } + case Action::LinkJobClass: + T = new tools::bitrig::Link(*this); break; + default: + T = &Generic_GCC::SelectTool(C, JA, Inputs); + } + } + + return *T; +} + +void Bitrig::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(options::OPT_nostdlibinc) || + DriverArgs.hasArg(options::OPT_nostdincxx)) + return; + + std::string Triple = getTriple().str(); + if (Triple.substr(0, 5) == "amd64") + Triple.replace(0, 5, "x86_64"); + + addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2"); + addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2/backward"); + addSystemInclude(DriverArgs, CC1Args, "/usr/include/c++/4.6.2/" + Triple); + +} + +void Bitrig::AddCXXStdlibLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + CmdArgs.push_back("-lstdc++"); +} + /// FreeBSD - FreeBSD tool chain which can call as(1) and ld(1) directly. FreeBSD::FreeBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) @@ -1957,6 +1979,16 @@ static std::string getMultiarchTriple(const llvm::Triple TargetTriple, // common linux triples that don't quite match the Clang triple for both // 32-bit and 64-bit targets. Multiarch fixes its install triples to these // regardless of what the actual target triple is. + case llvm::Triple::arm: + case llvm::Triple::thumb: + if (TargetTriple.getEnvironment() == llvm::Triple::GNUEABIHF) { + if (llvm::sys::fs::exists(SysRoot + "/lib/arm-linux-gnueabihf")) + return "arm-linux-gnueabihf"; + } else { + if (llvm::sys::fs::exists(SysRoot + "/lib/arm-linux-gnueabi")) + return "arm-linux-gnueabi"; + } + return TargetTriple.str(); case llvm::Triple::x86: if (llvm::sys::fs::exists(SysRoot + "/lib/i386-linux-gnu")) return "i386-linux-gnu"; @@ -2139,6 +2171,12 @@ Tool &Linux::SelectTool(const Compilation &C, const JobAction &JA, return *T; } +void Linux::addClangTargetOptions(ArgStringList &CC1Args) const { + const Generic_GCC::GCCVersion &V = GCCInstallation.getVersion(); + if (V >= Generic_GCC::GCCVersion::Parse("4.7.0")) + CC1Args.push_back("-fuse-init-array"); +} + void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { const Driver &D = getDriver(); @@ -2197,6 +2235,9 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, const StringRef ARMMultiarchIncludeDirs[] = { "/usr/include/arm-linux-gnueabi" }; + const StringRef ARMHFMultiarchIncludeDirs[] = { + "/usr/include/arm-linux-gnueabihf" + }; const StringRef MIPSMultiarchIncludeDirs[] = { "/usr/include/mips-linux-gnu" }; @@ -2215,7 +2256,10 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, } else if (getTriple().getArch() == llvm::Triple::x86) { MultiarchIncludeDirs = X86MultiarchIncludeDirs; } else if (getTriple().getArch() == llvm::Triple::arm) { - MultiarchIncludeDirs = ARMMultiarchIncludeDirs; + if (getTriple().getEnvironment() == llvm::Triple::GNUEABIHF) + MultiarchIncludeDirs = ARMHFMultiarchIncludeDirs; + else + MultiarchIncludeDirs = ARMMultiarchIncludeDirs; } else if (getTriple().getArch() == llvm::Triple::mips) { MultiarchIncludeDirs = MIPSMultiarchIncludeDirs; } else if (getTriple().getArch() == llvm::Triple::mipsel) { @@ -2281,7 +2325,7 @@ void Linux::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, // equivalent to '/usr/include/c++/X.Y' in almost all cases. StringRef LibDir = GCCInstallation.getParentLibPath(); StringRef InstallDir = GCCInstallation.getInstallPath(); - StringRef Version = GCCInstallation.getVersion(); + StringRef Version = GCCInstallation.getVersion().Text; if (!addLibStdCXXIncludePaths(LibDir + "/../include/c++/" + Version, (GCCInstallation.getTriple().str() + GCCInstallation.getMultiarchSuffix()), diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index eaa6be1b0d6a..95a11beea158 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -99,7 +99,7 @@ protected: StringRef getParentLibPath() const { return GCCParentLibPath; } /// \brief Get the detected GCC version string. - StringRef getVersion() const { return Version.Text; } + const GCCVersion &getVersion() const { return Version; } private: static void CollectLibDirsAndTriples( @@ -176,22 +176,6 @@ private: // the argument translation business. mutable bool TargetInitialized; - // FIXME: Remove this once there is a proper way to detect an ARC runtime - // for the simulator. - public: - mutable enum { - ARCSimulator_None, - ARCSimulator_HasARCRuntime, - ARCSimulator_NoARCRuntime - } ARCRuntimeForSimulator; - - mutable enum { - LibCXXSimulator_None, - LibCXXSimulator_NotAvailable, - LibCXXSimulator_Available - } LibCXXForSimulator; - -private: /// Whether we are targeting iPhoneOS target. mutable bool TargetIsIPhoneOS; @@ -201,12 +185,19 @@ private: /// The OS version we are targeting. mutable VersionTuple TargetVersion; +protected: + // FIXME: Remove this once there is a proper way to detect an ARC runtime + // for the simulator. + mutable VersionTuple TargetSimulatorVersionFromDefines; + +private: /// The default macosx-version-min of this tool chain; empty until /// initialized. std::string MacosxVersionMin; - bool hasARCRuntime() const; - bool hasSubscriptingRuntime() const; + /// The default ios-version-min of this tool chain; empty until + /// initialized. + std::string iOSVersionMin; private: void AddDeploymentTarget(DerivedArgList &Args) const; @@ -254,7 +245,7 @@ public: bool isTargetMacOS() const { return !isTargetIOSSimulator() && !isTargetIPhoneOS() && - ARCRuntimeForSimulator == ARCSimulator_None; + TargetSimulatorVersionFromDefines == VersionTuple(); } bool isTargetInitialized() const { return TargetInitialized; } @@ -279,14 +270,6 @@ public: return TargetVersion < VersionTuple(V0, V1, V2); } - /// AddLinkSearchPathArgs - Add the linker search paths to \arg CmdArgs. - /// - /// \param Args - The input argument list. - /// \param CmdArgs [out] - The command argument list to append the paths - /// (prefixed by -L) to. - virtual void AddLinkSearchPathArgs(const ArgList &Args, - ArgStringList &CmdArgs) const = 0; - /// AddLinkARCArgs - Add the linker arguments to link the ARC runtime library. virtual void AddLinkARCArgs(const ArgList &Args, ArgStringList &CmdArgs) const = 0; @@ -304,7 +287,7 @@ public: virtual bool HasNativeLLVMSupport() const; - virtual void configureObjCRuntime(ObjCRuntime &runtime) const; + virtual ObjCRuntime getDefaultObjCRuntime(bool isNonFragile) const; virtual bool hasBlocksRuntime() const; virtual DerivedArgList *TranslateArgs(const DerivedArgList &Args, @@ -333,7 +316,11 @@ public: return ToolChain::IsStrictAliasingDefault(); #endif } - + + virtual bool IsMathErrnoDefault() const { + return false; + } + virtual bool IsObjCDefaultSynthPropertiesDefault() const { return true; } @@ -342,12 +329,7 @@ public: // Non-fragile ABI is default for everything but i386. return getTriple().getArch() != llvm::Triple::x86; } - virtual bool IsObjCLegacyDispatchDefault() const { - // This is only used with the non-fragile ABI. - // Legacy dispatch is used everywhere except on x86_64. - return getTriple().getArch() != llvm::Triple::x86_64; - } virtual bool UseObjCMixedDispatch() const { // This is only used with the non-fragile ABI and non-legacy dispatch. @@ -392,9 +374,6 @@ public: /// @name Darwin ToolChain Implementation /// { - virtual void AddLinkSearchPathArgs(const ArgList &Args, - ArgStringList &CmdArgs) const; - virtual void AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const; void AddLinkRuntimeLib(const ArgList &Args, ArgStringList &CmdArgs, @@ -459,33 +438,39 @@ class LLVM_LIBRARY_VISIBILITY OpenBSD : public Generic_ELF { public: OpenBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + virtual bool IsMathErrnoDefault() const { return false; } virtual bool IsObjCNonFragileABIDefault() const { return true; } - virtual bool IsObjCLegacyDispatchDefault() const { - llvm::Triple::ArchType Arch = getTriple().getArch(); - if (Arch == llvm::Triple::arm || - Arch == llvm::Triple::x86 || - Arch == llvm::Triple::x86_64) - return false; - return true; - } virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const; }; +class LLVM_LIBRARY_VISIBILITY Bitrig : public Generic_ELF { +public: + Bitrig(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + + virtual bool IsMathErrnoDefault() const { return false; } + virtual bool IsObjCNonFragileABIDefault() const { return true; } + virtual bool IsObjCLegacyDispatchDefault() const { return false; } + + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, + const ActionList &Inputs) const; + + virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const; + virtual void AddCXXStdlibLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const; + virtual unsigned GetDefaultStackProtectorLevel(bool KernelOrKext) const { + return 1; + } +}; + class LLVM_LIBRARY_VISIBILITY FreeBSD : public Generic_ELF { public: FreeBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + virtual bool IsMathErrnoDefault() const { return false; } virtual bool IsObjCNonFragileABIDefault() const { return true; } - virtual bool IsObjCLegacyDispatchDefault() const { - llvm::Triple::ArchType Arch = getTriple().getArch(); - if (Arch == llvm::Triple::arm || - Arch == llvm::Triple::x86 || - Arch == llvm::Triple::x86_64) - return false; - return true; - } virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const; @@ -495,15 +480,8 @@ class LLVM_LIBRARY_VISIBILITY NetBSD : public Generic_ELF { public: NetBSD(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + virtual bool IsMathErrnoDefault() const { return false; } virtual bool IsObjCNonFragileABIDefault() const { return true; } - virtual bool IsObjCLegacyDispatchDefault() const { - llvm::Triple::ArchType Arch = getTriple().getArch(); - if (Arch == llvm::Triple::arm || - Arch == llvm::Triple::x86 || - Arch == llvm::Triple::x86_64) - return false; - return true; - } virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const; @@ -521,6 +499,8 @@ class LLVM_LIBRARY_VISIBILITY DragonFly : public Generic_ELF { public: DragonFly(const Driver &D, const llvm::Triple& Triple, const ArgList &Args); + virtual bool IsMathErrnoDefault() const { return false; } + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const; }; @@ -536,6 +516,7 @@ public: virtual void AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const; + virtual void addClangTargetOptions(ArgStringList &CC1Args) const; virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const; @@ -577,6 +558,10 @@ public: virtual Tool &SelectTool(const Compilation &C, const JobAction &JA, const ActionList &Inputs) const; + virtual bool IsObjCDefaultSynthPropertiesDefault() const { + return true; + } + virtual bool IsIntegratedAssemblerDefault() const; virtual bool IsUnwindTablesDefault() const; virtual const char *GetDefaultRelocationModel() const; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 47b52949546d..b4234cfbc845 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -16,11 +16,11 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Job.h" -#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/Option.h" #include "clang/Driver/Options.h" #include "clang/Driver/ToolChain.h" #include "clang/Driver/Util.h" +#include "clang/Basic/ObjCRuntime.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -174,8 +174,10 @@ static bool isObjCAutoRefCount(const ArgList &Args) { /// \brief Determine whether we are linking the ObjC runtime. static bool isObjCRuntimeLinked(const ArgList &Args) { - if (isObjCAutoRefCount(Args)) + if (isObjCAutoRefCount(Args)) { + Args.ClaimAllArgs(options::OPT_fobjc_link_runtime); return true; + } return Args.hasArg(options::OPT_fobjc_link_runtime); } @@ -422,16 +424,47 @@ void Clang::AddPreprocessingOptions(Compilation &C, getToolChain().AddClangSystemIncludeArgs(Args, CmdArgs); } +/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular +/// CPU. +// +// FIXME: This is redundant with -mcpu, why does LLVM use this. +// FIXME: tblgen this, or kill it! +static const char *getLLVMArchSuffixForARM(StringRef CPU) { + return llvm::StringSwitch<const char *>(CPU) + .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t") + .Cases("arm720t", "arm9", "arm9tdmi", "v4t") + .Cases("arm920", "arm920t", "arm922t", "v4t") + .Cases("arm940t", "ep9312","v4t") + .Cases("arm10tdmi", "arm1020t", "v5") + .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e") + .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e") + .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e") + .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6") + .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6") + .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2") + .Cases("cortex-a8", "cortex-a9", "v7") + .Case("cortex-m3", "v7m") + .Case("cortex-m4", "v7m") + .Case("cortex-m0", "v6m") + .Default(""); +} + /// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting. // // FIXME: tblgen this. -static const char *getARMTargetCPU(const ArgList &Args, +static std::string getARMTargetCPU(const ArgList &Args, const llvm::Triple &Triple) { // FIXME: Warn on inconsistent use of -mcpu and -march. // If we have -mcpu=, use that. - if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) - return A->getValue(Args); + if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { + StringRef MCPU = A->getValue(Args); + // Handle -mcpu=native. + if (MCPU == "native") + return llvm::sys::getHostCPUName(); + else + return MCPU; + } StringRef MArch; if (Arg *A = Args.getLastArg(options::OPT_march_EQ)) { @@ -442,13 +475,25 @@ static const char *getARMTargetCPU(const ArgList &Args, MArch = Triple.getArchName(); } + // Handle -march=native. + std::string NativeMArch; + if (MArch == "native") { + std::string CPU = llvm::sys::getHostCPUName(); + if (CPU != "generic") { + // Translate the native cpu into the architecture. The switch below will + // then chose the minimum cpu for that arch. + NativeMArch = std::string("arm") + getLLVMArchSuffixForARM(CPU); + MArch = NativeMArch; + } + } + return llvm::StringSwitch<const char *>(MArch) .Cases("armv2", "armv2a","arm2") .Case("armv3", "arm6") .Case("armv3m", "arm7m") .Cases("armv4", "armv4t", "arm7tdmi") .Cases("armv5", "armv5t", "arm10tdmi") - .Cases("armv5e", "armv5te", "arm1026ejs") + .Cases("armv5e", "armv5te", "arm1022e") .Case("armv5tej", "arm926ej-s") .Cases("armv6", "armv6k", "arm1136jf-s") .Case("armv6j", "arm1136j-s") @@ -465,31 +510,6 @@ static const char *getARMTargetCPU(const ArgList &Args, .Default("arm7tdmi"); } -/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular -/// CPU. -// -// FIXME: This is redundant with -mcpu, why does LLVM use this. -// FIXME: tblgen this, or kill it! -static const char *getLLVMArchSuffixForARM(StringRef CPU) { - return llvm::StringSwitch<const char *>(CPU) - .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t") - .Cases("arm720t", "arm9", "arm9tdmi", "v4t") - .Cases("arm920", "arm920t", "arm922t", "v4t") - .Cases("arm940t", "ep9312","v4t") - .Cases("arm10tdmi", "arm1020t", "v5") - .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e") - .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e") - .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e") - .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6") - .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6") - .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2") - .Cases("cortex-a8", "cortex-a9", "v7") - .Case("cortex-m3", "v7m") - .Case("cortex-m4", "v7m") - .Case("cortex-m0", "v6m") - .Default(""); -} - // FIXME: Move to target hook. static bool isSignedCharDefault(const llvm::Triple &Triple) { switch (Triple.getArch()) { @@ -601,25 +621,21 @@ static StringRef getARMFloatABI(const Driver &D, // Darwin defaults to "softfp" for v6 and v7. // // FIXME: Factor out an ARM class so we can cache the arch somewhere. - StringRef ArchName = + std::string ArchName = getLLVMArchSuffixForARM(getARMTargetCPU(Args, Triple)); - if (ArchName.startswith("v6") || ArchName.startswith("v7")) + if (StringRef(ArchName).startswith("v6") || + StringRef(ArchName).startswith("v7")) FloatABI = "softfp"; else FloatABI = "soft"; break; } - case llvm::Triple::Linux: { - if (Triple.getEnvironment() == llvm::Triple::GNUEABI) { - FloatABI = "softfp"; - break; - } - } - // fall through - default: switch(Triple.getEnvironment()) { + case llvm::Triple::GNUEABIHF: + FloatABI = "hard"; + break; case llvm::Triple::GNUEABI: FloatABI = "softfp"; break; @@ -628,9 +644,9 @@ static StringRef getARMFloatABI(const Driver &D, FloatABI = "softfp"; break; case llvm::Triple::ANDROIDEABI: { - StringRef ArchName = + std::string ArchName = getLLVMArchSuffixForARM(getARMTargetCPU(Args, Triple)); - if (ArchName.startswith("v7")) + if (StringRef(ArchName).startswith("v7")) FloatABI = "softfp"; else FloatABI = "soft"; @@ -666,6 +682,7 @@ void Clang::AddARMTargetArgs(const ArgList &Args, switch(Triple.getEnvironment()) { case llvm::Triple::ANDROIDEABI: case llvm::Triple::GNUEABI: + case llvm::Triple::GNUEABIHF: ABIName = "aapcs-linux"; break; case llvm::Triple::EABI: @@ -680,7 +697,7 @@ void Clang::AddARMTargetArgs(const ArgList &Args, // Set the CPU based on -march= and -mcpu=. CmdArgs.push_back("-target-cpu"); - CmdArgs.push_back(getARMTargetCPU(Args, Triple)); + CmdArgs.push_back(Args.MakeArgString(getARMTargetCPU(Args, Triple))); // Determine floating point ABI from the options & target defaults. StringRef FloatABI = getARMFloatABI(D, Args, Triple); @@ -755,6 +772,9 @@ void Clang::AddARMTargetArgs(const ArgList &Args, if (A->getOption().matches(options::OPT_mno_global_merge)) CmdArgs.push_back("-mno-global-merge"); } + + if (Args.hasArg(options::OPT_mno_implicit_float)) + CmdArgs.push_back("-no-implicit-float"); } // Get default architecture. @@ -825,19 +845,9 @@ static void getMipsCPUAndABI(const ArgList &Args, ABIName = getMipsABIFromArch(ArchName); } -void Clang::AddMIPSTargetArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { - const Driver &D = getToolChain().getDriver(); - StringRef CPUName; - StringRef ABIName; - getMipsCPUAndABI(Args, getToolChain(), CPUName, ABIName); - - CmdArgs.push_back("-target-cpu"); - CmdArgs.push_back(CPUName.data()); - - CmdArgs.push_back("-target-abi"); - CmdArgs.push_back(ABIName.data()); - +// Select the MIPS float ABI as determined by -msoft-float, -mhard-float, +// and -mfloat-abi=. +static StringRef getMipsFloatABI(const Driver &D, const ArgList &Args) { // Select the float ABI as determined by -msoft-float, -mhard-float, // and -mfloat-abi=. StringRef FloatABI; @@ -851,8 +861,7 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, else { FloatABI = A->getValue(Args); if (FloatABI != "soft" && FloatABI != "single" && FloatABI != "hard") { - D.Diag(diag::err_drv_invalid_mfloat_abi) - << A->getAsString(Args); + D.Diag(diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args); FloatABI = "hard"; } } @@ -866,6 +875,38 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, FloatABI = "hard"; } + return FloatABI; +} + +static void AddTargetFeature(const ArgList &Args, + ArgStringList &CmdArgs, + OptSpecifier OnOpt, + OptSpecifier OffOpt, + StringRef FeatureName) { + if (Arg *A = Args.getLastArg(OnOpt, OffOpt)) { + CmdArgs.push_back("-target-feature"); + if (A->getOption().matches(OnOpt)) + CmdArgs.push_back(Args.MakeArgString("+" + FeatureName)); + else + CmdArgs.push_back(Args.MakeArgString("-" + FeatureName)); + } +} + +void Clang::AddMIPSTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + const Driver &D = getToolChain().getDriver(); + StringRef CPUName; + StringRef ABIName; + getMipsCPUAndABI(Args, getToolChain(), CPUName, ABIName); + + CmdArgs.push_back("-target-cpu"); + CmdArgs.push_back(CPUName.data()); + + CmdArgs.push_back("-target-abi"); + CmdArgs.push_back(ABIName.data()); + + StringRef FloatABI = getMipsFloatABI(D, Args); + if (FloatABI == "soft") { // Floating point operations and argument passing are soft. CmdArgs.push_back("-msoft-float"); @@ -890,6 +931,82 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, CmdArgs.push_back("-mfloat-abi"); CmdArgs.push_back("hard"); } + + AddTargetFeature(Args, CmdArgs, + options::OPT_mips16, options::OPT_mno_mips16, + "mips16"); + AddTargetFeature(Args, CmdArgs, + options::OPT_mdsp, options::OPT_mno_dsp, + "dsp"); + AddTargetFeature(Args, CmdArgs, + options::OPT_mdspr2, options::OPT_mno_dspr2, + "dspr2"); +} + +/// getPPCTargetCPU - Get the (LLVM) name of the PowerPC cpu we are targeting. +static std::string getPPCTargetCPU(const ArgList &Args) { + if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { + StringRef CPUName = A->getValue(Args); + + if (CPUName == "native") { + std::string CPU = llvm::sys::getHostCPUName(); + if (!CPU.empty() && CPU != "generic") + return CPU; + else + return ""; + } + + return llvm::StringSwitch<const char *>(CPUName) + .Case("common", "generic") + .Case("440", "440") + .Case("440fp", "440") + .Case("450", "450") + .Case("601", "601") + .Case("602", "602") + .Case("603", "603") + .Case("603e", "603e") + .Case("603ev", "603ev") + .Case("604", "604") + .Case("604e", "604e") + .Case("620", "620") + .Case("G3", "g3") + .Case("7400", "7400") + .Case("G4", "g4") + .Case("7450", "7450") + .Case("G4+", "g4+") + .Case("750", "750") + .Case("970", "970") + .Case("G5", "g5") + .Case("a2", "a2") + .Case("power6", "pwr6") + .Case("power7", "pwr7") + .Case("powerpc", "ppc") + .Case("powerpc64", "ppc64") + .Default(""); + } + + return ""; +} + +void Clang::AddPPCTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + std::string TargetCPUName = getPPCTargetCPU(Args); + + // LLVM may default to generating code for the native CPU, + // but, like gcc, we default to a more generic option for + // each architecture. (except on Darwin) + llvm::Triple Triple = getToolChain().getTriple(); + if (TargetCPUName.empty() && !Triple.isOSDarwin()) { + if (Triple.getArch() == llvm::Triple::ppc64) + TargetCPUName = "ppc64"; + else + TargetCPUName = "ppc"; + } + + if (!TargetCPUName.empty()) { + CmdArgs.push_back("-target-cpu"); + CmdArgs.push_back(Args.MakeArgString(TargetCPUName.c_str())); + } } void Clang::AddSparcTargetArgs(const ArgList &Args, @@ -958,7 +1075,7 @@ void Clang::AddX86TargetArgs(const ArgList &Args, // FIXME: We should also incorporate the detected target features for use // with -native. std::string CPU = llvm::sys::getHostCPUName(); - if (!CPU.empty()) + if (!CPU.empty() && CPU != "generic") CPUName = Args.MakeArgString(CPU); } else CPUName = A->getValue(Args); @@ -982,6 +1099,11 @@ void Clang::AddX86TargetArgs(const ArgList &Args, CPUName = "x86-64"; else if (getToolChain().getArch() == llvm::Triple::x86) CPUName = "i486"; + } else if (getToolChain().getOS().startswith("bitrig")) { + if (getToolChain().getArch() == llvm::Triple::x86_64) + CPUName = "x86-64"; + else if (getToolChain().getArch() == llvm::Triple::x86) + CPUName = "i686"; } else if (getToolChain().getOS().startswith("freebsd")) { if (getToolChain().getArch() == llvm::Triple::x86_64) CPUName = "x86-64"; @@ -1088,7 +1210,7 @@ void Clang::AddHexagonTargetArgs(const ArgList &Args, CmdArgs.push_back("-fno-signed-char"); CmdArgs.push_back("-nobuiltininc"); - if (Args.hasArg(options::OPT_mqdsp6_compat)) + if (Args.hasArg(options::OPT_mqdsp6_compat)) CmdArgs.push_back("-mqdsp6-compat"); if (Arg *A = Args.getLastArg(options::OPT_G, @@ -1100,18 +1222,23 @@ void Clang::AddHexagonTargetArgs(const ArgList &Args, A->claim(); } + if (!Args.hasArg(options::OPT_fno_short_enums)) + CmdArgs.push_back("-fshort-enums"); + if (Args.getLastArg(options::OPT_mieee_rnd_near)) { + CmdArgs.push_back ("-mllvm"); + CmdArgs.push_back ("-enable-hexagon-ieee-rnd-near"); + } CmdArgs.push_back ("-mllvm"); CmdArgs.push_back ("-machine-sink-split=0"); } static bool -shouldUseExceptionTablesForObjCExceptions(unsigned objcABIVersion, +shouldUseExceptionTablesForObjCExceptions(const ObjCRuntime &runtime, const llvm::Triple &Triple) { // We use the zero-cost exception tables for Objective-C if the non-fragile // ABI is enabled or when compiling for x86_64 and ARM on Snow Leopard and // later. - - if (objcABIVersion >= 2) + if (runtime.isNonFragile()) return true; if (!Triple.isOSDarwin()) @@ -1130,7 +1257,7 @@ shouldUseExceptionTablesForObjCExceptions(unsigned objcABIVersion, static void addExceptionArgs(const ArgList &Args, types::ID InputType, const llvm::Triple &Triple, bool KernelOrKext, - unsigned objcABIVersion, + const ObjCRuntime &objcRuntime, ArgStringList &CmdArgs) { if (KernelOrKext) { // -mkernel and -fapple-kext imply no exceptions, so claim exception related @@ -1176,7 +1303,7 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType, CmdArgs.push_back("-fobjc-exceptions"); ShouldUseExceptionTables |= - shouldUseExceptionTablesForObjCExceptions(objcABIVersion, Triple); + shouldUseExceptionTablesForObjCExceptions(objcRuntime, Triple); } if (types::isCXX(InputType)) { @@ -1269,22 +1396,56 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) { /// This needs to be called before we add the C run-time (malloc, etc). static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { - // Add asan linker flags when linking an executable, but not a shared object. - if (Args.hasArg(options::OPT_shared) || - !Args.hasFlag(options::OPT_faddress_sanitizer, + if (!Args.hasFlag(options::OPT_faddress_sanitizer, options::OPT_fno_address_sanitizer, false)) return; + if(TC.getTriple().getEnvironment() == llvm::Triple::ANDROIDEABI) { + if (!Args.hasArg(options::OPT_shared)) { + if (!Args.hasArg(options::OPT_pie)) + TC.getDriver().Diag(diag::err_drv_asan_android_requires_pie); + // For an executable, we add a .preinit_array stub. + CmdArgs.push_back("-u"); + CmdArgs.push_back("__asan_preinit"); + CmdArgs.push_back("-lasan"); + } - // LibAsan is "libclang_rt.asan-<ArchName>.a" in the Linux library resource - // directory. - SmallString<128> LibAsan(TC.getDriver().ResourceDir); - llvm::sys::path::append(LibAsan, "lib", "linux", - (Twine("libclang_rt.asan-") + - TC.getArchName() + ".a")); - CmdArgs.push_back(Args.MakeArgString(LibAsan)); - CmdArgs.push_back("-lpthread"); - CmdArgs.push_back("-ldl"); - CmdArgs.push_back("-export-dynamic"); + CmdArgs.push_back("-lasan_preload"); + CmdArgs.push_back("-ldl"); + } else { + if (!Args.hasArg(options::OPT_shared)) { + // LibAsan is "libclang_rt.asan-<ArchName>.a" in the Linux library + // resource directory. + SmallString<128> LibAsan(TC.getDriver().ResourceDir); + llvm::sys::path::append(LibAsan, "lib", "linux", + (Twine("libclang_rt.asan-") + + TC.getArchName() + ".a")); + CmdArgs.push_back(Args.MakeArgString(LibAsan)); + CmdArgs.push_back("-lpthread"); + CmdArgs.push_back("-ldl"); + CmdArgs.push_back("-export-dynamic"); + } + } +} + +/// If ThreadSanitizer is enabled, add appropriate linker flags (Linux). +/// This needs to be called before we add the C run-time (malloc, etc). +static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs) { + if (!Args.hasFlag(options::OPT_fthread_sanitizer, + options::OPT_fno_thread_sanitizer, false)) + return; + if (!Args.hasArg(options::OPT_shared)) { + // LibTsan is "libclang_rt.tsan-<ArchName>.a" in the Linux library + // resource directory. + SmallString<128> LibTsan(TC.getDriver().ResourceDir); + llvm::sys::path::append(LibTsan, "lib", "linux", + (Twine("libclang_rt.tsan-") + + TC.getArchName() + ".a")); + CmdArgs.push_back(Args.MakeArgString(LibTsan)); + CmdArgs.push_back("-lpthread"); + CmdArgs.push_back("-ldl"); + CmdArgs.push_back("-export-dynamic"); + } } static bool shouldUseFramePointer(const ArgList &Args, @@ -1328,8 +1489,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(TripleStr)); // Select the appropriate action. - bool IsRewriter = false; - bool IsModernRewriter = false; + RewriteKind rewriteKind = RK_None; if (isa<AnalyzeJobAction>(JA)) { assert(JA.getType() == types::TY_Plist && "Invalid output type."); @@ -1380,7 +1540,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Use PCH if the user requested it. bool UsePCH = D.CCCUsePCH; - if (UsePCH) + if (JA.getType() == types::TY_Nothing) + CmdArgs.push_back("-fsyntax-only"); + else if (UsePCH) CmdArgs.push_back("-emit-pch"); else CmdArgs.push_back("-emit-pth"); @@ -1401,10 +1563,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-emit-pch"); } else if (JA.getType() == types::TY_RewrittenObjC) { CmdArgs.push_back("-rewrite-objc"); - IsModernRewriter = true; + rewriteKind = RK_NonFragile; } else if (JA.getType() == types::TY_RewrittenLegacyObjC) { CmdArgs.push_back("-rewrite-objc"); - IsRewriter = true; + rewriteKind = RK_Fragile; } else { assert(JA.getType() == types::TY_PP_Asm && "Unexpected output type!"); @@ -1488,22 +1650,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // This comes from the default translation the driver + cc1 // would do to enable flag_pic. - // - // FIXME: Centralize this code. - Arg *LastPICArg = 0; - for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) { - if ((*I)->getOption().matches(options::OPT_fPIC) || - (*I)->getOption().matches(options::OPT_fno_PIC) || - (*I)->getOption().matches(options::OPT_fpic) || - (*I)->getOption().matches(options::OPT_fno_pic) || - (*I)->getOption().matches(options::OPT_fPIE) || - (*I)->getOption().matches(options::OPT_fno_PIE) || - (*I)->getOption().matches(options::OPT_fpie) || - (*I)->getOption().matches(options::OPT_fno_pie)) { - LastPICArg = *I; - (*I)->claim(); - } - } + + Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC, + options::OPT_fpic, options::OPT_fno_pic, + options::OPT_fPIE, options::OPT_fno_PIE, + options::OPT_fpie, options::OPT_fno_pie); bool PICDisabled = false; bool PICEnabled = false; bool PICForPIE = false; @@ -1606,16 +1757,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().getID() != options::OPT_fhonor_nans) CmdArgs.push_back("-menable-no-nans"); - // -fno-math-errno is default. - bool MathErrno = false; + // -fmath-errno is the default on some platforms, e.g. BSD-derived OSes. + bool MathErrno = getToolChain().IsMathErrnoDefault(); if (Arg *A = Args.getLastArg(options::OPT_ffast_math, options::OPT_fmath_errno, - options::OPT_fno_math_errno)) { - if (A->getOption().getID() == options::OPT_fmath_errno) { - CmdArgs.push_back("-fmath-errno"); - MathErrno = true; - } - } + options::OPT_fno_math_errno)) + MathErrno = A->getOption().getID() == options::OPT_fmath_errno; + if (MathErrno) + CmdArgs.push_back("-fmath-errno"); // There are several flags which require disabling very specific // optimizations. Any of these being disabled forces us to turn off the @@ -1661,12 +1810,33 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, !TrappingMath) CmdArgs.push_back("-menable-unsafe-fp-math"); - // We separately look for the '-ffast-math' flag, and if we find it, tell the - // frontend to provide the appropriate preprocessor macros. This is distinct - // from enabling any optimizations as it induces a language change which must - // survive serialization and deserialization, etc. + + // Validate and pass through -fp-contract option. + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + options::OPT_ffp_contract)) { + if (A->getOption().getID() == options::OPT_ffp_contract) { + StringRef Val = A->getValue(Args); + if (Val == "fast" || Val == "on" || Val == "off") { + CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + Val)); + } else { + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + } + } else { // A is OPT_ffast_math + // If fast-math is set then set the fp-contract mode to fast. + CmdArgs.push_back(Args.MakeArgString("-ffp-contract=fast")); + } + } + + // We separately look for the '-ffast-math' and '-ffinite-math-only' flags, + // and if we find them, tell the frontend to provide the appropriate + // preprocessor macros. This is distinct from enabling any optimizations as + // these options induce language changes which must survive serialization + // and deserialization, etc. if (Args.hasArg(options::OPT_ffast_math)) CmdArgs.push_back("-ffast-math"); + if (Args.hasArg(options::OPT_ffinite_math_only)) + CmdArgs.push_back("-ffinite-math-only"); // Decide whether to use verbose asm. Verbose assembly is the default on // toolchains which have the integrated assembler on by default. @@ -1711,6 +1881,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, AsynchronousUnwindTables)) CmdArgs.push_back("-munwind-tables"); + getToolChain().addClangTargetOptions(CmdArgs); + if (Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { CmdArgs.push_back("-mlimit-float-precision"); CmdArgs.push_back(A->getValue(Args)); @@ -1741,6 +1913,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, AddMIPSTargetArgs(Args, CmdArgs); break; + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + AddPPCTargetArgs(Args, CmdArgs); + break; + case llvm::Triple::sparc: AddSparcTargetArgs(Args, CmdArgs); break; @@ -1800,13 +1977,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, D.CCLogDiagnosticsFilename : "-"); } - // Special case debug options to only pass -g to clang. This is - // wrong. + // Use the last option from "-g" group. "-gline-tables-only" is + // preserved, all other debug options are substituted with "-g". Args.ClaimAllArgs(options::OPT_g_Group); - if (Arg *A = Args.getLastArg(options::OPT_g_Group)) - if (!A->getOption().matches(options::OPT_g0)) { + if (Arg *A = Args.getLastArg(options::OPT_g_Group)) { + if (A->getOption().matches(options::OPT_gline_tables_only)) { + CmdArgs.push_back("-gline-tables-only"); + } else if (!A->getOption().matches(options::OPT_g0) && + !A->getOption().matches(options::OPT_ggdb0)) { CmdArgs.push_back("-g"); } + } + + // We ignore flags -gstrict-dwarf and -grecord-gcc-switches for now. + Args.ClaimAllArgs(options::OPT_g_flags_Group); Args.AddAllArgs(CmdArgs, options::OPT_ffunction_sections); Args.AddAllArgs(CmdArgs, options::OPT_fdata_sections); @@ -1917,7 +2101,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } Args.AddAllArgs(CmdArgs, options::OPT_W_Group); - Args.AddLastArg(CmdArgs, options::OPT_pedantic); + if (Args.hasFlag(options::OPT_pedantic, options::OPT_no_pedantic, false)) + CmdArgs.push_back("-pedantic"); Args.AddLastArg(CmdArgs, options::OPT_pedantic_errors); Args.AddLastArg(CmdArgs, options::OPT_w); @@ -2007,11 +2192,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Arg *A = Args.getLastArg(options::OPT_Wlarge_by_value_copy_EQ, options::OPT_Wlarge_by_value_copy_def)) { - CmdArgs.push_back("-Wlarge-by-value-copy"); - if (A->getNumValues()) - CmdArgs.push_back(A->getValue(Args)); - else - CmdArgs.push_back("64"); // default value for -Wlarge-by-value-copy. + if (A->getNumValues()) { + StringRef bytes = A->getValue(Args); + CmdArgs.push_back(Args.MakeArgString("-Wlarge-by-value-copy=" + bytes)); + } else + CmdArgs.push_back("-Wlarge-by-value-copy=64"); // default value + } + + if (Arg *A = Args.getLastArg(options::OPT_fbounds_checking, + options::OPT_fbounds_checking_EQ)) { + if (A->getNumValues()) { + StringRef val = A->getValue(Args); + CmdArgs.push_back(Args.MakeArgString("-fbounds-checking=" + val)); + } else + CmdArgs.push_back("-fbounds-checking=1"); } if (Args.hasArg(options::OPT__relocatable_pch)) @@ -2066,6 +2260,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fvisibility_inlines_hidden); + Args.AddLastArg(CmdArgs, options::OPT_ftlsmodel_EQ); + // -fhosted is default. if (Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false) || KernelOrKext) @@ -2079,6 +2275,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fno_limit_debug_info); Args.AddLastArg(CmdArgs, options::OPT_fno_operator_names); Args.AddLastArg(CmdArgs, options::OPT_faltivec); + Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); + Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type); // Report and error for -faltivec on anything other then PowerPC. if (const Arg *A = Args.getLastArg(options::OPT_faltivec)) @@ -2106,6 +2304,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.getLastArg(options::OPT_fapple_kext)) CmdArgs.push_back("-fapple-kext"); + if (Args.hasFlag(options::OPT_frewrite_includes, + options::OPT_fno_rewrite_includes, false)) + CmdArgs.push_back("-frewrite-includes"); + Args.AddLastArg(CmdArgs, options::OPT_fobjc_sender_dependent_dispatch); Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_print_source_range_info); Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits); @@ -2259,6 +2461,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, getToolChain().getTriple().getOS() == llvm::Triple::Win32)) CmdArgs.push_back("-fms-extensions"); + // -fms-inline-asm. + if (Args.hasArg(options::OPT_fenable_experimental_ms_inline_asm)) + CmdArgs.push_back("-fenable-experimental-ms-inline-asm"); + // -fms-compatibility=0 is default. if (Args.hasFlag(options::OPT_fms_compatibility, options::OPT_fno_ms_compatibility, @@ -2309,83 +2515,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasArg(options::OPT_fno_inline_functions)) CmdArgs.push_back("-fno-inline-functions"); - // -fobjc-nonfragile-abi=0 is default. - ObjCRuntime objCRuntime; - unsigned objcABIVersion = 0; - bool NeXTRuntimeIsDefault - = (IsRewriter || IsModernRewriter || - getToolChain().getTriple().isOSDarwin()); - if (Args.hasFlag(options::OPT_fnext_runtime, options::OPT_fgnu_runtime, - NeXTRuntimeIsDefault)) { - objCRuntime.setKind(ObjCRuntime::NeXT); - } else { - CmdArgs.push_back("-fgnu-runtime"); - objCRuntime.setKind(ObjCRuntime::GNU); - } - getToolChain().configureObjCRuntime(objCRuntime); - if (objCRuntime.HasARC) - CmdArgs.push_back("-fobjc-runtime-has-arc"); - if (objCRuntime.HasWeak) - CmdArgs.push_back("-fobjc-runtime-has-weak"); - if (objCRuntime.HasTerminate) - CmdArgs.push_back("-fobjc-runtime-has-terminate"); - - // Compute the Objective-C ABI "version" to use. Version numbers are - // slightly confusing for historical reasons: - // 1 - Traditional "fragile" ABI - // 2 - Non-fragile ABI, version 1 - // 3 - Non-fragile ABI, version 2 - objcABIVersion = 1; - // If -fobjc-abi-version= is present, use that to set the version. - if (Arg *A = Args.getLastArg(options::OPT_fobjc_abi_version_EQ)) { - if (StringRef(A->getValue(Args)) == "1") - objcABIVersion = 1; - else if (StringRef(A->getValue(Args)) == "2") - objcABIVersion = 2; - else if (StringRef(A->getValue(Args)) == "3") - objcABIVersion = 3; - else - D.Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args); - } else { - // Otherwise, determine if we are using the non-fragile ABI. - bool NonFragileABIIsDefault = - (IsModernRewriter || - (!IsRewriter && getToolChain().IsObjCNonFragileABIDefault())); - if (Args.hasFlag(options::OPT_fobjc_nonfragile_abi, - options::OPT_fno_objc_nonfragile_abi, - NonFragileABIIsDefault)) { - // Determine the non-fragile ABI version to use. -#ifdef DISABLE_DEFAULT_NONFRAGILEABI_TWO - unsigned NonFragileABIVersion = 1; -#else - unsigned NonFragileABIVersion = 2; -#endif - - if (Arg *A = Args.getLastArg( - options::OPT_fobjc_nonfragile_abi_version_EQ)) { - if (StringRef(A->getValue(Args)) == "1") - NonFragileABIVersion = 1; - else if (StringRef(A->getValue(Args)) == "2") - NonFragileABIVersion = 2; - else - D.Diag(diag::err_drv_clang_unsupported) - << A->getAsString(Args); - } - - objcABIVersion = 1 + NonFragileABIVersion; - } else { - objcABIVersion = 1; - } - } + ObjCRuntime objcRuntime = AddObjCRuntimeArgs(Args, CmdArgs, rewriteKind); - if (objcABIVersion == 1) { - CmdArgs.push_back("-fobjc-fragile-abi"); - } else { - // -fobjc-dispatch-method is only relevant with the nonfragile-abi, and - // legacy is the default. + // -fobjc-dispatch-method is only relevant with the nonfragile-abi, and + // legacy is the default. + if (objcRuntime.isNonFragile()) { if (!Args.hasFlag(options::OPT_fobjc_legacy_dispatch, options::OPT_fno_objc_legacy_dispatch, - getToolChain().IsObjCLegacyDispatchDefault())) { + objcRuntime.isLegacyDispatchDefaultForArch( + getToolChain().getTriple().getArch()))) { if (getToolChain().UseObjCMixedDispatch()) CmdArgs.push_back("-fobjc-dispatch-method=mixed"); else @@ -2428,7 +2566,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // -fobjc-infer-related-result-type is the default, except in the Objective-C // rewriter. - if (IsRewriter || IsModernRewriter) + if (rewriteKind != RK_None) CmdArgs.push_back("-fno-objc-infer-related-result-type"); // Handle -fobjc-gc and -fobjc-gc-only. They are exclusive, and -fobjc-gc-only @@ -2451,7 +2589,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Add exception args. addExceptionArgs(Args, InputType, getToolChain().getTriple(), - KernelOrKext, objcABIVersion, CmdArgs); + KernelOrKext, objcRuntime, CmdArgs); if (getToolChain().UseSjLjExceptions()) CmdArgs.push_back("-fsjlj-exceptions"); @@ -2490,12 +2628,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Honor -fpack-struct= and -fpack-struct, if given. Note that // -fno-pack-struct doesn't apply to -fpack-struct=. if (Arg *A = Args.getLastArg(options::OPT_fpack_struct_EQ)) { - CmdArgs.push_back("-fpack-struct"); - CmdArgs.push_back(A->getValue(Args)); + std::string PackStructStr = "-fpack-struct="; + PackStructStr += A->getValue(Args); + CmdArgs.push_back(Args.MakeArgString(PackStructStr)); } else if (Args.hasFlag(options::OPT_fpack_struct, options::OPT_fno_pack_struct, false)) { - CmdArgs.push_back("-fpack-struct"); - CmdArgs.push_back("1"); + CmdArgs.push_back("-fpack-struct=1"); } if (Args.hasArg(options::OPT_mkernel) || @@ -2729,7 +2867,7 @@ void ClangAs::AddARMTargetArgs(const ArgList &Args, // Set the CPU based on -march= and -mcpu=. CmdArgs.push_back("-target-cpu"); - CmdArgs.push_back(getARMTargetCPU(Args, Triple)); + CmdArgs.push_back(Args.MakeArgString(getARMTargetCPU(Args, Triple))); // Honor -mfpu=. if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) @@ -2740,6 +2878,131 @@ void ClangAs::AddARMTargetArgs(const ArgList &Args, addFPMathArgs(D, A, Args, CmdArgs, getARMTargetCPU(Args, Triple)); } +/// Add options related to the Objective-C runtime/ABI. +/// +/// Returns true if the runtime is non-fragile. +ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args, + ArgStringList &cmdArgs, + RewriteKind rewriteKind) const { + // Look for the controlling runtime option. + Arg *runtimeArg = args.getLastArg(options::OPT_fnext_runtime, + options::OPT_fgnu_runtime, + options::OPT_fobjc_runtime_EQ); + + // Just forward -fobjc-runtime= to the frontend. This supercedes + // options about fragility. + if (runtimeArg && + runtimeArg->getOption().matches(options::OPT_fobjc_runtime_EQ)) { + ObjCRuntime runtime; + StringRef value = runtimeArg->getValue(args); + if (runtime.tryParse(value)) { + getToolChain().getDriver().Diag(diag::err_drv_unknown_objc_runtime) + << value; + } + + runtimeArg->render(args, cmdArgs); + return runtime; + } + + // Otherwise, we'll need the ABI "version". Version numbers are + // slightly confusing for historical reasons: + // 1 - Traditional "fragile" ABI + // 2 - Non-fragile ABI, version 1 + // 3 - Non-fragile ABI, version 2 + unsigned objcABIVersion = 1; + // If -fobjc-abi-version= is present, use that to set the version. + if (Arg *abiArg = args.getLastArg(options::OPT_fobjc_abi_version_EQ)) { + StringRef value = abiArg->getValue(args); + if (value == "1") + objcABIVersion = 1; + else if (value == "2") + objcABIVersion = 2; + else if (value == "3") + objcABIVersion = 3; + else + getToolChain().getDriver().Diag(diag::err_drv_clang_unsupported) + << value; + } else { + // Otherwise, determine if we are using the non-fragile ABI. + bool nonFragileABIIsDefault = + (rewriteKind == RK_NonFragile || + (rewriteKind == RK_None && + getToolChain().IsObjCNonFragileABIDefault())); + if (args.hasFlag(options::OPT_fobjc_nonfragile_abi, + options::OPT_fno_objc_nonfragile_abi, + nonFragileABIIsDefault)) { + // Determine the non-fragile ABI version to use. +#ifdef DISABLE_DEFAULT_NONFRAGILEABI_TWO + unsigned nonFragileABIVersion = 1; +#else + unsigned nonFragileABIVersion = 2; +#endif + + if (Arg *abiArg = args.getLastArg( + options::OPT_fobjc_nonfragile_abi_version_EQ)) { + StringRef value = abiArg->getValue(args); + if (value == "1") + nonFragileABIVersion = 1; + else if (value == "2") + nonFragileABIVersion = 2; + else + getToolChain().getDriver().Diag(diag::err_drv_clang_unsupported) + << value; + } + + objcABIVersion = 1 + nonFragileABIVersion; + } else { + objcABIVersion = 1; + } + } + + // We don't actually care about the ABI version other than whether + // it's non-fragile. + bool isNonFragile = objcABIVersion != 1; + + // If we have no runtime argument, ask the toolchain for its default runtime. + // However, the rewriter only really supports the Mac runtime, so assume that. + ObjCRuntime runtime; + if (!runtimeArg) { + switch (rewriteKind) { + case RK_None: + runtime = getToolChain().getDefaultObjCRuntime(isNonFragile); + break; + case RK_Fragile: + runtime = ObjCRuntime(ObjCRuntime::FragileMacOSX, VersionTuple()); + break; + case RK_NonFragile: + runtime = ObjCRuntime(ObjCRuntime::MacOSX, VersionTuple()); + break; + } + + // -fnext-runtime + } else if (runtimeArg->getOption().matches(options::OPT_fnext_runtime)) { + // On Darwin, make this use the default behavior for the toolchain. + if (getToolChain().getTriple().isOSDarwin()) { + runtime = getToolChain().getDefaultObjCRuntime(isNonFragile); + + // Otherwise, build for a generic macosx port. + } else { + runtime = ObjCRuntime(ObjCRuntime::MacOSX, VersionTuple()); + } + + // -fgnu-runtime + } else { + assert(runtimeArg->getOption().matches(options::OPT_fgnu_runtime)); + // Legacy behaviour is to target the gnustep runtime if we are i + // non-fragile mode or the GCC runtime in fragile mode. + if (isNonFragile) + runtime = ObjCRuntime(ObjCRuntime::GNUstep, VersionTuple()); + else + runtime = ObjCRuntime(ObjCRuntime::GCC, VersionTuple()); + } + + cmdArgs.push_back(args.MakeArgString( + "-fobjc-runtime=" + runtime.getAsString())); + return runtime; +} + void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -4024,9 +4287,6 @@ void darwin::Link::AddLinkArgs(Compilation &C, } else if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { CmdArgs.push_back("-syslibroot"); CmdArgs.push_back(A->getValue(Args)); - } else if (getDarwinToolChain().isTargetIPhoneOS()) { - CmdArgs.push_back("-syslibroot"); - CmdArgs.push_back("/Developer/SDKs/Extra"); } Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace); @@ -4085,7 +4345,6 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgs(CmdArgs, options::OPT_t); Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); Args.AddAllArgs(CmdArgs, options::OPT_u_Group); - Args.AddAllArgs(CmdArgs, options::OPT_A); Args.AddLastArg(CmdArgs, options::OPT_e); Args.AddAllArgs(CmdArgs, options::OPT_m_Separate); Args.AddAllArgs(CmdArgs, options::OPT_r); @@ -4099,8 +4358,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); - if (!Args.hasArg(options::OPT_A) && - !Args.hasArg(options::OPT_nostdlib) && + if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { // Derived from startfile spec. if (Args.hasArg(options::OPT_dynamiclib)) { @@ -4144,6 +4402,14 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, // darwin_crt2 spec is empty. } + // By default on OS X 10.8 and later, we don't link with a crt1.o + // file and the linker knows to use _main as the entry point. But, + // when compiling with -pg, we need to link with the gcrt1.o file, + // so pass the -no_new_main option to tell the linker to use the + // "start" symbol as the entry point. + if (getDarwinToolChain().isTargetMacOS() && + !getDarwinToolChain().isMacosxVersionLT(10, 8)) + CmdArgs.push_back("-no_new_main"); } else { if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_object) || @@ -4201,30 +4467,30 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, // This is more complicated in gcc... CmdArgs.push_back("-lgomp"); - getDarwinToolChain().AddLinkSearchPathArgs(Args, CmdArgs); - - if (isObjCRuntimeLinked(Args)) { + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + + if (isObjCRuntimeLinked(Args) && + !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { // Avoid linking compatibility stubs on i386 mac. if (!getDarwinToolChain().isTargetMacOS() || getDarwinToolChain().getArchName() != "i386") { // If we don't have ARC or subscripting runtime support, link in the // runtime stubs. We have to do this *before* adding any of the normal // linker inputs so that its initializer gets run first. - ObjCRuntime runtime; - getDarwinToolChain().configureObjCRuntime(runtime); + ObjCRuntime runtime = + getDarwinToolChain().getDefaultObjCRuntime(/*nonfragile*/ true); // We use arclite library for both ARC and subscripting support. - if ((!runtime.HasARC && isObjCAutoRefCount(Args)) || - !runtime.HasSubscripting) + if ((!runtime.hasARC() && isObjCAutoRefCount(Args)) || + !runtime.hasSubscripting()) getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs); - CmdArgs.push_back("-framework"); - CmdArgs.push_back("Foundation"); } + CmdArgs.push_back("-framework"); + CmdArgs.push_back("Foundation"); // Link libobj. CmdArgs.push_back("-lobjc"); } - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); - if (LinkingOutput) { CmdArgs.push_back("-arch_multiple"); CmdArgs.push_back("-final_output"); @@ -4245,8 +4511,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, getDarwinToolChain().AddLinkRuntimeLibArgs(Args, CmdArgs); } - if (!Args.hasArg(options::OPT_A) && - !Args.hasArg(options::OPT_nostdlib) && + if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { // endfile_spec is empty. } @@ -4698,6 +4963,142 @@ void openbsd::Link::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } +void bitrig::Assemble::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, + options::OPT_Xassembler); + + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + CmdArgs.push_back(II.getFilename()); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath("as")); + C.addCommand(new Command(JA, *this, Exec, CmdArgs)); +} + +void bitrig::Link::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getDriver(); + ArgStringList CmdArgs; + + if ((!Args.hasArg(options::OPT_nostdlib)) && + (!Args.hasArg(options::OPT_shared))) { + CmdArgs.push_back("-e"); + CmdArgs.push_back("__start"); + } + + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-Bstatic"); + } else { + if (Args.hasArg(options::OPT_rdynamic)) + CmdArgs.push_back("-export-dynamic"); + CmdArgs.push_back("--eh-frame-hdr"); + CmdArgs.push_back("-Bdynamic"); + if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-shared"); + } else { + CmdArgs.push_back("-dynamic-linker"); + CmdArgs.push_back("/usr/libexec/ld.so"); + } + } + + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Invalid output."); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) { + if (Args.hasArg(options::OPT_pg)) + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("gcrt0.o"))); + else + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crt0.o"))); + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtbegin.o"))); + } else { + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtbeginS.o"))); + } + } + + Args.AddAllArgs(CmdArgs, options::OPT_L); + Args.AddAllArgs(CmdArgs, options::OPT_T_Group); + Args.AddAllArgs(CmdArgs, options::OPT_e); + + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + if (D.CCCIsCXX) { + getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); + if (Args.hasArg(options::OPT_pg)) + CmdArgs.push_back("-lm_p"); + else + CmdArgs.push_back("-lm"); + } + + if (Args.hasArg(options::OPT_pthread)) + CmdArgs.push_back("-lpthread"); + if (!Args.hasArg(options::OPT_shared)) { + if (Args.hasArg(options::OPT_pg)) + CmdArgs.push_back("-lc_p"); + else + CmdArgs.push_back("-lc"); + } + + std::string myarch = "-lclang_rt."; + const llvm::Triple &T = getToolChain().getTriple(); + llvm::Triple::ArchType Arch = T.getArch(); + switch (Arch) { + case llvm::Triple::arm: + myarch += ("arm"); + break; + case llvm::Triple::x86: + myarch += ("i386"); + break; + case llvm::Triple::x86_64: + myarch += ("amd64"); + break; + default: + assert(0 && "Unsupported architecture"); + } + CmdArgs.push_back(Args.MakeArgString(myarch)); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtend.o"))); + else + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtendS.o"))); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath("ld")); + C.addCommand(new Command(JA, *this, Exec, CmdArgs)); +} + void freebsd::Assemble::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -4744,6 +5145,14 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA, const Driver &D = getToolChain().getDriver(); ArgStringList CmdArgs; + // Silence warning for "clang -g foo.o -o foo" + Args.ClaimAllArgs(options::OPT_g_Group); + // and "clang -emit-llvm foo.o -o foo" + Args.ClaimAllArgs(options::OPT_emit_llvm); + // and for "clang -w foo.o -o foo". Other warning options are already + // handled somewhere else. + Args.ClaimAllArgs(options::OPT_w); + if (!D.SysRoot.empty()) CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); @@ -4759,6 +5168,14 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-dynamic-linker"); CmdArgs.push_back("/libexec/ld-elf.so.1"); } + if (getToolChain().getTriple().getOSMajorVersion() >= 9) { + llvm::Triple::ArchType Arch = getToolChain().getArch(); + if (Arch == llvm::Triple::arm || Arch == llvm::Triple::sparc || + Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) { + CmdArgs.push_back("--hash-style=both"); + } + } + CmdArgs.push_back("--enable-new-dtags"); } // When building 32-bit code on FreeBSD/amd64, we have to explicitly @@ -5064,6 +5481,14 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, StringRef MArch = getToolChain().getArchName(); if (MArch == "armv7" || MArch == "armv7a" || MArch == "armv7-a") CmdArgs.push_back("-mfpu=neon"); + + StringRef ARMFloatABI = getARMFloatABI(getToolChain().getDriver(), Args, + getToolChain().getTriple()); + CmdArgs.push_back(Args.MakeArgString("-mfloat-abi=" + ARMFloatABI)); + + Args.AddLastArg(CmdArgs, options::OPT_march_EQ); + Args.AddLastArg(CmdArgs, options::OPT_mcpu_EQ); + Args.AddLastArg(CmdArgs, options::OPT_mfpu_EQ); } else if (getToolChain().getArch() == llvm::Triple::mips || getToolChain().getArch() == llvm::Triple::mipsel || getToolChain().getArch() == llvm::Triple::mips64 || @@ -5089,11 +5514,19 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-EB"); else CmdArgs.push_back("-EL"); - } - Args.AddLastArg(CmdArgs, options::OPT_march_EQ); - Args.AddLastArg(CmdArgs, options::OPT_mcpu_EQ); - Args.AddLastArg(CmdArgs, options::OPT_mfpu_EQ); + Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC, + options::OPT_fpic, options::OPT_fno_pic, + options::OPT_fPIE, options::OPT_fno_PIE, + options::OPT_fpie, options::OPT_fno_pie); + if (LastPICArg && + (LastPICArg->getOption().matches(options::OPT_fPIC) || + LastPICArg->getOption().matches(options::OPT_fpic) || + LastPICArg->getOption().matches(options::OPT_fPIE) || + LastPICArg->getOption().matches(options::OPT_fpie))) { + CmdArgs.push_back("-KPIC"); + } + } Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); @@ -5112,9 +5545,10 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } -static void AddLibgcc(const Driver &D, ArgStringList &CmdArgs, - const ArgList &Args) { - bool StaticLibgcc = Args.hasArg(options::OPT_static) || +static void AddLibgcc(llvm::Triple Triple, const Driver &D, + ArgStringList &CmdArgs, const ArgList &Args) { + bool isAndroid = Triple.getEnvironment() == llvm::Triple::ANDROIDEABI; + bool StaticLibgcc = isAndroid || Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_static_libgcc); if (!D.CCCIsCXX) CmdArgs.push_back("-lgcc"); @@ -5130,7 +5564,7 @@ static void AddLibgcc(const Driver &D, ArgStringList &CmdArgs, CmdArgs.push_back("--no-as-needed"); } - if (StaticLibgcc) + if (StaticLibgcc && !isAndroid) CmdArgs.push_back("-lgcc_eh"); else if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX) CmdArgs.push_back("-lgcc"); @@ -5144,13 +5578,16 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, const toolchains::Linux& ToolChain = static_cast<const toolchains::Linux&>(getToolChain()); const Driver &D = ToolChain.getDriver(); + const bool isAndroid = ToolChain.getTriple().getEnvironment() == + llvm::Triple::ANDROIDEABI; + ArgStringList CmdArgs; // Silence warning for "clang -g foo.o -o foo" Args.ClaimAllArgs(options::OPT_g_Group); // and "clang -emit-llvm foo.o -o foo" Args.ClaimAllArgs(options::OPT_emit_llvm); - // and for "clang -g foo.o -o foo". Other warning options are already + // and for "clang -w foo.o -o foo". Other warning options are already // handled somewhere else. Args.ClaimAllArgs(options::OPT_w); @@ -5204,6 +5641,10 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-static"); } else if (Args.hasArg(options::OPT_shared)) { CmdArgs.push_back("-shared"); + if ((ToolChain.getArch() == llvm::Triple::arm + || ToolChain.getArch() == llvm::Triple::thumb) && isAndroid) { + CmdArgs.push_back("-Bsymbolic"); + } } if (ToolChain.getArch() == llvm::Triple::arm || @@ -5211,11 +5652,17 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, (!Args.hasArg(options::OPT_static) && !Args.hasArg(options::OPT_shared))) { CmdArgs.push_back("-dynamic-linker"); - if (ToolChain.getArch() == llvm::Triple::x86) + if (isAndroid) + CmdArgs.push_back("/system/bin/linker"); + else if (ToolChain.getArch() == llvm::Triple::x86) CmdArgs.push_back("/lib/ld-linux.so.2"); else if (ToolChain.getArch() == llvm::Triple::arm || - ToolChain.getArch() == llvm::Triple::thumb) - CmdArgs.push_back("/lib/ld-linux.so.3"); + ToolChain.getArch() == llvm::Triple::thumb) { + if (ToolChain.getTriple().getEnvironment() == llvm::Triple::GNUEABIHF) + CmdArgs.push_back("/lib/ld-linux-armhf.so.3"); + else + CmdArgs.push_back("/lib/ld-linux.so.3"); + } else if (ToolChain.getArch() == llvm::Triple::mips || ToolChain.getArch() == llvm::Triple::mipsel) CmdArgs.push_back("/lib/ld.so.1"); @@ -5235,25 +5682,27 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { - const char *crt1 = NULL; - if (!Args.hasArg(options::OPT_shared)){ - if (Args.hasArg(options::OPT_pie)) - crt1 = "Scrt1.o"; - else - crt1 = "crt1.o"; - } - if (crt1) - CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1))); + if (!isAndroid) { + const char *crt1 = NULL; + if (!Args.hasArg(options::OPT_shared)){ + if (Args.hasArg(options::OPT_pie)) + crt1 = "Scrt1.o"; + else + crt1 = "crt1.o"; + } + if (crt1) + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1))); - CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o"))); + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o"))); + } const char *crtbegin; if (Args.hasArg(options::OPT_static)) - crtbegin = "crtbeginT.o"; + crtbegin = isAndroid ? "crtbegin_static.o" : "crtbeginT.o"; else if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) - crtbegin = "crtbeginS.o"; + crtbegin = isAndroid ? "crtbegin_so.o" : "crtbeginS.o"; else - crtbegin = "crtbegin.o"; + crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbegin.o"; CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); } @@ -5276,7 +5725,9 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); - if (D.CCCIsCXX && !Args.hasArg(options::OPT_nostdlib)) { + if (D.CCCIsCXX && + !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) && !Args.hasArg(options::OPT_static); if (OnlyLibstdcxxStatic) @@ -5289,34 +5740,37 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, // Call this before we add the C run-time. addAsanRTLinux(getToolChain(), Args, CmdArgs); + addTsanRTLinux(getToolChain(), Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib)) { - if (Args.hasArg(options::OPT_static)) - CmdArgs.push_back("--start-group"); + if (!Args.hasArg(options::OPT_nodefaultlibs)) { + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("--start-group"); - AddLibgcc(D, CmdArgs, Args); + AddLibgcc(ToolChain.getTriple(), D, CmdArgs, Args); - if (Args.hasArg(options::OPT_pthread) || - Args.hasArg(options::OPT_pthreads)) - CmdArgs.push_back("-lpthread"); - - CmdArgs.push_back("-lc"); + if (Args.hasArg(options::OPT_pthread) || + Args.hasArg(options::OPT_pthreads)) + CmdArgs.push_back("-lpthread"); - if (Args.hasArg(options::OPT_static)) - CmdArgs.push_back("--end-group"); - else - AddLibgcc(D, CmdArgs, Args); + CmdArgs.push_back("-lc"); + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("--end-group"); + else + AddLibgcc(ToolChain.getTriple(), D, CmdArgs, Args); + } if (!Args.hasArg(options::OPT_nostartfiles)) { const char *crtend; if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) - crtend = "crtendS.o"; + crtend = isAndroid ? "crtend_so.o" : "crtendS.o"; else - crtend = "crtend.o"; + crtend = isAndroid ? "crtend_android.o" : "crtend.o"; CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtend))); - CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); + if (!isAndroid) + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); } } @@ -5581,7 +6035,14 @@ void visualstudio::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-nologo"); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + Args.AddAllArgValues(CmdArgs, options::OPT_l); + + // Add filenames immediately. + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + if (it->isFilename()) + CmdArgs.push_back(it->getFilename()); + } const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("link.exe")); diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 651a8f2963ce..999c57a2b63a 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -18,6 +18,8 @@ #include "llvm/Support/Compiler.h" namespace clang { + class ObjCRuntime; + namespace driver { class Driver; @@ -39,10 +41,16 @@ namespace tools { void AddARMTargetArgs(const ArgList &Args, ArgStringList &CmdArgs, bool KernelOrKext) const; void AddMIPSTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const; + void AddPPCTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const; void AddSparcTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const; void AddX86TargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const; void AddHexagonTargetArgs (const ArgList &Args, ArgStringList &CmdArgs) const; + enum RewriteKind { RK_None, RK_Fragile, RK_NonFragile }; + + ObjCRuntime AddObjCRuntimeArgs(const ArgList &args, ArgStringList &cmdArgs, + RewriteKind rewrite) const; + public: Clang(const ToolChain &TC) : Tool("clang", "clang frontend", TC) {} @@ -369,6 +377,36 @@ namespace openbsd { }; } // end namespace openbsd + /// bitrig -- Directly call GNU Binutils assembler and linker +namespace bitrig { + class LLVM_LIBRARY_VISIBILITY Assemble : public Tool { + public: + Assemble(const ToolChain &TC) : Tool("bitrig::Assemble", "assembler", + TC) {} + + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + class LLVM_LIBRARY_VISIBILITY Link : public Tool { + public: + Link(const ToolChain &TC) : Tool("bitrig::Link", "linker", TC) {} + + virtual bool hasIntegratedCPP() const { return false; } + virtual bool isLinkJob() const { return true; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; +} // end namespace bitrig + /// freebsd -- Directly call GNU Binutils assembler and linker namespace freebsd { class LLVM_LIBRARY_VISIBILITY Assemble : public Tool { diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp index 50742fe25e4e..9d8fcfd6bb44 100644 --- a/lib/Driver/Types.cpp +++ b/lib/Driver/Types.cpp @@ -67,7 +67,8 @@ bool types::appendSuffixForType(ID Id) { bool types::canLipoType(ID Id) { return (Id == TY_Nothing || Id == TY_Image || - Id == TY_Object); + Id == TY_Object || + Id == TY_LTO_BC); } bool types::isAcceptedByClang(ID Id) { @@ -129,6 +130,7 @@ bool types::isCXX(ID Id) { case TY_ObjCXX: case TY_PP_ObjCXX: case TY_CXXHeader: case TY_PP_CXXHeader: case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: + case TY_CUDA: return true; } } diff --git a/lib/Edit/CMakeLists.txt b/lib/Edit/CMakeLists.txt index c87478cf7d1d..cce1c197ae50 100644 --- a/lib/Edit/CMakeLists.txt +++ b/lib/Edit/CMakeLists.txt @@ -1,7 +1,20 @@ -set(LLVM_USED_LIBS clangBasic clangAST clangLex) - add_clang_library(clangEdit Commit.cpp EditedSource.cpp RewriteObjCFoundationAPI.cpp ) + +add_dependencies(clangEdit + ClangAttrClasses + ClangAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangStmtNodes + ) + +target_link_libraries(clangEdit + clangBasic + clangAST + clangLex + ) diff --git a/lib/Edit/Commit.cpp b/lib/Edit/Commit.cpp index c45ee1f850af..41c72e42e6a5 100644 --- a/lib/Edit/Commit.cpp +++ b/lib/Edit/Commit.cpp @@ -332,6 +332,7 @@ bool Commit::canReplaceText(SourceLocation loc, StringRef text, if (invalidTemp) return false; + Len = text.size(); return file.substr(Offs.getOffset()).startswith(text); } diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp index 5b7fa4ad1b67..b2a16635d050 100644 --- a/lib/Edit/EditedSource.cpp +++ b/lib/Edit/EditedSource.cpp @@ -100,8 +100,11 @@ bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc, FileOffset B = I->first; FileOffset E = B.getWithOffset(FA.RemoveLen); + if (BeginOffs == B) + break; + if (BeginOffs < E) { - if (BeginOffs >= B) { + if (BeginOffs > B) { BeginOffs = E; ++I; } diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp index 24a0db187557..d15b7a75e8f0 100644 --- a/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -14,6 +14,7 @@ #include "clang/Edit/Rewriters.h" #include "clang/Edit/Commit.h" #include "clang/Lex/Lexer.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/NSAPI.h" @@ -22,7 +23,8 @@ using namespace clang; using namespace edit; static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, - IdentifierInfo *&ClassId) { + IdentifierInfo *&ClassId, + const LangOptions &LangOpts) { if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl()) return false; @@ -34,6 +36,18 @@ static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, if (Msg->getReceiverKind() == ObjCMessageExpr::Class) return true; + // When in ARC mode we also convert "[[.. alloc] init]" messages to literals, + // since the change from +1 to +0 will be handled fine by ARC. + if (LangOpts.ObjCAutoRefCount) { + if (Msg->getReceiverKind() == ObjCMessageExpr::Instance) { + if (const ObjCMessageExpr *Rec = dyn_cast<ObjCMessageExpr>( + Msg->getInstanceReceiver()->IgnoreParenImpCasts())) { + if (Rec->getMethodFamily() == OMF_alloc) + return true; + } + } + } + return false; } @@ -44,7 +58,7 @@ static bool checkForLiteralCreation(const ObjCMessageExpr *Msg, bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, const NSAPI &NS, Commit &commit) { IdentifierInfo *II = 0; - if (!checkForLiteralCreation(Msg, II)) + if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) return false; if (Msg->getNumArgs() != 1) return false; @@ -54,16 +68,19 @@ bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, if ((isa<ObjCStringLiteral>(Arg) && NS.getNSClassId(NSAPI::ClassId_NSString) == II && - NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) || + (NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel || + NS.getNSStringSelector(NSAPI::NSStr_initWithString) == Sel)) || (isa<ObjCArrayLiteral>(Arg) && NS.getNSClassId(NSAPI::ClassId_NSArray) == II && - NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) || + (NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel || + NS.getNSArraySelector(NSAPI::NSArr_initWithArray) == Sel)) || (isa<ObjCDictionaryLiteral>(Arg) && NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II && - NS.getNSDictionarySelector( - NSAPI::NSDict_dictionaryWithDictionary) == Sel)) { + (NS.getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithDictionary) == Sel || + NS.getNSDictionarySelector(NSAPI::NSDict_initWithDictionary) == Sel))) { commit.replaceWithInner(Msg->getSourceRange(), Msg->getArg(0)->getSourceRange()); @@ -77,15 +94,91 @@ bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg, // rewriteToObjCSubscriptSyntax. //===----------------------------------------------------------------------===// +/// \brief Check for classes that accept 'objectForKey:' (or the other selectors +/// that the migrator handles) but return their instances as 'id', resulting +/// in the compiler resolving 'objectForKey:' as the method from NSDictionary. +/// +/// When checking if we can convert to subscripting syntax, check whether +/// the receiver is a result of a class method from a hardcoded list of +/// such classes. In such a case return the specific class as the interface +/// of the receiver. +/// +/// FIXME: Remove this when these classes start using 'instancetype'. +static const ObjCInterfaceDecl * +maybeAdjustInterfaceForSubscriptingCheck(const ObjCInterfaceDecl *IFace, + const Expr *Receiver, + ASTContext &Ctx) { + assert(IFace && Receiver); + + // If the receiver has type 'id'... + if (!Ctx.isObjCIdType(Receiver->getType().getUnqualifiedType())) + return IFace; + + const ObjCMessageExpr * + InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts()); + if (!InnerMsg) + return IFace; + + QualType ClassRec; + switch (InnerMsg->getReceiverKind()) { + case ObjCMessageExpr::Instance: + case ObjCMessageExpr::SuperInstance: + return IFace; + + case ObjCMessageExpr::Class: + ClassRec = InnerMsg->getClassReceiver(); + break; + case ObjCMessageExpr::SuperClass: + ClassRec = InnerMsg->getSuperType(); + break; + } + + if (ClassRec.isNull()) + return IFace; + + // ...and it is the result of a class message... + + const ObjCObjectType *ObjTy = ClassRec->getAs<ObjCObjectType>(); + if (!ObjTy) + return IFace; + const ObjCInterfaceDecl *OID = ObjTy->getInterface(); + + // ...and the receiving class is NSMapTable or NSLocale, return that + // class as the receiving interface. + if (OID->getName() == "NSMapTable" || + OID->getName() == "NSLocale") + return OID; + + return IFace; +} + +static bool canRewriteToSubscriptSyntax(const ObjCInterfaceDecl *&IFace, + const ObjCMessageExpr *Msg, + ASTContext &Ctx, + Selector subscriptSel) { + const Expr *Rec = Msg->getInstanceReceiver(); + if (!Rec) + return false; + IFace = maybeAdjustInterfaceForSubscriptingCheck(IFace, Rec, Ctx); + + if (const ObjCMethodDecl *MD = IFace->lookupInstanceMethod(subscriptSel)) { + if (!MD->isUnavailable()) + return true; + } + return false; +} + +static bool subscriptOperatorNeedsParens(const Expr *FullExpr); + static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) { - Receiver = Receiver->IgnoreImpCasts(); - if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) { + if (subscriptOperatorNeedsParens(Receiver)) { SourceRange RecRange = Receiver->getSourceRange(); commit.insertWrap("(", RecRange, ")"); } } -static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) { +static bool rewriteToSubscriptGetCommon(const ObjCMessageExpr *Msg, + Commit &commit) { if (Msg->getNumArgs() != 1) return false; const Expr *Rec = Msg->getInstanceReceiver(); @@ -106,8 +199,34 @@ static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) { return true; } -static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg, +static bool rewriteToArraySubscriptGet(const ObjCInterfaceDecl *IFace, + const ObjCMessageExpr *Msg, + const NSAPI &NS, + Commit &commit) { + if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), + NS.getObjectAtIndexedSubscriptSelector())) + return false; + return rewriteToSubscriptGetCommon(Msg, commit); +} + +static bool rewriteToDictionarySubscriptGet(const ObjCInterfaceDecl *IFace, + const ObjCMessageExpr *Msg, + const NSAPI &NS, + Commit &commit) { + if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), + NS.getObjectForKeyedSubscriptSelector())) + return false; + return rewriteToSubscriptGetCommon(Msg, commit); +} + +static bool rewriteToArraySubscriptSet(const ObjCInterfaceDecl *IFace, + const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), + NS.getSetObjectAtIndexedSubscriptSelector())) + return false; + if (Msg->getNumArgs() != 2) return false; const Expr *Rec = Msg->getInstanceReceiver(); @@ -134,8 +253,14 @@ static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg, return true; } -static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg, +static bool rewriteToDictionarySubscriptSet(const ObjCInterfaceDecl *IFace, + const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + if (!canRewriteToSubscriptSyntax(IFace, Msg, NS.getASTContext(), + NS.getSetObjectForKeyedSubscriptSelector())) + return false; + if (Msg->getNumArgs() != 2) return false; const Expr *Rec = Msg->getInstanceReceiver(); @@ -162,7 +287,7 @@ static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg, } bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, - const NSAPI &NS, Commit &commit) { + const NSAPI &NS, Commit &commit) { if (!Msg || Msg->isImplicit() || Msg->getReceiverKind() != ObjCMessageExpr::Instance) return false; @@ -175,25 +300,22 @@ bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg, const_cast<ObjCMethodDecl *>(Method)); if (!IFace) return false; - IdentifierInfo *II = IFace->getIdentifier(); Selector Sel = Msg->getSelector(); - if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) && - Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) || - (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) && - Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey))) - return rewriteToSubscriptGet(Msg, commit); + if (Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) + return rewriteToArraySubscriptGet(IFace, Msg, NS, commit); + + if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)) + return rewriteToDictionarySubscriptGet(IFace, Msg, NS, commit); if (Msg->getNumArgs() != 2) return false; - if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) && - Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) - return rewriteToArraySubscriptSet(Msg, commit); + if (Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex)) + return rewriteToArraySubscriptSet(IFace, Msg, NS, commit); - if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) && - Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) - return rewriteToDictionarySubscriptSet(Msg, commit); + if (Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey)) + return rewriteToDictionarySubscriptSet(IFace, Msg, NS, commit); return false; } @@ -208,11 +330,15 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, const NSAPI &NS, Commit &commit); static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, const NSAPI &NS, Commit &commit); +static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); +static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit); bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, const NSAPI &NS, Commit &commit) { IdentifierInfo *II = 0; - if (!checkForLiteralCreation(Msg, II)) + if (!checkForLiteralCreation(Msg, II, NS.getASTContext().getLangOpts())) return false; if (II == NS.getNSClassId(NSAPI::ClassId_NSArray)) @@ -221,6 +347,8 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, return rewriteToDictionaryLiteral(Msg, NS, commit); if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber)) return rewriteToNumberLiteral(Msg, NS, commit); + if (II == NS.getNSClassId(NSAPI::ClassId_NSString)) + return rewriteToStringBoxedExpression(Msg, NS, commit); return false; } @@ -229,6 +357,9 @@ bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg, // rewriteToArrayLiteral. //===----------------------------------------------------------------------===// +/// \brief Adds an explicit cast to 'id' if the type is not objc object. +static void objectifyExpr(const Expr *E, Commit &commit); + static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, const NSAPI &NS, Commit &commit) { Selector Sel = Msg->getSelector(); @@ -244,19 +375,24 @@ static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg, if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) { if (Msg->getNumArgs() != 1) return false; + objectifyExpr(Msg->getArg(0), commit); SourceRange ArgRange = Msg->getArg(0)->getSourceRange(); commit.replaceWithInner(MsgRange, ArgRange); commit.insertWrap("@[", ArgRange, "]"); return true; } - if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) { + if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects) || + Sel == NS.getNSArraySelector(NSAPI::NSArr_initWithObjects)) { if (Msg->getNumArgs() == 0) return false; const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1); if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr)) return false; + for (unsigned i = 0, e = Msg->getNumArgs() - 1; i != e; ++i) + objectifyExpr(Msg->getArg(i), commit); + if (Msg->getNumArgs() == 1) { commit.replace(MsgRange, "@[]"); return true; @@ -291,6 +427,10 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, NSAPI::NSDict_dictionaryWithObjectForKey)) { if (Msg->getNumArgs() != 2) return false; + + objectifyExpr(Msg->getArg(0), commit); + objectifyExpr(Msg->getArg(1), commit); + SourceRange ValRange = Msg->getArg(0)->getSourceRange(); SourceRange KeyRange = Msg->getArg(1)->getSourceRange(); // Insert key before the value. @@ -305,7 +445,8 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, } if (Sel == NS.getNSDictionarySelector( - NSAPI::NSDict_dictionaryWithObjectsAndKeys)) { + NSAPI::NSDict_dictionaryWithObjectsAndKeys) || + Sel == NS.getNSDictionarySelector(NSAPI::NSDict_initWithObjectsAndKeys)) { if (Msg->getNumArgs() % 2 != 1) return false; unsigned SentinelIdx = Msg->getNumArgs() - 1; @@ -319,6 +460,9 @@ static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg, } for (unsigned i = 0; i < SentinelIdx; i += 2) { + objectifyExpr(Msg->getArg(i), commit); + objectifyExpr(Msg->getArg(i+1), commit); + SourceRange ValRange = Msg->getArg(i)->getSourceRange(); SourceRange KeyRange = Msg->getArg(i+1)->getSourceRange(); // Insert value after key. @@ -357,7 +501,7 @@ static bool rewriteToCharLiteral(const ObjCMessageExpr *Msg, return true; } - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); } static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, @@ -371,7 +515,7 @@ static bool rewriteToBoolLiteral(const ObjCMessageExpr *Msg, return true; } - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); } namespace { @@ -473,10 +617,10 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, literalE = UOE->getSubExpr(); } - // Only integer and floating literals; non-literals or imaginary literal - // cannot be rewritten. + // Only integer and floating literals, otherwise try to rewrite to boxed + // expression. if (!isa<IntegerLiteral>(literalE) && !isa<FloatingLiteral>(literalE)) - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); ASTContext &Ctx = NS.getASTContext(); Selector Sel = Msg->getSelector(); @@ -496,7 +640,7 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, case NSAPI::NSNumberWithShort: case NSAPI::NSNumberWithUnsignedShort: case NSAPI::NSNumberWithBool: - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); case NSAPI::NSNumberWithUnsignedInt: case NSAPI::NSNumberWithUnsignedInteger: @@ -536,15 +680,16 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, } // We will need to modify the literal suffix to get the same type as the call. - // Don't even try if it came from a macro. + // Try with boxed expression if it came from a macro. if (ArgRange.getBegin().isMacroID()) - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); bool LitIsFloat = ArgTy->isFloatingType(); - // For a float passed to integer call, don't try rewriting. It is difficult - // and a very uncommon case anyway. + // For a float passed to integer call, don't try rewriting to objc literal. + // It is difficult and a very uncommon case anyway. + // But try with boxed expression. if (LitIsFloat && !CallIsFloating) - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); // Try to modify the literal make it the same type as the method call. // -Modify the suffix, and/or @@ -555,11 +700,11 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, if (const IntegerLiteral *IntE = dyn_cast<IntegerLiteral>(literalE)) isIntZero = !IntE->getValue().getBoolValue(); if (!getLiteralInfo(ArgRange, LitIsFloat, isIntZero, Ctx, LitInfo)) - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); // Not easy to do int -> float with hex/octal and uncommon anyway. if (!LitIsFloat && CallIsFloating && (LitInfo.Hex || LitInfo.Octal)) - return false; + return rewriteToNumericBoxedExpression(Msg, NS, commit); SourceLocation LitB = LitInfo.WithoutSuffRange.getBegin(); SourceLocation LitE = LitInfo.WithoutSuffRange.getEnd(); @@ -585,3 +730,284 @@ static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg, } return true; } + +// FIXME: Make determination of operator precedence more general and +// make it broadly available. +static bool subscriptOperatorNeedsParens(const Expr *FullExpr) { + const Expr* Expr = FullExpr->IgnoreImpCasts(); + if (isa<ArraySubscriptExpr>(Expr) || + isa<CallExpr>(Expr) || + isa<DeclRefExpr>(Expr) || + isa<CXXNamedCastExpr>(Expr) || + isa<CXXConstructExpr>(Expr) || + isa<CXXThisExpr>(Expr) || + isa<CXXTypeidExpr>(Expr) || + isa<CXXUnresolvedConstructExpr>(Expr) || + isa<ObjCMessageExpr>(Expr) || + isa<ObjCPropertyRefExpr>(Expr) || + isa<ObjCProtocolExpr>(Expr) || + isa<MemberExpr>(Expr) || + isa<ObjCIvarRefExpr>(Expr) || + isa<ParenExpr>(FullExpr) || + isa<ParenListExpr>(Expr) || + isa<SizeOfPackExpr>(Expr)) + return false; + + return true; +} +static bool castOperatorNeedsParens(const Expr *FullExpr) { + const Expr* Expr = FullExpr->IgnoreImpCasts(); + if (isa<ArraySubscriptExpr>(Expr) || + isa<CallExpr>(Expr) || + isa<DeclRefExpr>(Expr) || + isa<CastExpr>(Expr) || + isa<CXXNewExpr>(Expr) || + isa<CXXConstructExpr>(Expr) || + isa<CXXDeleteExpr>(Expr) || + isa<CXXNoexceptExpr>(Expr) || + isa<CXXPseudoDestructorExpr>(Expr) || + isa<CXXScalarValueInitExpr>(Expr) || + isa<CXXThisExpr>(Expr) || + isa<CXXTypeidExpr>(Expr) || + isa<CXXUnresolvedConstructExpr>(Expr) || + isa<ObjCMessageExpr>(Expr) || + isa<ObjCPropertyRefExpr>(Expr) || + isa<ObjCProtocolExpr>(Expr) || + isa<MemberExpr>(Expr) || + isa<ObjCIvarRefExpr>(Expr) || + isa<ParenExpr>(FullExpr) || + isa<ParenListExpr>(Expr) || + isa<SizeOfPackExpr>(Expr) || + isa<UnaryOperator>(Expr)) + return false; + + return true; +} + +static void objectifyExpr(const Expr *E, Commit &commit) { + if (!E) return; + + QualType T = E->getType(); + if (T->isObjCObjectPointerType()) { + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { + if (ICE->getCastKind() != CK_CPointerToObjCPointerCast) + return; + } else { + return; + } + } else if (!T->isPointerType()) { + return; + } + + SourceRange Range = E->getSourceRange(); + if (castOperatorNeedsParens(E)) + commit.insertWrap("(", Range, ")"); + commit.insertBefore(Range.getBegin(), "(id)"); +} + +//===----------------------------------------------------------------------===// +// rewriteToNumericBoxedExpression. +//===----------------------------------------------------------------------===// + +static bool isEnumConstant(const Expr *E) { + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) + if (const ValueDecl *VD = DRE->getDecl()) + return isa<EnumConstantDecl>(VD); + + return false; +} + +static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + if (Msg->getNumArgs() != 1) + return false; + + const Expr *Arg = Msg->getArg(0); + if (Arg->isTypeDependent()) + return false; + + ASTContext &Ctx = NS.getASTContext(); + Selector Sel = Msg->getSelector(); + llvm::Optional<NSAPI::NSNumberLiteralMethodKind> + MKOpt = NS.getNSNumberLiteralMethodKind(Sel); + if (!MKOpt) + return false; + NSAPI::NSNumberLiteralMethodKind MK = *MKOpt; + + const Expr *OrigArg = Arg->IgnoreImpCasts(); + QualType FinalTy = Arg->getType(); + QualType OrigTy = OrigArg->getType(); + uint64_t FinalTySize = Ctx.getTypeSize(FinalTy); + uint64_t OrigTySize = Ctx.getTypeSize(OrigTy); + + bool isTruncated = FinalTySize < OrigTySize; + bool needsCast = false; + + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { + switch (ICE->getCastKind()) { + case CK_LValueToRValue: + case CK_NoOp: + case CK_UserDefinedConversion: + break; + + case CK_IntegralCast: { + if (MK == NSAPI::NSNumberWithBool && OrigTy->isBooleanType()) + break; + // Be more liberal with Integer/UnsignedInteger which are very commonly + // used. + if ((MK == NSAPI::NSNumberWithInteger || + MK == NSAPI::NSNumberWithUnsignedInteger) && + !isTruncated) { + if (OrigTy->getAs<EnumType>() || isEnumConstant(OrigArg)) + break; + if ((MK==NSAPI::NSNumberWithInteger) == OrigTy->isSignedIntegerType() && + OrigTySize >= Ctx.getTypeSize(Ctx.IntTy)) + break; + } + + needsCast = true; + break; + } + + case CK_PointerToBoolean: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_AtomicToNonAtomic: + needsCast = true; + break; + + case CK_Dependent: + case CK_BitCast: + case CK_LValueBitCast: + case CK_BaseToDerived: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_Dynamic: + case CK_ToUnion: + case CK_ArrayToPointerDecay: + case CK_FunctionToPointerDecay: + case CK_NullToPointer: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_MemberPointerToBoolean: + case CK_ReinterpretMemberPointer: + case CK_ConstructorConversion: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_ToVoid: + case CK_VectorSplat: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: + case CK_FloatingRealToComplex: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralRealToComplex: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_ARCProduceObject: + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: + case CK_ARCExtendBlockObject: + case CK_NonAtomicToAtomic: + case CK_CopyAndAutoreleaseBlockObject: + return false; + } + } + + if (needsCast) { + DiagnosticsEngine &Diags = Ctx.getDiagnostics(); + // FIXME: Use a custom category name to distinguish migration diagnostics. + unsigned diagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "converting to boxing syntax requires casting %0 to %1"); + Diags.Report(Msg->getExprLoc(), diagID) << OrigTy << FinalTy + << Msg->getSourceRange(); + return false; + } + + SourceRange ArgRange = OrigArg->getSourceRange(); + commit.replaceWithInner(Msg->getSourceRange(), ArgRange); + + if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) + commit.insertBefore(ArgRange.getBegin(), "@"); + else + commit.insertWrap("@(", ArgRange, ")"); + + return true; +} + +//===----------------------------------------------------------------------===// +// rewriteToStringBoxedExpression. +//===----------------------------------------------------------------------===// + +static bool doRewriteToUTF8StringBoxedExpressionHelper( + const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + const Expr *Arg = Msg->getArg(0); + if (Arg->isTypeDependent()) + return false; + + ASTContext &Ctx = NS.getASTContext(); + + const Expr *OrigArg = Arg->IgnoreImpCasts(); + QualType OrigTy = OrigArg->getType(); + if (OrigTy->isArrayType()) + OrigTy = Ctx.getArrayDecayedType(OrigTy); + + if (const StringLiteral * + StrE = dyn_cast<StringLiteral>(OrigArg->IgnoreParens())) { + commit.replaceWithInner(Msg->getSourceRange(), StrE->getSourceRange()); + commit.insert(StrE->getLocStart(), "@"); + return true; + } + + if (const PointerType *PT = OrigTy->getAs<PointerType>()) { + QualType PointeeType = PT->getPointeeType(); + if (Ctx.hasSameUnqualifiedType(PointeeType, Ctx.CharTy)) { + SourceRange ArgRange = OrigArg->getSourceRange(); + commit.replaceWithInner(Msg->getSourceRange(), ArgRange); + + if (isa<ParenExpr>(OrigArg) || isa<IntegerLiteral>(OrigArg)) + commit.insertBefore(ArgRange.getBegin(), "@"); + else + commit.insertWrap("@(", ArgRange, ")"); + + return true; + } + } + + return false; +} + +static bool rewriteToStringBoxedExpression(const ObjCMessageExpr *Msg, + const NSAPI &NS, Commit &commit) { + Selector Sel = Msg->getSelector(); + + if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithUTF8String) || + Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCString)) { + if (Msg->getNumArgs() != 1) + return false; + return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); + } + + if (Sel == NS.getNSStringSelector(NSAPI::NSStr_stringWithCStringEncoding)) { + if (Msg->getNumArgs() != 2) + return false; + + const Expr *encodingArg = Msg->getArg(1); + if (NS.isNSUTF8StringEncodingConstant(encodingArg) || + NS.isNSASCIIStringEncodingConstant(encodingArg)) + return doRewriteToUTF8StringBoxedExpressionHelper(Msg, NS, commit); + } + + return false; +} diff --git a/lib/Frontend/ASTConsumers.cpp b/lib/Frontend/ASTConsumers.cpp index 390ae09497b0..bb1a4e664468 100644 --- a/lib/Frontend/ASTConsumers.cpp +++ b/lib/Frontend/ASTConsumers.cpp @@ -12,47 +12,116 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/ASTConsumers.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/FileManager.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/RecordLayout.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "llvm/Module.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Timer.h" using namespace clang; //===----------------------------------------------------------------------===// /// ASTPrinter - Pretty-printer and dumper of ASTs namespace { - class ASTPrinter : public ASTConsumer { + class ASTPrinter : public ASTConsumer, + public RecursiveASTVisitor<ASTPrinter> { + typedef RecursiveASTVisitor<ASTPrinter> base; + + public: + ASTPrinter(raw_ostream *Out = NULL, bool Dump = false, + StringRef FilterString = "") + : Out(Out ? *Out : llvm::outs()), Dump(Dump), + FilterString(FilterString) {} + + virtual void HandleTranslationUnit(ASTContext &Context) { + TranslationUnitDecl *D = Context.getTranslationUnitDecl(); + + if (FilterString.empty()) { + if (Dump) + D->dump(Out); + else + D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true); + return; + } + + TraverseDecl(D); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseDecl(Decl *D) { + if (filterMatches(D)) { + Out.changeColor(llvm::raw_ostream::BLUE) << + (Dump ? "Dumping " : "Printing ") << getName(D) << ":\n"; + Out.resetColor(); + if (Dump) + D->dump(Out); + else + D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true); + // Don't traverse child nodes to avoid output duplication. + return true; + } + return base::TraverseDecl(D); + } + + private: + std::string getName(Decl *D) { + if (isa<NamedDecl>(D)) + return cast<NamedDecl>(D)->getQualifiedNameAsString(); + return ""; + } + bool filterMatches(Decl *D) { + return getName(D).find(FilterString) != std::string::npos; + } + raw_ostream &Out; bool Dump; + std::string FilterString; + }; + + class ASTDeclNodeLister : public ASTConsumer, + public RecursiveASTVisitor<ASTDeclNodeLister> { + typedef RecursiveASTVisitor<ASTPrinter> base; public: - ASTPrinter(raw_ostream* o = NULL, bool Dump = false) - : Out(o? *o : llvm::outs()), Dump(Dump) { } + ASTDeclNodeLister(raw_ostream *Out = NULL) + : Out(Out ? *Out : llvm::outs()) {} virtual void HandleTranslationUnit(ASTContext &Context) { - PrintingPolicy Policy = Context.getPrintingPolicy(); - Policy.Dump = Dump; - Context.getTranslationUnitDecl()->print(Out, Policy, /*Indentation=*/0, - /*PrintInstantiation=*/true); + TraverseDecl(Context.getTranslationUnitDecl()); } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + virtual bool VisitNamedDecl(NamedDecl *D) { + Out << D->getQualifiedNameAsString() << "\n"; + return true; + } + + private: + raw_ostream &Out; }; } // end anonymous namespace -ASTConsumer *clang::CreateASTPrinter(raw_ostream* out) { - return new ASTPrinter(out); +ASTConsumer *clang::CreateASTPrinter(raw_ostream *Out, + StringRef FilterString) { + return new ASTPrinter(Out, /*Dump=*/ false, FilterString); +} + +ASTConsumer *clang::CreateASTDumper(StringRef FilterString) { + return new ASTPrinter(0, /*Dump=*/ true, FilterString); } -ASTConsumer *clang::CreateASTDumper() { - return new ASTPrinter(0, true); +ASTConsumer *clang::CreateASTDeclNodeLister() { + return new ASTDeclNodeLister(0); } //===----------------------------------------------------------------------===// diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 7aa96032ee36..42a67720c353 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -17,12 +17,6 @@ #include "clang/AST/DeclVisitor.h" #include "clang/AST/TypeOrdering.h" #include "clang/AST/StmtVisitor.h" -#include "clang/Driver/Compilation.h" -#include "clang/Driver/Driver.h" -#include "clang/Driver/Job.h" -#include "clang/Driver/ArgList.h" -#include "clang/Driver/Options.h" -#include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -122,7 +116,8 @@ static OnDiskDataMap &getOnDiskDataMap() { } static void cleanupOnDiskMapAtExit(void) { - // No mutex required here since we are leaving the program. + // Use the mutex because there can be an alive thread destroying an ASTUnit. + llvm::MutexGuard Guard(getOnDiskMutex()); OnDiskDataMap &M = getOnDiskDataMap(); for (OnDiskDataMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { // We don't worry about freeing the memory associated with OnDiskDataMap. @@ -220,6 +215,7 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), NumWarningsInPreamble(0), ShouldCacheCodeCompletionResults(false), + IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), CompletionCacheTopLevelHashValue(0), PreambleTopLevelHashValue(0), CurrentTopLevelHashValue(0), @@ -275,43 +271,43 @@ static unsigned getDeclShowContexts(NamedDecl *ND, if (!ND) return 0; - unsigned Contexts = 0; + uint64_t Contexts = 0; if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND) || isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND)) { // Types can appear in these contexts. if (LangOpts.CPlusPlus || !isa<TagDecl>(ND)) - Contexts |= (1 << (CodeCompletionContext::CCC_TopLevel - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) - | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) - | (1 << (CodeCompletionContext::CCC_Statement - 1)) - | (1 << (CodeCompletionContext::CCC_Type - 1)) - | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); // In C++, types can appear in expressions contexts (for functional casts). if (LangOpts.CPlusPlus) - Contexts |= (1 << (CodeCompletionContext::CCC_Expression - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_Expression); // In Objective-C, message sends can send interfaces. In Objective-C++, // all types are available due to functional casts. if (LangOpts.CPlusPlus || isa<ObjCInterfaceDecl>(ND)) - Contexts |= (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); // In Objective-C, you can only be a subclass of another Objective-C class if (isa<ObjCInterfaceDecl>(ND)) - Contexts |= (1 << (CodeCompletionContext::CCC_ObjCInterfaceName - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName); // Deal with tag names. if (isa<EnumDecl>(ND)) { - Contexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_EnumTag); // Part of the nested-name-specifier in C++0x. if (LangOpts.CPlusPlus0x) IsNestedNameSpecifier = true; } else if (RecordDecl *Record = dyn_cast<RecordDecl>(ND)) { if (Record->isUnion()) - Contexts |= (1 << (CodeCompletionContext::CCC_UnionTag - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_UnionTag); else - Contexts |= (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); + Contexts |= (1LL << CodeCompletionContext::CCC_ClassOrStructTag); if (LangOpts.CPlusPlus) IsNestedNameSpecifier = true; @@ -319,16 +315,16 @@ static unsigned getDeclShowContexts(NamedDecl *ND, IsNestedNameSpecifier = true; } else if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) { // Values can appear in these contexts. - Contexts = (1 << (CodeCompletionContext::CCC_Statement - 1)) - | (1 << (CodeCompletionContext::CCC_Expression - 1)) - | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); + Contexts = (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); } else if (isa<ObjCProtocolDecl>(ND)) { - Contexts = (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1)); + Contexts = (1LL << CodeCompletionContext::CCC_ObjCProtocolName); } else if (isa<ObjCCategoryDecl>(ND)) { - Contexts = (1 << (CodeCompletionContext::CCC_ObjCCategoryName - 1)); + Contexts = (1LL << CodeCompletionContext::CCC_ObjCCategoryName); } else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) { - Contexts = (1 << (CodeCompletionContext::CCC_Namespace - 1)); + Contexts = (1LL << CodeCompletionContext::CCC_Namespace); // Part of the nested-name-specifier. IsNestedNameSpecifier = true; @@ -364,7 +360,8 @@ void ASTUnit::CacheCodeCompletionResults() { CachedCodeCompletionResult CachedResult; CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, *CachedCompletionAllocator, - getCodeCompletionTUInfo()); + getCodeCompletionTUInfo(), + IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration, Ctx->getLangOpts(), IsNestedNameSpecifier); @@ -402,23 +399,23 @@ void ASTUnit::CacheCodeCompletionResults() { if (TheSema->Context.getLangOpts().CPlusPlus && IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) { // The contexts in which a nested-name-specifier can appear in C++. - unsigned NNSContexts - = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) - | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) - | (1 << (CodeCompletionContext::CCC_Statement - 1)) - | (1 << (CodeCompletionContext::CCC_Expression - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) - | (1 << (CodeCompletionContext::CCC_EnumTag - 1)) - | (1 << (CodeCompletionContext::CCC_UnionTag - 1)) - | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)) - | (1 << (CodeCompletionContext::CCC_Type - 1)) - | (1 << (CodeCompletionContext::CCC_PotentiallyQualifiedName - 1)) - | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)); + uint64_t NNSContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_PotentiallyQualifiedName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); if (isa<NamespaceDecl>(Results[I].Declaration) || isa<NamespaceAliasDecl>(Results[I].Declaration)) - NNSContexts |= (1 << (CodeCompletionContext::CCC_Namespace - 1)); + NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace); if (unsigned RemainingContexts = NNSContexts & ~CachedResult.ShowInContexts) { @@ -429,7 +426,8 @@ void ASTUnit::CacheCodeCompletionResults() { CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, *CachedCompletionAllocator, - getCodeCompletionTUInfo()); + getCodeCompletionTUInfo(), + IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts = RemainingContexts; CachedResult.Priority = CCP_NestedNameSpecifier; CachedResult.TypeClass = STC_Void; @@ -451,20 +449,21 @@ void ASTUnit::CacheCodeCompletionResults() { CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, *CachedCompletionAllocator, - getCodeCompletionTUInfo()); + getCodeCompletionTUInfo(), + IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts - = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) - | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) - | (1 << (CodeCompletionContext::CCC_Statement - 1)) - | (1 << (CodeCompletionContext::CCC_Expression - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) - | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1)) - | (1 << (CodeCompletionContext::CCC_PreprocessorExpression - 1)) - | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) - | (1 << (CodeCompletionContext::CCC_OtherWithMacros - 1)); + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_MacroNameUse) + | (1LL << CodeCompletionContext::CCC_PreprocessorExpression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_OtherWithMacros); CachedResult.Priority = Results[I].Priority; CachedResult.Kind = Results[I].CursorKind; @@ -659,7 +658,8 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, RemappedFile *RemappedFiles, unsigned NumRemappedFiles, bool CaptureDiagnostics, - bool AllowPCHWithCompilerErrors) { + bool AllowPCHWithCompilerErrors, + bool UserFilesAreVolatile) { OwningPtr<ASTUnit> AST(new ASTUnit(true)); // Recover resources if we crash before exiting this method. @@ -675,8 +675,10 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, AST->CaptureDiagnostics = CaptureDiagnostics; AST->Diagnostics = Diags; AST->FileMgr = new FileManager(FileSystemOpts); + AST->UserFilesAreVolatile = UserFilesAreVolatile; AST->SourceMgr = new SourceManager(AST->getDiagnostics(), - AST->getFileManager()); + AST->getFileManager(), + UserFilesAreVolatile); AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager(), AST->getDiagnostics(), AST->ASTFileLangOpts, @@ -1078,7 +1080,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { LangOpts = &Clang->getLangOpts(); FileSystemOpts = Clang->getFileSystemOpts(); FileMgr = new FileManager(FileSystemOpts); - SourceMgr = new SourceManager(getDiagnostics(), *FileMgr); + SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, + UserFilesAreVolatile); TheSema.reset(); Ctx = 0; PP = 0; @@ -1139,7 +1142,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { StoredDiagnostics); } - Act->Execute(); + if (!Act->Execute()) + goto error; transferASTDataFromCompilerInstance(*Clang); @@ -1665,7 +1669,8 @@ StringRef ASTUnit::getMainFileName() const { ASTUnit *ASTUnit::create(CompilerInvocation *CI, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, - bool CaptureDiagnostics) { + bool CaptureDiagnostics, + bool UserFilesAreVolatile) { OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); @@ -1673,7 +1678,9 @@ ASTUnit *ASTUnit::create(CompilerInvocation *CI, AST->Invocation = CI; AST->FileSystemOpts = CI->getFileSystemOpts(); AST->FileMgr = new FileManager(AST->FileSystemOpts); - AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr, + UserFilesAreVolatile); return AST.take(); } @@ -1688,6 +1695,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, bool CaptureDiagnostics, bool PrecompilePreamble, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, + bool UserFilesAreVolatile, OwningPtr<ASTUnit> *ErrAST) { assert(CI && "A CompilerInvocation is required"); @@ -1695,7 +1704,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, ASTUnit *AST = Unit; if (!AST) { // Create the AST unit. - OwnAST.reset(create(CI, Diags, CaptureDiagnostics)); + OwnAST.reset(create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile)); AST = OwnAST.get(); } @@ -1709,6 +1718,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, AST->PreambleRebuildCounter = 2; AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1801,7 +1812,13 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, AST->getCurrentTopLevelHashValue())); Clang->setASTConsumer(new MultiplexConsumer(Consumers)); } - Act->Execute(); + if (!Act->Execute()) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return 0; + } // Steal the created target, context, and preprocessor. AST->transferASTDataFromCompilerInstance(*Clang); @@ -1849,7 +1866,9 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, bool CaptureDiagnostics, bool PrecompilePreamble, TranslationUnitKind TUKind, - bool CacheCodeCompletionResults) { + bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, + bool UserFilesAreVolatile) { // Create the AST unit. OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); @@ -1859,7 +1878,10 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->CaptureDiagnostics = CaptureDiagnostics; AST->TUKind = TUKind; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; AST->Invocation = CI; + AST->UserFilesAreVolatile = UserFilesAreVolatile; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1883,8 +1905,10 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, bool PrecompilePreamble, TranslationUnitKind TUKind, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, bool SkipFunctionBodies, + bool UserFilesAreVolatile, OwningPtr<ASTUnit> *ErrAST) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object @@ -1942,6 +1966,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, AST->CaptureDiagnostics = CaptureDiagnostics; AST->TUKind = TUKind; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->UserFilesAreVolatile = UserFilesAreVolatile; AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); AST->Invocation = CI; @@ -2034,38 +2061,37 @@ namespace { /// results from an ASTUnit with the code-completion results provided to it, /// then passes the result on to class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer { - unsigned long long NormalContexts; + uint64_t NormalContexts; ASTUnit &AST; CodeCompleteConsumer &Next; public: AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next, - bool IncludeMacros, bool IncludeCodePatterns, - bool IncludeGlobals) - : CodeCompleteConsumer(IncludeMacros, IncludeCodePatterns, IncludeGlobals, - Next.isOutputBinary()), AST(AST), Next(Next) + const CodeCompleteOptions &CodeCompleteOpts) + : CodeCompleteConsumer(CodeCompleteOpts, Next.isOutputBinary()), + AST(AST), Next(Next) { // Compute the set of contexts in which we will look when we don't have // any information about the specific context. NormalContexts - = (1LL << (CodeCompletionContext::CCC_TopLevel - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCInterface - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCImplementation - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCIvarList - 1)) - | (1LL << (CodeCompletionContext::CCC_Statement - 1)) - | (1LL << (CodeCompletionContext::CCC_Expression - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) - | (1LL << (CodeCompletionContext::CCC_DotMemberAccess - 1)) - | (1LL << (CodeCompletionContext::CCC_ArrowMemberAccess - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCPropertyAccess - 1)) - | (1LL << (CodeCompletionContext::CCC_ObjCProtocolName - 1)) - | (1LL << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) - | (1LL << (CodeCompletionContext::CCC_Recovery - 1)); + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_DotMemberAccess) + | (1LL << CodeCompletionContext::CCC_ArrowMemberAccess) + | (1LL << CodeCompletionContext::CCC_ObjCPropertyAccess) + | (1LL << CodeCompletionContext::CCC_ObjCProtocolName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_Recovery); if (AST.getASTContext().getLangOpts().CPlusPlus) - NormalContexts |= (1LL << (CodeCompletionContext::CCC_EnumTag - 1)) - | (1LL << (CodeCompletionContext::CCC_UnionTag - 1)) - | (1LL << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); + NormalContexts |= (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag); } virtual void ProcessCodeCompleteResults(Sema &S, @@ -2180,9 +2206,9 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, unsigned NumResults) { // Merge the results we were given with the results we cached. bool AddedResult = false; - unsigned InContexts - = (Context.getKind() == CodeCompletionContext::CCC_Recovery? NormalContexts - : (1ULL << (Context.getKind() - 1))); + uint64_t InContexts = + Context.getKind() == CodeCompletionContext::CCC_Recovery + ? NormalContexts : (1LL << Context.getKind()); // Contains the set of names that are hidden by "local" completion results. llvm::StringSet<llvm::BumpPtrAllocator> HiddenNames; typedef CodeCompletionResult Result; @@ -2273,6 +2299,7 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, unsigned NumRemappedFiles, bool IncludeMacros, bool IncludeCodePatterns, + bool IncludeBriefComments, CodeCompleteConsumer &Consumer, DiagnosticsEngine &Diag, LangOptions &LangOpts, SourceManager &SourceMgr, FileManager &FileMgr, @@ -2289,13 +2316,17 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, CCInvocation(new CompilerInvocation(*Invocation)); FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); + CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts; PreprocessorOptions &PreprocessorOpts = CCInvocation->getPreprocessorOpts(); - FrontendOpts.ShowMacrosInCodeCompletion - = IncludeMacros && CachedCompletionResults.empty(); - FrontendOpts.ShowCodePatternsInCodeCompletion = IncludeCodePatterns; - FrontendOpts.ShowGlobalSymbolsInCodeCompletion - = CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeMacros = IncludeMacros && + CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns; + CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments; + + assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); + FrontendOpts.CodeCompletionAt.FileName = File; FrontendOpts.CodeCompletionAt.Line = Line; FrontendOpts.CodeCompletionAt.Column = Column; @@ -2364,10 +2395,7 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, // Use the code completion consumer we were given, but adding any cached // code-completion results. AugmentedCodeCompleteConsumer *AugmentedConsumer - = new AugmentedCodeCompleteConsumer(*this, Consumer, - FrontendOpts.ShowMacrosInCodeCompletion, - FrontendOpts.ShowCodePatternsInCodeCompletion, - FrontendOpts.ShowGlobalSymbolsInCodeCompletion); + = new AugmentedCodeCompleteConsumer(*this, Consumer, CodeCompleteOpts); Clang->setCodeCompletionConsumer(AugmentedConsumer); Clang->getFrontendOpts().SkipFunctionBodies = true; diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 2bee240a007c..0566d54f36a7 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -1,14 +1,3 @@ -set( LLVM_USED_LIBS - clangAST - clangBasic - clangDriver - clangEdit - clangLex - clangParse - clangSema - clangSerialization - ) - add_clang_library(clangFrontend ASTConsumers.cpp ASTMerge.cpp @@ -41,21 +30,29 @@ add_clang_library(clangFrontend Warnings.cpp ) -IF(MSVC) - get_target_property(NON_ANSI_COMPILE_FLAGS clangFrontend COMPILE_FLAGS) - string(REPLACE /Za - "" NON_ANSI_COMPILE_FLAGS - ${NON_ANSI_COMPILE_FLAGS}) - set_target_properties(clangFrontend PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) -ENDIF(MSVC) - -add_dependencies(clangFrontend +add_dependencies(clangFrontend ClangAttrClasses ClangAttrList - ClangCC1Options - ClangDiagnosticFrontend + ClangAttrParsedAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticAST + ClangDiagnosticCommon + ClangDiagnosticDriver + ClangDiagnosticFrontend ClangDiagnosticLex ClangDiagnosticSema ClangDriverOptions - ClangDeclNodes - ClangStmtNodes) + ClangStmtNodes + ) + +target_link_libraries(clangFrontend + clangAST + clangBasic + clangDriver + clangEdit + clangLex + clangParse + clangSema + clangSerialization + ) diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 803e418e2413..6de153107f3b 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -387,9 +387,7 @@ void CompilerInstance::createCodeCompletionConsumer() { setCodeCompletionConsumer( createCodeCompletionConsumer(getPreprocessor(), Loc.FileName, Loc.Line, Loc.Column, - getFrontendOpts().ShowMacrosInCodeCompletion, - getFrontendOpts().ShowCodePatternsInCodeCompletion, - getFrontendOpts().ShowGlobalSymbolsInCodeCompletion, + getFrontendOpts().CodeCompleteOpts, llvm::outs())); if (!CompletionConsumer) return; @@ -415,16 +413,13 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, const std::string &Filename, unsigned Line, unsigned Column, - bool ShowMacros, - bool ShowCodePatterns, - bool ShowGlobals, + const CodeCompleteOptions &Opts, raw_ostream &OS) { if (EnableCodeCompletion(PP, Filename, Line, Column)) return 0; // Set up the creation routine for code-completion. - return new PrintingCodeCompleteConsumer(ShowMacros, ShowCodePatterns, - ShowGlobals, OS); + return new PrintingCodeCompleteConsumer(Opts, OS); } void CompilerInstance::createSema(TranslationUnitKind TUKind, @@ -456,7 +451,7 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) { FileMgr->FixupRelativePath(NewOutFile); if (llvm::error_code ec = llvm::sys::fs::rename(it->TempFilename, NewOutFile.str())) { - getDiagnostics().Report(diag::err_fe_unable_to_rename_temp) + getDiagnostics().Report(diag::err_unable_to_rename_temp) << it->TempFilename << it->Filename << ec.message(); bool existed; @@ -560,7 +555,8 @@ CompilerInstance::createOutputFile(StringRef OutputPath, TempPath += "-%%%%%%%%"; int fd; if (llvm::sys::fs::unique_file(TempPath.str(), fd, TempPath, - /*makeAbsolute=*/false) == llvm::errc::success) { + /*makeAbsolute=*/false, 0664) + == llvm::errc::success) { OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); OSFile = TempFile = TempPath.str(); } @@ -859,13 +855,6 @@ Module *CompilerInstance::loadModule(SourceLocation ImportLoc, } // Determine what file we're searching from. - SourceManager &SourceMgr = getSourceManager(); - SourceLocation ExpandedImportLoc = SourceMgr.getExpansionLoc(ImportLoc); - const FileEntry *CurFile - = SourceMgr.getFileEntryForID(SourceMgr.getFileID(ExpandedImportLoc)); - if (!CurFile) - CurFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); - StringRef ModuleName = Path[0].first->getName(); SourceLocation ModuleNameLoc = Path[0].second; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 4c5b063f7d41..d39679caf13e 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -13,7 +13,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Driver/Arg.h" #include "clang/Driver/ArgList.h" -#include "clang/Driver/CC1Options.h" +#include "clang/Driver/Options.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/OptTable.h" #include "clang/Driver/Option.h" @@ -181,8 +181,21 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, ToArgsList &Res) { } static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { - if (Opts.DebugInfo) - Res.push_back("-g"); + switch (Opts.DebugInfo) { + case CodeGenOptions::NoDebugInfo: + break; + case CodeGenOptions::DebugLineTablesOnly: + Res.push_back("-gline-tables-only"); + break; + case CodeGenOptions::LimitedDebugInfo: + Res.push_back("-g"); + Res.push_back("-flimit-debug-info"); + break; + case CodeGenOptions::FullDebugInfo: + Res.push_back("-g"); + Res.push_back("-fno-limit-debug-info"); + break; + } if (Opts.DisableLLVMOpts) Res.push_back("-disable-llvm-optzns"); if (Opts.DisableRedZone) @@ -193,14 +206,12 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { Res.push_back("-fdebug-compilation-dir", Opts.DebugCompilationDir); if (!Opts.DwarfDebugFlags.empty()) Res.push_back("-dwarf-debug-flags", Opts.DwarfDebugFlags); - if (Opts.ObjCRuntimeHasARC) - Res.push_back("-fobjc-runtime-has-arc"); - if (Opts.ObjCRuntimeHasTerminate) - Res.push_back("-fobjc-runtime-has-terminate"); if (Opts.EmitGcovArcs) Res.push_back("-femit-coverage-data"); if (Opts.EmitGcovNotes) Res.push_back("-femit-coverage-notes"); + if (Opts.EmitOpenCLArgMetadata) + Res.push_back("-cl-kernel-arg-info"); if (!Opts.MergeAllConstants) Res.push_back("-fno-merge-all-constants"); if (Opts.NoCommon) @@ -270,6 +281,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { Res.push_back("-fobjc-dispatch-method=non-legacy"); break; } + if (Opts.BoundsChecking > 0) + Res.push_back("-fbounds-checking=" + llvm::utostr(Opts.BoundsChecking)); if (Opts.NumRegisterParameters) Res.push_back("-mregparm", llvm::utostr(Opts.NumRegisterParameters)); if (Opts.NoGlobalMerge) @@ -296,6 +309,20 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { Res.push_back("-disable-llvm-verifier"); for (unsigned i = 0, e = Opts.BackendOptions.size(); i != e; ++i) Res.push_back("-backend-option", Opts.BackendOptions[i]); + + switch (Opts.DefaultTLSModel) { + case CodeGenOptions::GeneralDynamicTLSModel: + break; + case CodeGenOptions::LocalDynamicTLSModel: + Res.push_back("-ftls-model=local-dynamic"); + break; + case CodeGenOptions::InitialExecTLSModel: + Res.push_back("-ftls-model=initial-exec"); + break; + case CodeGenOptions::LocalExecTLSModel: + Res.push_back("-ftls-model=local-exec"); + break; + } } static void DependencyOutputOptsToArgs(const DependencyOutputOptions &Opts, @@ -407,6 +434,7 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::PluginAction: llvm_unreachable("Invalid kind!"); + case frontend::ASTDeclList: return "-ast-list"; case frontend::ASTDump: return "-ast-dump"; case frontend::ASTDumpXML: return "-ast-dump-xml"; case frontend::ASTPrint: return "-ast-print"; @@ -445,6 +473,18 @@ static void FileSystemOptsToArgs(const FileSystemOptions &Opts, ToArgsList &Res) Res.push_back("-working-directory", Opts.WorkingDir); } +static void CodeCompleteOptionsToArgs(const CodeCompleteOptions &Opts, + ToArgsList &Res) { + if (Opts.IncludeMacros) + Res.push_back("-code-completion-macros"); + if (Opts.IncludeCodePatterns) + Res.push_back("-code-completion-patterns"); + if (!Opts.IncludeGlobals) + Res.push_back("-no-code-completion-globals"); + if (Opts.IncludeBriefComments) + Res.push_back("-code-completion-brief-comments"); +} + static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) { if (Opts.DisableFree) Res.push_back("-disable-free"); @@ -452,12 +492,6 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) { Res.push_back("-relocatable-pch"); if (Opts.ShowHelp) Res.push_back("-help"); - if (Opts.ShowMacrosInCodeCompletion) - Res.push_back("-code-completion-macros"); - if (Opts.ShowCodePatternsInCodeCompletion) - Res.push_back("-code-completion-patterns"); - if (!Opts.ShowGlobalSymbolsInCodeCompletion) - Res.push_back("-no-code-completion-globals"); if (Opts.ShowStats) Res.push_back("-print-stats"); if (Opts.ShowTimers) @@ -485,6 +519,7 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) { Res.push_back("-arcmt-migrate"); break; } + CodeCompleteOptionsToArgs(Opts.CodeCompleteOpts, Res); if (!Opts.MTMigrateDir.empty()) Res.push_back("-mt-migrate-directory", Opts.MTMigrateDir); if (!Opts.ARCMTMigrateReportOut.empty()) @@ -524,6 +559,8 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) { for(unsigned i = 0, e = Opts.PluginArgs.size(); i != e; ++i) Res.push_back("-plugin-arg-" + Opts.ActionName, Opts.PluginArgs[i]); } + if (!Opts.ASTDumpFilter.empty()) + Res.push_back("-ast-dump-filter", Opts.ASTDumpFilter); for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i) Res.push_back("-load", Opts.Plugins[i]); for (unsigned i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) { @@ -608,6 +645,16 @@ static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, Res.push_back(E.Path); } + /// User-specified system header prefixes. + for (unsigned i = 0, e = Opts.SystemHeaderPrefixes.size(); i != e; ++i) { + if (Opts.SystemHeaderPrefixes[i].IsSystemHeader) + Res.push_back("-isystem-prefix"); + else + Res.push_back("-ino-system-prefix"); + + Res.push_back(Opts.SystemHeaderPrefixes[i].Prefix); + } + if (!Opts.ResourceDir.empty()) Res.push_back("-resource-dir", Opts.ResourceDir); if (!Opts.ModuleCachePath.empty()) @@ -653,8 +700,6 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { Res.push_back("-fmsc-version=" + llvm::utostr(Opts.MSCVersion)); if (Opts.Borland) Res.push_back("-fborland-extensions"); - if (!Opts.ObjCNonFragileABI) - Res.push_back("-fobjc-fragile-abi"); if (Opts.ObjCDefaultSynthProperties) Res.push_back("-fobjc-default-synthesize-properties"); // NoInline is implicit. @@ -690,8 +735,6 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { Res.push_back("-fno-rtti"); if (Opts.MSBitfields) Res.push_back("-mms-bitfields"); - if (!Opts.NeXTRuntime) - Res.push_back("-fgnu-runtime"); if (Opts.Freestanding) Res.push_back("-ffreestanding"); if (Opts.NoBuiltin) @@ -721,6 +764,11 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { Res.push_back("-ftrapv-handler", Opts.OverflowHandler); break; } + switch (Opts.getFPContractMode()) { + case LangOptions::FPC_Off: Res.push_back("-ffp-contract=off"); break; + case LangOptions::FPC_On: Res.push_back("-ffp-contract=on"); break; + case LangOptions::FPC_Fast: Res.push_back("-ffp-contract=fast"); break; + } if (Opts.HeinousExtensions) Res.push_back("-fheinous-gnu-extensions"); // Optimize is implicit. @@ -761,6 +809,7 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { Res.push_back("-fobjc-gc-only"); } } + Res.push_back("-fobjc-runtime=" + Opts.ObjCRuntime.getAsString()); if (Opts.ObjCAutoRefCount) Res.push_back("-fobjc-arc"); if (Opts.ObjCRuntimeHasWeak) @@ -770,7 +819,7 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { if (Opts.AppleKext) Res.push_back("-fapple-kext"); - + if (Opts.getVisibilityMode() != DefaultVisibility) { Res.push_back("-fvisibility"); if (Opts.getVisibilityMode() == HiddenVisibility) { @@ -880,7 +929,7 @@ static void TargetOptsToArgs(const TargetOptions &Opts, Res.push_back("-target-feature", Opts.Features[i]); } -void CompilerInvocation::toArgs(std::vector<std::string> &Res) { +void CompilerInvocation::toArgs(std::vector<std::string> &Res) const { ToArgsList List(Res); AnalyzerOptsToArgs(getAnalyzerOpts(), List); CodeGenOptsToArgs(getCodeGenOpts(), List); @@ -900,7 +949,7 @@ void CompilerInvocation::toArgs(std::vector<std::string> &Res) { //===----------------------------------------------------------------------===// using namespace clang::driver; -using namespace clang::driver::cc1options; +using namespace clang::driver::options; // @@ -909,14 +958,66 @@ static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, unsigned DefaultOpt = 0; if (IK == IK_OpenCL && !Args.hasArg(OPT_cl_opt_disable)) DefaultOpt = 2; - // -Os/-Oz implies -O2 - return (Args.hasArg(OPT_Os) || Args.hasArg (OPT_Oz)) ? 2 : - Args.getLastArgIntValue(OPT_O, DefaultOpt, Diags); + + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O0)) + return 0; + + assert (A->getOption().matches(options::OPT_O)); + + llvm::StringRef S(A->getValue(Args)); + if (S == "s" || S == "z" || S.empty()) + return 2; + + return Args.getLastArgIntValue(OPT_O, DefaultOpt, Diags); + } + + return DefaultOpt; +} + +static unsigned getOptimizationLevelSize(ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O)) { + switch (A->getValue(Args)[0]) { + default: + return 0; + case 's': + return 1; + case 'z': + return 2; + } + } + } + return 0; +} + +static void addWarningArgs(ArgList &Args, std::vector<std::string> &Warnings) { + for (arg_iterator I = Args.filtered_begin(OPT_W_Group), + E = Args.filtered_end(); I != E; ++I) { + Arg *A = *I; + // If the argument is a pure flag, add its name (minus the "-W" at the beginning) + // to the warning list. Else, add its value (for the OPT_W case). + if (A->getOption().getKind() == Option::FlagClass) { + Warnings.push_back(A->getOption().getName().substr(2)); + } else { + for (unsigned Idx = 0, End = A->getNumValues(); + Idx < End; ++Idx) { + StringRef V = A->getValue(Args, Idx); + // "-Wl," and such are not warning options. + // FIXME: Should be handled by putting these in separate flags. + if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,")) + continue; + + Warnings.push_back(V); + } + } + } } static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, DiagnosticsEngine &Diags) { - using namespace cc1options; + using namespace options; bool Success = true; if (Arg *A = Args.getLastArg(OPT_analyzer_store)) { StringRef Name = A->getValue(Args); @@ -1026,7 +1127,6 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function); Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG); Opts.CFGAddImplicitDtors = Args.hasArg(OPT_analysis_CFGAddImplicitDtors); - Opts.CFGAddInitializers = Args.hasArg(OPT_analysis_CFGAddInitializers); Opts.TrimGraph = Args.hasArg(OPT_trim_egraph); Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags); Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 4, Diags); @@ -1066,7 +1166,7 @@ static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, DiagnosticsEngine &Diags) { - using namespace cc1options; + using namespace options; bool Success = true; unsigned OptLevel = getOptimizationLevel(Args, IK, Diags); @@ -1083,12 +1183,18 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, : CodeGenOptions::OnlyAlwaysInlining; // -fno-inline-functions overrides OptimizationLevel > 1. Opts.NoInline = Args.hasArg(OPT_fno_inline); - Opts.Inlining = Args.hasArg(OPT_fno_inline_functions) ? + Opts.Inlining = Args.hasArg(OPT_fno_inline_functions) ? CodeGenOptions::OnlyAlwaysInlining : Opts.Inlining; - Opts.DebugInfo = Args.hasArg(OPT_g); - Opts.LimitDebugInfo = !Args.hasArg(OPT_fno_limit_debug_info) - || Args.hasArg(OPT_flimit_debug_info); + if (Args.hasArg(OPT_gline_tables_only)) { + Opts.DebugInfo = CodeGenOptions::DebugLineTablesOnly; + } else if (Args.hasArg(OPT_g_Flag)) { + if (Args.hasFlag(OPT_flimit_debug_info, OPT_fno_limit_debug_info, true)) + Opts.DebugInfo = CodeGenOptions::LimitedDebugInfo; + else + Opts.DebugInfo = CodeGenOptions::FullDebugInfo; + } + Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); @@ -1099,8 +1205,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants); Opts.NoCommon = Args.hasArg(OPT_fno_common); Opts.NoImplicitFloat = Args.hasArg(OPT_no_implicit_float); - Opts.OptimizeSize = Args.hasArg(OPT_Os); - Opts.OptimizeSize = Args.hasArg(OPT_Oz) ? 2 : Opts.OptimizeSize; + Opts.OptimizeSize = getOptimizationLevelSize(Args, IK, Diags); Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) || Args.hasArg(OPT_ffreestanding)); Opts.UnrollLoops = Args.hasArg(OPT_funroll_loops) || @@ -1108,8 +1213,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); - Opts.ObjCRuntimeHasARC = Args.hasArg(OPT_fobjc_runtime_has_arc); - Opts.ObjCRuntimeHasTerminate = Args.hasArg(OPT_fobjc_runtime_has_terminate); Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device); Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); @@ -1145,6 +1248,9 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); Opts.TrapFuncName = Args.getLastArgValue(OPT_ftrap_function_EQ); + Opts.BoundsChecking = Args.getLastArgIntValue(OPT_fbounds_checking_EQ, 0, + Diags); + Opts.UseInitArray = Args.hasArg(OPT_fuse_init_array); Opts.FunctionSections = Args.hasArg(OPT_ffunction_sections); Opts.DataSections = Args.hasArg(OPT_fdata_sections); @@ -1156,6 +1262,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data); Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); + Opts.EmitOpenCLArgMetadata = Args.hasArg(OPT_cl_kernel_arg_info); + Opts.EmitMicrosoftInlineAsm = Args.hasArg(OPT_fenable_experimental_ms_inline_asm); Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file); @@ -1180,12 +1288,28 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, } } + if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) { + StringRef Name = A->getValue(Args); + unsigned Model = llvm::StringSwitch<unsigned>(Name) + .Case("global-dynamic", CodeGenOptions::GeneralDynamicTLSModel) + .Case("local-dynamic", CodeGenOptions::LocalDynamicTLSModel) + .Case("initial-exec", CodeGenOptions::InitialExecTLSModel) + .Case("local-exec", CodeGenOptions::LocalExecTLSModel) + .Default(~0U); + if (Model == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.DefaultTLSModel = static_cast<CodeGenOptions::TLSModel>(Model); + } + } + return Success; } static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, ArgList &Args) { - using namespace cc1options; + using namespace options; Opts.OutputFile = Args.getLastArgValue(OPT_dependency_file); Opts.Targets = Args.getAllArgValues(OPT_MT); Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); @@ -1198,7 +1322,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, DiagnosticsEngine *Diags) { - using namespace cc1options; + using namespace options; bool Success = true; Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file); @@ -1273,6 +1397,8 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.ElideType = !Args.hasArg(OPT_fno_elide_type); + Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree); Opts.ErrorLimit = Args.getLastArgIntValue(OPT_ferror_limit, 0, Diags); Opts.MacroBacktraceLimit = Args.getLastArgIntValue(OPT_fmacro_backtrace_limit, @@ -1295,16 +1421,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, } Opts.MessageLength = Args.getLastArgIntValue(OPT_fmessage_length, 0, Diags); Opts.DumpBuildInformation = Args.getLastArgValue(OPT_dump_build_information); - - for (arg_iterator it = Args.filtered_begin(OPT_W), - ie = Args.filtered_end(); it != ie; ++it) { - StringRef V = (*it)->getValue(Args); - // "-Wl," and such are not warnings options. - if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,")) - continue; - - Opts.Warnings.push_back(V); - } + addWarningArgs(Args, Opts.Warnings); return Success; } @@ -1315,12 +1432,14 @@ static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, DiagnosticsEngine &Diags) { - using namespace cc1options; + using namespace options; Opts.ProgramAction = frontend::ParseSyntaxOnly; if (const Arg *A = Args.getLastArg(OPT_Action_Group)) { switch (A->getOption().getID()) { default: llvm_unreachable("Invalid option in group!"); + case OPT_ast_list: + Opts.ProgramAction = frontend::ASTDeclList; break; case OPT_ast_dump: Opts.ProgramAction = frontend::ASTDump; break; case OPT_ast_dump_xml: @@ -1418,11 +1537,6 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.Plugins = Args.getAllArgValues(OPT_load); Opts.RelocatablePCH = Args.hasArg(OPT_relocatable_pch); Opts.ShowHelp = Args.hasArg(OPT_help); - Opts.ShowMacrosInCodeCompletion = Args.hasArg(OPT_code_completion_macros); - Opts.ShowCodePatternsInCodeCompletion - = Args.hasArg(OPT_code_completion_patterns); - Opts.ShowGlobalSymbolsInCodeCompletion - = !Args.hasArg(OPT_no_code_completion_globals); Opts.ShowStats = Args.hasArg(OPT_print_stats); Opts.ShowTimers = Args.hasArg(OPT_ftime_report); Opts.ShowVersion = Args.hasArg(OPT_version); @@ -1432,6 +1546,17 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings); Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile); Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); + Opts.ASTDumpFilter = Args.getLastArgValue(OPT_ast_dump_filter); + + Opts.CodeCompleteOpts.IncludeMacros + = Args.hasArg(OPT_code_completion_macros); + Opts.CodeCompleteOpts.IncludeCodePatterns + = Args.hasArg(OPT_code_completion_patterns); + Opts.CodeCompleteOpts.IncludeGlobals + = !Args.hasArg(OPT_no_code_completion_globals); + Opts.CodeCompleteOpts.IncludeBriefComments + = Args.hasArg(OPT_code_completion_brief_comments); + Opts.OverrideRecordLayoutsFile = Args.getLastArgValue(OPT_foverride_record_layout_EQ); if (const Arg *A = Args.getLastArg(OPT_arcmt_check, @@ -1535,7 +1660,7 @@ std::string CompilerInvocation::GetResourcesPath(const char *Argv0, } static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { - using namespace cc1options; + using namespace options; Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/"); Opts.Verbose = Args.hasArg(OPT_v); Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc); @@ -1620,6 +1745,14 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddPath((*I)->getValue(Args), frontend::System, false, false, /*IgnoreSysRoot=*/true, /*IsInternal=*/true, (*I)->getOption().matches(OPT_internal_externc_isystem)); + + // Add the path prefixes which are implicitly treated as being system headers. + for (arg_iterator I = Args.filtered_begin(OPT_isystem_prefix, + OPT_ino_system_prefix), + E = Args.filtered_end(); + I != E; ++I) + Opts.AddSystemHeaderPrefix((*I)->getValue(Args), + (*I)->getOption().matches(OPT_isystem_prefix)); } void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, @@ -1677,9 +1810,22 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, Opts.HexFloats = Std.hasHexFloats(); Opts.ImplicitInt = Std.hasImplicitInt(); - // OpenCL has some additional defaults. + // Set OpenCL Version. if (LangStd == LangStandard::lang_opencl) { Opts.OpenCL = 1; + Opts.OpenCLVersion = 100; + } + else if (LangStd == LangStandard::lang_opencl11) { + Opts.OpenCL = 1; + Opts.OpenCLVersion = 110; + } + else if (LangStd == LangStandard::lang_opencl12) { + Opts.OpenCL = 1; + Opts.OpenCLVersion = 120; + } + + // OpenCL has some additional defaults. + if (Opts.OpenCL) { Opts.AltiVec = 0; Opts.CXXOperatorNames = 1; Opts.LaxVectorConversions = 0; @@ -1751,13 +1897,24 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, } } + // -cl-std only applies for OpenCL language standards. + // Override the -std option in this case. if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) { - if (strcmp(A->getValue(Args), "CL1.1") != 0) { + LangStandard::Kind OpenCLLangStd + = llvm::StringSwitch<LangStandard::Kind>(A->getValue(Args)) + .Case("CL", LangStandard::lang_opencl) + .Case("CL1.1", LangStandard::lang_opencl11) + .Case("CL1.2", LangStandard::lang_opencl12) + .Default(LangStandard::lang_unspecified); + + if (OpenCLLangStd == LangStandard::lang_unspecified) { Diags.Report(diag::err_drv_invalid_value) - << A->getAsString(Args) << A->getValue(Args); + << A->getAsString(Args) << A->getValue(Args); } + else + LangStd = OpenCLLangStd; } - + CompilerInvocation::setLangDefaults(Opts, IK, LangStd); // We abuse '-f[no-]gnu-keywords' to force overriding all GNU-extension @@ -1772,16 +1929,23 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.CXXOperatorNames = 0; if (Opts.ObjC1) { + if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) { + StringRef value = arg->getValue(Args); + if (Opts.ObjCRuntime.tryParse(value)) + Diags.Report(diag::err_drv_unknown_objc_runtime) << value; + } + if (Args.hasArg(OPT_fobjc_gc_only)) Opts.setGC(LangOptions::GCOnly); else if (Args.hasArg(OPT_fobjc_gc)) Opts.setGC(LangOptions::HybridGC); else if (Args.hasArg(OPT_fobjc_arc)) { Opts.ObjCAutoRefCount = 1; - if (Args.hasArg(OPT_fobjc_fragile_abi)) + if (!Opts.ObjCRuntime.isNonFragile()) Diags.Report(diag::err_arc_nonfragile_abi); } + Opts.ObjCRuntimeHasWeak = Opts.ObjCRuntime.hasWeak(); if (Args.hasArg(OPT_fobjc_runtime_has_weak)) Opts.ObjCRuntimeHasWeak = 1; @@ -1825,6 +1989,18 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Diags.Report(diag::err_drv_invalid_value) << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; + if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { + StringRef Val = A->getValue(Args); + if (Val == "fast") + Opts.setFPContractMode(LangOptions::FPC_Fast); + else if (Val == "on") + Opts.setFPContractMode(LangOptions::FPC_On); + else if (Val == "off") + Opts.setFPContractMode(LangOptions::FPC_Off); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + if (Args.hasArg(OPT_fvisibility_inlines_hidden)) Opts.InlineVisibilityHidden = 1; @@ -1876,25 +2052,21 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); Opts.MathErrno = Args.hasArg(OPT_fmath_errno); - Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 1024, + Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 512, Diags); Opts.ConstexprCallDepth = Args.getLastArgIntValue(OPT_fconstexpr_depth, 512, Diags); Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); - Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy, + Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy_EQ, 0, Diags); Opts.MSBitfields = Args.hasArg(OPT_mms_bitfields); - Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); Opts.ObjCConstantStringClass = Args.getLastArgValue(OPT_fconstant_string_class); - Opts.ObjCNonFragileABI = !Args.hasArg(OPT_fobjc_fragile_abi); - if (Opts.ObjCNonFragileABI) - Opts.ObjCNonFragileABI2 = true; Opts.ObjCDefaultSynthProperties = Args.hasArg(OPT_fobjc_default_synthesize_properties); Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); - Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct, 0, Diags); + Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct_EQ, 0, Diags); Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags); Opts.PIELevel = Args.getLastArgIntValue(OPT_pie_level, 0, Diags); Opts.Static = Args.hasArg(OPT_static_define); @@ -1924,9 +2096,10 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.Deprecated); // FIXME: Eliminate this dependency. - unsigned Opt = getOptimizationLevel(Args, IK, Diags); + unsigned Opt = getOptimizationLevel(Args, IK, Diags), + OptSize = getOptimizationLevelSize(Args, IK, Diags); Opts.Optimize = Opt != 0; - Opts.OptimizeSize = Args.hasArg(OPT_Os) || Args.hasArg(OPT_Oz); + Opts.OptimizeSize = OptSize != 0; // This is the __NO_INLINE__ define, which just depends on things like the // optimization level and -fno-inline, not actually whether the backend has @@ -1934,6 +2107,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.NoInlineDefine = !Opt || Args.hasArg(OPT_fno_inline); Opts.FastMath = Args.hasArg(OPT_ffast_math); + Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only); unsigned SSP = Args.getLastArgIntValue(OPT_stack_protector, 0, Diags); switch (SSP) { @@ -1950,7 +2124,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, FileManager &FileMgr, DiagnosticsEngine &Diags) { - using namespace cc1options; + using namespace options; Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch); Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth); if (const Arg *A = Args.getLastArg(OPT_token_cache)) @@ -2052,16 +2226,17 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, ArgList &Args) { - using namespace cc1options; + using namespace options; Opts.ShowCPP = !Args.hasArg(OPT_dM); Opts.ShowComments = Args.hasArg(OPT_C); Opts.ShowLineMarkers = !Args.hasArg(OPT_P); Opts.ShowMacroComments = Args.hasArg(OPT_CC); Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); } static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) { - using namespace cc1options; + using namespace options; Opts.ABI = Args.getLastArgValue(OPT_target_abi); Opts.CXXABI = Args.getLastArgValue(OPT_cxx_abi); Opts.CPU = Args.getLastArgValue(OPT_target_cpu); @@ -2083,7 +2258,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, bool Success = true; // Parse the arguments. - OwningPtr<OptTable> Opts(createCC1OptTable()); + OwningPtr<OptTable> Opts(createDriverOptTable()); unsigned MissingArgIndex, MissingArgCount; OwningPtr<InputArgList> Args( Opts->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount)); @@ -2102,6 +2277,15 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success = false; } + // Issue errors on arguments that are not valid for CC1. + for (ArgList::iterator I = Args->begin(), E = Args->end(); + I != E; ++I) { + if (!(*I)->getOption().isCC1Option()) { + Diags.Report(diag::err_drv_unknown_argument) << (*I)->getAsString(*Args); + Success = false; + } + } + Success = ParseAnalyzerArgs(Res.getAnalyzerOpts(), *Args, Diags) && Success; Success = ParseMigratorArgs(Res.getMigratorOpts(), *Args) && Success; ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args); diff --git a/lib/Frontend/CreateInvocationFromCommandLine.cpp b/lib/Frontend/CreateInvocationFromCommandLine.cpp index b477adebaddc..0aca86e2c97f 100644 --- a/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -43,13 +43,17 @@ clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, Args.push_back("<clang>"); // FIXME: Remove dummy argument. Args.insert(Args.end(), ArgList.begin(), ArgList.end()); - // FIXME: Find a cleaner way to force the driver into restricted modes. We - // also want to force it to use clang. + // FIXME: Find a cleaner way to force the driver into restricted modes. Args.push_back("-fsyntax-only"); // FIXME: We shouldn't have to pass in the path info. driver::Driver TheDriver("clang", llvm::sys::getDefaultTargetTriple(), "a.out", false, *Diags); + // Force driver to use clang. + // FIXME: This seems like a hack. Maybe the "Clang" tool subclass should be + // available for using it to get the arguments, thus avoiding the overkill + // of using the driver. + TheDriver.setForcedClangUse(); // Don't check that inputs exist, they may have been remapped. TheDriver.setCheckInputsExist(false); diff --git a/lib/Frontend/DiagnosticRenderer.cpp b/lib/Frontend/DiagnosticRenderer.cpp index 6c3bb1d69534..f052f90fa953 100644 --- a/lib/Frontend/DiagnosticRenderer.cpp +++ b/lib/Frontend/DiagnosticRenderer.cpp @@ -22,56 +22,6 @@ #include <algorithm> using namespace clang; -/// Look through spelling locations for a macro argument expansion, and -/// if found skip to it so that we can trace the argument rather than the macros -/// in which that argument is used. If no macro argument expansion is found, -/// don't skip anything and return the starting location. -static SourceLocation skipToMacroArgExpansion(const SourceManager &SM, - SourceLocation StartLoc) { - for (SourceLocation L = StartLoc; L.isMacroID(); - L = SM.getImmediateSpellingLoc(L)) { - if (SM.isMacroArgExpansion(L)) - return L; - } - - // Otherwise just return initial location, there's nothing to skip. - return StartLoc; -} - -/// Gets the location of the immediate macro caller, one level up the stack -/// toward the initial macro typed into the source. -static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, - SourceLocation Loc) { - if (!Loc.isMacroID()) return Loc; - - // When we have the location of (part of) an expanded parameter, its spelling - // location points to the argument as typed into the macro call, and - // therefore is used to locate the macro caller. - if (SM.isMacroArgExpansion(Loc)) - return SM.getImmediateSpellingLoc(Loc); - - // Otherwise, the caller of the macro is located where this macro is - // expanded (while the spelling is part of the macro definition). - return SM.getImmediateExpansionRange(Loc).first; -} - -/// Gets the location of the immediate macro callee, one level down the stack -/// toward the leaf macro. -static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, - SourceLocation Loc) { - if (!Loc.isMacroID()) return Loc; - - // When we have the location of (part of) an expanded parameter, its - // expansion location points to the unexpanded paramater reference within - // the macro definition (or callee). - if (SM.isMacroArgExpansion(Loc)) - return SM.getImmediateExpansionRange(Loc).first; - - // Otherwise, the callee of the macro is located where this location was - // spelled inside the macro definition. - return SM.getImmediateSpellingLoc(Loc); -} - /// \brief Retrieve the name of the immediate macro expansion. /// /// This routine starts from a source location, and finds the name of the macro @@ -109,24 +59,9 @@ static StringRef getImmediateMacroName(SourceLocation Loc, return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); } -/// Get the presumed location of a diagnostic message. This computes the -/// presumed location for the top of any macro backtrace when present. -static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, - SourceLocation Loc) { - // This is a condensed form of the algorithm used by emitCaretDiagnostic to - // walk to the top of the macro call stack. - while (Loc.isMacroID()) { - Loc = skipToMacroArgExpansion(SM, Loc); - Loc = getImmediateMacroCallerLoc(SM, Loc); - } - - return SM.getPresumedLoc(Loc); -} - -DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM, - const LangOptions &LangOpts, +DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts) -: SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} +: LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} DiagnosticRenderer::~DiagnosticRenderer() {} @@ -184,18 +119,23 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, StringRef Message, ArrayRef<CharSourceRange> Ranges, ArrayRef<FixItHint> FixItHints, + const SourceManager *SM, DiagOrStoredDiag D) { + assert(SM || Loc.isInvalid()); beginDiagnostic(D, Level); - PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc); + PresumedLoc PLoc; + if (Loc.isValid()) { + PLoc = SM->getPresumedLocForDisplay(Loc); - // First, if this diagnostic is not in the main file, print out the - // "included from" lines. - emitIncludeStack(PLoc.getIncludeLoc(), Level); + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + emitIncludeStack(PLoc.getIncludeLoc(), Level, *SM); + } // Next, emit the actual diagnostic message. - emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D); + emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D); // Only recurse if we have a valid location. if (Loc.isValid()) { @@ -205,7 +145,7 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, llvm::SmallVector<FixItHint, 8> MergedFixits; if (!FixItHints.empty()) { - mergeFixits(FixItHints, SM, LangOpts, MergedFixits); + mergeFixits(FixItHints, *SM, LangOpts, MergedFixits); FixItHints = MergedFixits; } @@ -216,7 +156,7 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, MutableRanges.push_back(I->RemoveRange); unsigned MacroDepth = 0; - emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints, + emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints, *SM, MacroDepth); } @@ -230,6 +170,8 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), Diag.getRanges(), Diag.getFixIts(), + Diag.getLocation().isValid() ? &Diag.getLocation().getManager() + : 0, &Diag); } @@ -245,7 +187,8 @@ void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { /// \param Loc The include location of the current file (not the diagnostic /// location). void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, - DiagnosticsEngine::Level Level) { + DiagnosticsEngine::Level Level, + const SourceManager &SM) { // Skip redundant include stacks altogether. if (LastIncludeLoc == Loc) return; @@ -254,12 +197,13 @@ void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) return; - emitIncludeStackRecursively(Loc); + emitIncludeStackRecursively(Loc, SM); } /// \brief Helper to recursivly walk up the include stack and print each layer /// on the way back down. -void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) { +void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc, + const SourceManager &SM) { if (Loc.isInvalid()) return; @@ -268,10 +212,10 @@ void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) { return; // Emit the other include frames first. - emitIncludeStackRecursively(PLoc.getIncludeLoc()); + emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM); // Emit the inclusion text/note. - emitIncludeLocation(Loc, PLoc); + emitIncludeLocation(Loc, PLoc, SM); } /// \brief Recursively emit notes for each macro expansion and caret @@ -292,6 +236,7 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets( DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, ArrayRef<FixItHint> Hints, + const SourceManager &SM, unsigned &MacroDepth, unsigned OnMacroInst) { @@ -302,26 +247,26 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets( if (Loc.isFileID()) { assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); MacroDepth = OnMacroInst; - emitCodeContext(Loc, Level, Ranges, Hints); + emitCodeContext(Loc, Level, Ranges, Hints, SM); return; } // Otherwise recurse through each macro expansion layer. // When processing macros, skip over the expansions leading up to // a macro argument, and trace the argument's expansion stack instead. - Loc = skipToMacroArgExpansion(SM, Loc); + Loc = SM.skipToMacroArgExpansion(Loc); - SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); + SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc); // FIXME: Map ranges? - emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth, + emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, SM, MacroDepth, OnMacroInst + 1); // Save the original location so we can find the spelling of the macro call. SourceLocation MacroLoc = Loc; // Map the location. - Loc = getImmediateMacroCalleeLoc(SM, Loc); + Loc = SM.getImmediateMacroCalleeLoc(Loc); unsigned MacroSkipStart = 0, MacroSkipEnd = 0; if (MacroDepth > DiagOpts.MacroBacktraceLimit && @@ -341,9 +286,9 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets( I != E; ++I) { SourceLocation Start = I->getBegin(), End = I->getEnd(); if (Start.isMacroID()) - I->setBegin(getImmediateMacroCalleeLoc(SM, Start)); + I->setBegin(SM.getImmediateMacroCalleeLoc(Start)); if (End.isMacroID()) - I->setEnd(getImmediateMacroCalleeLoc(SM, End)); + I->setEnd(SM.getImmediateMacroCalleeLoc(End)); } if (Suppressed) { @@ -365,22 +310,22 @@ void DiagnosticRenderer::emitMacroExpansionsAndCarets( << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, Message.str(), - Ranges, ArrayRef<FixItHint>()); + Ranges, ArrayRef<FixItHint>(), &SM); } DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc, - PresumedLoc PLoc) { + PresumedLoc PLoc, + const SourceManager &SM) { // Generate a note indicating the include location. SmallString<200> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "in file included from " << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; - emitNote(Loc, Message.str()); + emitNote(Loc, Message.str(), &SM); } void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) { - emitNote(SourceLocation(), Message); + emitNote(SourceLocation(), Message, 0); } - diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index da4bdfabc067..a4321e720ed0 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -83,31 +83,31 @@ public: } }; - /// \brief Checks deserialized declarations and emits error if a name - /// matches one given in command-line using -error-on-deserialized-decl. - class DeserializedDeclsChecker : public DelegatingDeserializationListener { - ASTContext &Ctx; - std::set<std::string> NamesToCheck; - - public: - DeserializedDeclsChecker(ASTContext &Ctx, - const std::set<std::string> &NamesToCheck, - ASTDeserializationListener *Previous) - : DelegatingDeserializationListener(Previous), - Ctx(Ctx), NamesToCheck(NamesToCheck) { } - - virtual void DeclRead(serialization::DeclID ID, const Decl *D) { - if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) - if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) { - unsigned DiagID - = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, - "%0 was deserialized"); - Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID) - << ND->getNameAsString(); - } - - DelegatingDeserializationListener::DeclRead(ID, D); - } +/// \brief Checks deserialized declarations and emits error if a name +/// matches one given in command-line using -error-on-deserialized-decl. +class DeserializedDeclsChecker : public DelegatingDeserializationListener { + ASTContext &Ctx; + std::set<std::string> NamesToCheck; + +public: + DeserializedDeclsChecker(ASTContext &Ctx, + const std::set<std::string> &NamesToCheck, + ASTDeserializationListener *Previous) + : DelegatingDeserializationListener(Previous), + Ctx(Ctx), NamesToCheck(NamesToCheck) { } + + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) { + unsigned DiagID + = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, + "%0 was deserialized"); + Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID) + << ND->getNameAsString(); + } + + DelegatingDeserializationListener::DeclRead(ID, D); + } }; } // end anonymous namespace @@ -162,6 +162,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, setCurrentInput(Input); setCompilerInstance(&CI); + bool HasBegunSourceFile = false; if (!BeginInvocation(CI)) goto failure; @@ -214,6 +215,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // Inform the diagnostic client we are processing a source file. CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); + HasBegunSourceFile = true; // Initialize the action. if (!BeginSourceFileAction(CI, Input.File)) @@ -228,6 +230,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // Inform the diagnostic client we are processing a source file. CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), &CI.getPreprocessor()); + HasBegunSourceFile = true; // Initialize the action. if (!BeginSourceFileAction(CI, Input.File)) @@ -309,13 +312,14 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.setFileManager(0); } - CI.getDiagnosticClient().EndSourceFile(); + if (HasBegunSourceFile) + CI.getDiagnosticClient().EndSourceFile(); setCurrentInput(FrontendInputFile()); setCompilerInstance(0); return false; } -void FrontendAction::Execute() { +bool FrontendAction::Execute() { CompilerInstance &CI = getCompilerInstance(); // Initialize the main file entry. This needs to be delayed until after PCH @@ -325,7 +329,7 @@ void FrontendAction::Execute() { getCurrentInput().IsSystem ? SrcMgr::C_System : SrcMgr::C_User)) - return; + return false; } if (CI.hasFrontendTimer()) { @@ -333,6 +337,8 @@ void FrontendAction::Execute() { ExecuteAction(); } else ExecuteAction(); + + return true; } void FrontendAction::EndSourceFile() { diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 737ee4a098bb..24960cf6a0c9 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -47,13 +47,18 @@ void InitOnlyAction::ExecuteAction() { ASTConsumer *ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) - return CreateASTPrinter(OS); + return CreateASTPrinter(OS, CI.getFrontendOpts().ASTDumpFilter); return 0; } ASTConsumer *ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return CreateASTDumper(); + return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter); +} + +ASTConsumer *ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateASTDeclNodeLister(); } ASTConsumer *ASTDumpXMLAction::CreateASTConsumer(CompilerInstance &CI, @@ -131,7 +136,7 @@ ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, /// /// \param Module The module we're collecting includes from. /// -/// \param Includes Will be augmented with the set of #includes or #imports +/// \param Includes Will be augmented with the set of \#includes or \#imports /// needed to load all of the named headers. static void collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp index 3f7e6825140c..8178f7a9362a 100644 --- a/lib/Frontend/InitHeaderSearch.cpp +++ b/lib/Frontend/InitHeaderSearch.cpp @@ -40,6 +40,7 @@ class InitHeaderSearch { std::vector<std::pair<IncludeDirGroup, DirectoryLookup> > IncludePath; typedef std::vector<std::pair<IncludeDirGroup, DirectoryLookup> >::const_iterator path_iterator; + std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes; HeaderSearch &Headers; bool Verbose; std::string IncludeSysroot; @@ -57,6 +58,12 @@ public: bool isCXXAware, bool isUserSupplied, bool isFramework, bool IgnoreSysRoot = false); + /// AddSystemHeaderPrefix - Add the specified prefix to the system header + /// prefix list. + void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) { + SystemHeaderPrefixes.push_back(std::make_pair(Prefix, IsSystemHeader)); + } + /// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu /// libstdc++. void AddGnuCPlusPlusIncludePaths(StringRef Base, @@ -210,6 +217,8 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, switch (os) { case llvm::Triple::FreeBSD: case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + case llvm::Triple::Bitrig: break; default: // FIXME: temporary hack: hard-coded paths. @@ -623,6 +632,8 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) { bool DontSearchCurDir = false; // TODO: set to true if -I- is set? Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir); + Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes); + // If verbose, print the list of directories that will be searched. if (Verbose) { llvm::errs() << "#include \"...\" search starts here:\n"; @@ -660,6 +671,10 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, Init.AddDefaultIncludePaths(Lang, Triple, HSOpts); + for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i) + Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix, + HSOpts.SystemHeaderPrefixes[i].IsSystemHeader); + if (HSOpts.UseBuiltinIncludes) { // Set up the builtin include directory in the module map. llvm::sys::Path P(HSOpts.ResourceDir); diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp index 93d49b0563be..1440da6e3755 100644 --- a/lib/Frontend/InitPreprocessor.cpp +++ b/lib/Frontend/InitPreprocessor.cpp @@ -49,7 +49,7 @@ static void DefineBuiltinMacro(MacroBuilder &Builder, StringRef Macro, } } -/// AddImplicitInclude - Add an implicit #include of the specified file to the +/// AddImplicitInclude - Add an implicit \#include of the specified file to the /// predefines buffer. static void AddImplicitInclude(MacroBuilder &Builder, StringRef File, FileManager &FileMgr) { @@ -66,8 +66,8 @@ static void AddImplicitIncludeMacros(MacroBuilder &Builder, Builder.append("##"); // ##? } -/// AddImplicitIncludePTH - Add an implicit #include using the original file -/// used to generate a PTH cache. +/// AddImplicitIncludePTH - Add an implicit \#include using the original file +/// used to generate a PTH cache. static void AddImplicitIncludePTH(MacroBuilder &Builder, Preprocessor &PP, StringRef ImplicitIncludePTH) { PTHManager *P = PP.getPTHManager(); @@ -288,20 +288,16 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, else if (!LangOpts.GNUMode && LangOpts.Digraphs) Builder.defineMacro("__STDC_VERSION__", "199409L"); } else { - if (LangOpts.GNUMode) - Builder.defineMacro("__cplusplus"); - else { - // C++0x [cpp.predefined]p1: - // The name_ _cplusplus is defined to the value 201103L when compiling a - // C++ translation unit. - if (LangOpts.CPlusPlus0x) - Builder.defineMacro("__cplusplus", "201103L"); - // C++03 [cpp.predefined]p1: - // The name_ _cplusplus is defined to the value 199711L when compiling a - // C++ translation unit. - else - Builder.defineMacro("__cplusplus", "199711L"); - } + // C++11 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201103L when compiling a + // C++ translation unit. + if (LangOpts.CPlusPlus0x) + Builder.defineMacro("__cplusplus", "201103L"); + // C++03 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 199711L when compiling a + // C++ translation unit. + else + Builder.defineMacro("__cplusplus", "199711L"); } if (LangOpts.ObjC1) @@ -369,7 +365,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__"); if (LangOpts.ObjC1) { - if (LangOpts.ObjCNonFragileABI) { + if (LangOpts.ObjCRuntime.isNonFragile()) { Builder.defineMacro("__OBJC2__"); if (LangOpts.ObjCExceptions) @@ -379,8 +375,13 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.getGC() != LangOptions::NonGC) Builder.defineMacro("__OBJC_GC__"); - if (LangOpts.NeXTRuntime) + if (LangOpts.ObjCRuntime.isNeXTFamily()) Builder.defineMacro("__NEXT_RUNTIME__"); + + Builder.defineMacro("IBOutlet", "__attribute__((iboutlet))"); + Builder.defineMacro("IBOutletCollection(ClassName)", + "__attribute__((iboutletcollection(ClassName)))"); + Builder.defineMacro("IBAction", "void)__attribute__((ibaction)"); } // darwin_constant_cfstrings controls this. This is also dependent @@ -444,6 +445,26 @@ static void InitializePredefinedMacros(const TargetInfo &TI, // Initialize target-specific preprocessor defines. + // __BYTE_ORDER__ was added in GCC 4.6. It's analogous + // to the macro __BYTE_ORDER (no trailing underscores) + // from glibc's <endian.h> header. + // We don't support the PDP-11 as a target, but include + // the define so it can still be compared against. + Builder.defineMacro("__ORDER_LITTLE_ENDIAN__", "1234"); + Builder.defineMacro("__ORDER_BIG_ENDIAN__", "4321"); + Builder.defineMacro("__ORDER_PDP_ENDIAN__", "3412"); + if (TI.isBigEndian()) + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_BIG_ENDIAN__"); + else + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"); + + + if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_LP64"); + Builder.defineMacro("__LP64__"); + } + // Define type sizing macros based on the target properties. assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); Builder.defineMacro("__CHAR_BIT__", "8"); @@ -501,6 +522,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (!LangOpts.CharIsSigned) Builder.defineMacro("__CHAR_UNSIGNED__"); + if (!TargetInfo::isTypeSigned(TI.getWCharType())) + Builder.defineMacro("__WCHAR_UNSIGNED__"); + if (!TargetInfo::isTypeSigned(TI.getWIntType())) Builder.defineMacro("__WINT_UNSIGNED__"); @@ -520,15 +544,13 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (TI.getLongLongWidth() > TI.getLongWidth()) DefineExactWidthIntType(TargetInfo::SignedLongLong, TI, Builder); - // Add __builtin_va_list typedef. - Builder.append(TI.getVAListDeclaration()); - if (const char *Prefix = TI.getUserLabelPrefix()) Builder.defineMacro("__USER_LABEL_PREFIX__", Prefix); - // Build configuration options. FIXME: these should be controlled by - // command line options or something. - Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + if (LangOpts.FastMath || LangOpts.FiniteMathOnly) + Builder.defineMacro("__FINITE_MATH_ONLY__", "1"); + else + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); if (LangOpts.GNUInline) Builder.defineMacro("__GNUC_GNU_INLINE__"); diff --git a/lib/Frontend/LayoutOverrideSource.cpp b/lib/Frontend/LayoutOverrideSource.cpp index eb7865e1124a..e0232503dfe4 100644 --- a/lib/Frontend/LayoutOverrideSource.cpp +++ b/lib/Frontend/LayoutOverrideSource.cpp @@ -9,6 +9,7 @@ #include "clang/Frontend/LayoutOverrideSource.h" #include "clang/AST/Decl.h" #include "llvm/Support/raw_ostream.h" +#include <cctype> #include <fstream> #include <string> diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index 9e1587c8fa2f..5311ed52cd29 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" +#include <cctype> #include <cstdio> using namespace clang; @@ -87,7 +88,7 @@ private: unsigned CurLine; bool EmittedTokensOnThisLine; - bool EmittedMacroOnThisLine; + bool EmittedDirectiveOnThisLine; SrcMgr::CharacteristicKind FileType; SmallString<512> CurFilename; bool Initialized; @@ -103,7 +104,7 @@ public: CurLine = 0; CurFilename += "<uninit>"; EmittedTokensOnThisLine = false; - EmittedMacroOnThisLine = false; + EmittedDirectiveOnThisLine = false; FileType = SrcMgr::C_User; Initialized = false; @@ -111,10 +112,15 @@ public: UseLineDirective = PP.getLangOpts().MicrosoftExt; } - void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } - bool StartNewLineIfNeeded(); + void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; } + bool hasEmittedDirectiveOnThisLine() const { + return EmittedDirectiveOnThisLine; + } + + bool startNewLineIfNeeded(bool ShouldUpdateCurrentLine = true); virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, @@ -158,11 +164,7 @@ public: void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo, const char *Extra, unsigned ExtraLen) { - if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) { - OS << '\n'; - EmittedTokensOnThisLine = false; - EmittedMacroOnThisLine = false; - } + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); // Emit #line directives or GNU line markers depending on what mode we're in. if (UseLineDirective) { @@ -207,23 +209,21 @@ bool PrintPPOutputPPCallbacks::MoveToLine(unsigned LineNo) { } else { // Okay, we're in -P mode, which turns off line markers. However, we still // need to emit a newline between tokens on different lines. - if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) { - OS << '\n'; - EmittedTokensOnThisLine = false; - EmittedMacroOnThisLine = false; - } + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); } CurLine = LineNo; return true; } -bool PrintPPOutputPPCallbacks::StartNewLineIfNeeded() { - if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) { +bool +PrintPPOutputPPCallbacks::startNewLineIfNeeded(bool ShouldUpdateCurrentLine) { + if (EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) { OS << '\n'; EmittedTokensOnThisLine = false; - EmittedMacroOnThisLine = false; - ++CurLine; + EmittedDirectiveOnThisLine = false; + if (ShouldUpdateCurrentLine) + ++CurLine; return true; } @@ -307,7 +307,7 @@ void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, MoveToLine(MI->getDefinitionLoc()); PrintMacroDefinition(*MacroNameTok.getIdentifierInfo(), *MI, PP, OS); - EmittedMacroOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, @@ -317,12 +317,13 @@ void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, MoveToLine(MacroNameTok.getLocation()); OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName(); - EmittedMacroOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, const std::string &Str) { + startNewLineIfNeeded(); MoveToLine(Loc); OS << "#pragma comment(" << Kind->getName(); @@ -343,11 +344,12 @@ void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, } OS << ')'; - EmittedTokensOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, StringRef Str) { + startNewLineIfNeeded(); MoveToLine(Loc); OS << "#pragma message("; @@ -366,26 +368,29 @@ void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, OS << '"'; OS << ')'; - EmittedTokensOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks:: PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); MoveToLine(Loc); OS << "#pragma " << Namespace << " diagnostic push"; - EmittedTokensOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks:: PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); MoveToLine(Loc); OS << "#pragma " << Namespace << " diagnostic pop"; - EmittedTokensOnThisLine = true; + setEmittedDirectiveOnThisLine(); } void PrintPPOutputPPCallbacks:: PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, diag::Mapping Map, StringRef Str) { + startNewLineIfNeeded(); MoveToLine(Loc); OS << "#pragma " << Namespace << " diagnostic "; switch (Map) { @@ -403,7 +408,7 @@ PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, break; } OS << " \"" << Str << '"'; - EmittedTokensOnThisLine = true; + setEmittedDirectiveOnThisLine(); } /// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this @@ -471,10 +476,9 @@ struct UnknownPragmaHandler : public PragmaHandler { Token &PragmaTok) { // Figure out what line we went to and insert the appropriate number of // newline characters. - Callbacks->StartNewLineIfNeeded(); + Callbacks->startNewLineIfNeeded(); Callbacks->MoveToLine(PragmaTok.getLocation()); Callbacks->OS.write(Prefix, strlen(Prefix)); - Callbacks->SetEmittedTokensOnThisLine(); // Read and print all of the pragma tokens. while (PragmaTok.isNot(tok::eod)) { if (PragmaTok.hasLeadingSpace()) @@ -483,7 +487,7 @@ struct UnknownPragmaHandler : public PragmaHandler { Callbacks->OS.write(&TokSpell[0], TokSpell.size()); PP.LexUnexpandedToken(PragmaTok); } - Callbacks->StartNewLineIfNeeded(); + Callbacks->setEmittedDirectiveOnThisLine(); } }; } // end anonymous namespace @@ -497,6 +501,10 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, PrevPrevTok.startToken(); PrevTok.startToken(); while (1) { + if (Callbacks->hasEmittedDirectiveOnThisLine()) { + Callbacks->startNewLineIfNeeded(); + Callbacks->MoveToLine(Tok.getLocation()); + } // If this token is at the start of a line, emit newlines if needed. if (Tok.isAtStartOfLine() && Callbacks->HandleFirstTokOnLine(Tok)) { @@ -533,7 +541,7 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, if (Tok.getKind() == tok::comment) Callbacks->HandleNewlinesInToken(&S[0], S.size()); } - Callbacks->SetEmittedTokensOnThisLine(); + Callbacks->setEmittedTokensOnThisLine(); if (Tok.is(tok::eof)) break; diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp index 7bf8742a8040..a20f30d6be83 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -53,10 +53,9 @@ class SDiagsRenderer : public DiagnosticNoteRenderer { RecordData &Record; public: SDiagsRenderer(SDiagsWriter &Writer, RecordData &Record, - const SourceManager &SM, const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts) - : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts), + : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer), Record(Record){} virtual ~SDiagsRenderer() {} @@ -67,18 +66,21 @@ protected: DiagnosticsEngine::Level Level, StringRef Message, ArrayRef<CharSourceRange> Ranges, + const SourceManager *SM, DiagOrStoredDiag D); virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, - ArrayRef<CharSourceRange> Ranges) {} + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) {} - void emitNote(SourceLocation Loc, StringRef Message); + void emitNote(SourceLocation Loc, StringRef Message, const SourceManager *SM); virtual void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, - ArrayRef<FixItHint> Hints); + ArrayRef<FixItHint> Hints, + const SourceManager &SM); virtual void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level); @@ -137,15 +139,16 @@ private: unsigned getEmitFile(const char *Filename); /// \brief Add SourceLocation information the specified record. - void AddLocToRecord(SourceLocation Loc, const SourceManager &SM, + void AddLocToRecord(SourceLocation Loc, const SourceManager *SM, PresumedLoc PLoc, RecordDataImpl &Record, unsigned TokSize = 0); /// \brief Add SourceLocation information the specified record. void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, - const SourceManager &SM, + const SourceManager *SM, unsigned TokSize = 0) { - AddLocToRecord(Loc, SM, SM.getPresumedLoc(Loc), Record, TokSize); + AddLocToRecord(Loc, SM, SM ? SM->getPresumedLoc(Loc) : PresumedLoc(), + Record, TokSize); } /// \brief Add CharSourceRange information the specified record. @@ -241,7 +244,7 @@ static void EmitRecordID(unsigned ID, const char *Name, } void SDiagsWriter::AddLocToRecord(SourceLocation Loc, - const SourceManager &SM, + const SourceManager *SM, PresumedLoc PLoc, RecordDataImpl &Record, unsigned TokSize) { @@ -257,19 +260,19 @@ void SDiagsWriter::AddLocToRecord(SourceLocation Loc, Record.push_back(getEmitFile(PLoc.getFilename())); Record.push_back(PLoc.getLine()); Record.push_back(PLoc.getColumn()+TokSize); - Record.push_back(SM.getFileOffset(Loc)); + Record.push_back(SM->getFileOffset(Loc)); } void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, RecordDataImpl &Record, const SourceManager &SM) { - AddLocToRecord(Range.getBegin(), Record, SM); + AddLocToRecord(Range.getBegin(), Record, &SM); unsigned TokSize = 0; if (Range.isTokenRange()) TokSize = Lexer::MeasureTokenLength(Range.getEnd(), SM, *LangOpts); - AddLocToRecord(Range.getEnd(), Record, SM, TokSize); + AddLocToRecord(Range.getEnd(), Record, &SM, TokSize); } unsigned SDiagsWriter::getEmitFile(const char *FileName){ @@ -484,13 +487,15 @@ void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, diagBuf.clear(); Info.FormatDiagnostic(diagBuf); - SourceManager &SM = Info.getSourceManager(); - SDiagsRenderer Renderer(*this, Record, SM, *LangOpts, DiagOpts); + const SourceManager * + SM = Info.hasSourceManager() ? &Info.getSourceManager() : 0; + SDiagsRenderer Renderer(*this, Record, *LangOpts, DiagOpts); Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, diagBuf.str(), Info.getRanges(), llvm::makeArrayRef(Info.getFixItHints(), Info.getNumFixItHints()), + SM, &Info); } @@ -500,6 +505,7 @@ SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, DiagOrStoredDiag D) { // Emit the RECORD_DIAG record. Writer.Record.clear(); @@ -539,7 +545,8 @@ void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, void SDiagsRenderer::emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange> &Ranges, - ArrayRef<FixItHint> Hints) { + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { // Emit Source Ranges. for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end(); it != ei; ++it) { @@ -562,7 +569,8 @@ void SDiagsRenderer::emitCodeContext(SourceLocation Loc, } } -void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message) { +void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message, + const SourceManager *SM) { Writer.Stream.EnterSubblock(BLOCK_DIAG, 4); RecordData Record; Record.push_back(RECORD_DIAG); diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp index 65fb1ae13f21..9bb3e1dce8c7 100644 --- a/lib/Frontend/TextDiagnostic.cpp +++ b/lib/Frontend/TextDiagnostic.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include <algorithm> +#include <cctype> using namespace clang; @@ -31,16 +32,36 @@ static const enum raw_ostream::Colors caretColor = raw_ostream::GREEN; static const enum raw_ostream::Colors warningColor = raw_ostream::MAGENTA; +static const enum raw_ostream::Colors templateColor = + raw_ostream::CYAN; static const enum raw_ostream::Colors errorColor = raw_ostream::RED; static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; // Used for changing only the bold attribute. static const enum raw_ostream::Colors savedColor = raw_ostream::SAVEDCOLOR; +/// \brief Add highlights to differences in template strings. +static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, + bool &Normal, bool Bold) { + for (unsigned i = 0, e = Str.size(); i < e; ++i) + if (Str[i] != ToggleHighlight) { + OS << Str[i]; + } else { + if (Normal) + OS.changeColor(templateColor, true); + else { + OS.resetColor(); + if (Bold) + OS.changeColor(savedColor, true); + } + Normal = !Normal; + } +} + /// \brief Number of spaces to indent when word-wrapping. const unsigned WordWrapIndentation = 6; -int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { +static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { int bytes = 0; while (0<i) { if (SourceLine[--i]=='\t') @@ -69,7 +90,7 @@ int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { /// \param TabStop used to expand tabs /// \return pair(printable text, 'true' iff original text was printable) /// -std::pair<SmallString<16>,bool> +static std::pair<SmallString<16>, bool> printableTextForNextCharacter(StringRef SourceLine, size_t *i, unsigned TabStop) { assert(i && "i must not be null"); @@ -146,7 +167,7 @@ printableTextForNextCharacter(StringRef SourceLine, size_t *i, return std::make_pair(expandedByte, false); } -void expandTabs(std::string &SourceLine, unsigned TabStop) { +static void expandTabs(std::string &SourceLine, unsigned TabStop) { size_t i = SourceLine.size(); while (i>0) { i--; @@ -164,7 +185,7 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) { /// characters will appear at (numbering the first column as 0). /// /// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab -/// character) then the the array will map that byte to the first column the +/// character) then the array will map that byte to the first column the /// tab appears at and the next value in the map will have been incremented /// more than once. /// @@ -179,9 +200,9 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) { /// /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} /// -/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to /// display) -void byteToColumn(StringRef SourceLine, unsigned TabStop, +static void byteToColumn(StringRef SourceLine, unsigned TabStop, SmallVectorImpl<int> &out) { out.clear(); @@ -213,9 +234,9 @@ void byteToColumn(StringRef SourceLine, unsigned TabStop, /// /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} /// -/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to /// display) -void columnToByte(StringRef SourceLine, unsigned TabStop, +static void columnToByte(StringRef SourceLine, unsigned TabStop, SmallVectorImpl<int> &out) { out.clear(); @@ -307,11 +328,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // correctly. unsigned CaretStart = 0, CaretEnd = CaretLine.size(); for (; CaretStart != CaretEnd; ++CaretStart) - if (!isspace(CaretLine[CaretStart])) + if (!isspace(static_cast<unsigned char>(CaretLine[CaretStart]))) break; for (; CaretEnd != CaretStart; --CaretEnd) - if (!isspace(CaretLine[CaretEnd - 1])) + if (!isspace(static_cast<unsigned char>(CaretLine[CaretEnd - 1]))) break; // caret has already been inserted into CaretLine so the above whitespace @@ -322,17 +343,33 @@ static void selectInterestingSourceRegion(std::string &SourceLine, if (!FixItInsertionLine.empty()) { unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); for (; FixItStart != FixItEnd; ++FixItStart) - if (!isspace(FixItInsertionLine[FixItStart])) + if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItStart]))) break; for (; FixItEnd != FixItStart; --FixItEnd) - if (!isspace(FixItInsertionLine[FixItEnd - 1])) + if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItEnd - 1]))) break; CaretStart = std::min(FixItStart, CaretStart); CaretEnd = std::max(FixItEnd, CaretEnd); } + // CaretEnd may have been set at the middle of a character + // If it's not at a character's first column then advance it past the current + // character. + while (static_cast<int>(CaretEnd) < map.columns() && + -1 == map.columnToByte(CaretEnd)) + ++CaretEnd; + + assert((static_cast<int>(CaretStart) > map.columns() || + -1!=map.columnToByte(CaretStart)) && + "CaretStart must not point to a column in the middle of a source" + " line character"); + assert((static_cast<int>(CaretEnd) > map.columns() || + -1!=map.columnToByte(CaretEnd)) && + "CaretEnd must not point to a column in the middle of a source line" + " character"); + // CaretLine[CaretStart, CaretEnd) contains all of the interesting // parts of the caret line. While this slice is smaller than the // number of columns we have, try to grow the slice to encompass @@ -366,12 +403,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // Skip over any whitespace we see here; we're looking for // another bit of interesting text. while (NewStart && - (map.byteToColumn(NewStart)==-1 || isspace(SourceLine[NewStart]))) + (map.byteToColumn(NewStart)==-1 || + isspace(static_cast<unsigned char>(SourceLine[NewStart])))) --NewStart; // Skip over this bit of "interesting" text. while (NewStart && - (map.byteToColumn(NewStart)!=-1 && !isspace(SourceLine[NewStart]))) + (map.byteToColumn(NewStart)!=-1 && + !isspace(static_cast<unsigned char>(SourceLine[NewStart])))) --NewStart; // Move up to the non-whitespace character we just saw. @@ -392,12 +431,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // Skip over any whitespace we see here; we're looking for // another bit of interesting text. while (NewEnd<SourceLine.size() && - (map.byteToColumn(NewEnd)==-1 || isspace(SourceLine[NewEnd]))) + (map.byteToColumn(NewEnd)==-1 || + isspace(static_cast<unsigned char>(SourceLine[NewEnd])))) ++NewEnd; // Skip over this bit of "interesting" text. while (NewEnd<SourceLine.size() && - (map.byteToColumn(NewEnd)!=-1 && !isspace(SourceLine[NewEnd]))) + (map.byteToColumn(NewEnd)!=-1 && + !isspace(static_cast<unsigned char>(SourceLine[NewEnd])))) ++NewEnd; unsigned NewColumns = map.byteToColumn(NewEnd) - @@ -549,6 +590,7 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str, /// \param Column the column number at which the first character of \p /// Str will be printed. This will be non-zero when part of the first /// line has already been printed. +/// \param Bold if the current text should be bold /// \param Indentation the number of spaces to indent any lines beyond /// the first line. /// \returns true if word-wrapping was required, or false if the @@ -556,8 +598,10 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str, static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns, unsigned Column = 0, + bool Bold = false, unsigned Indentation = WordWrapIndentation) { const unsigned Length = std::min(Str.find('\n'), Str.size()); + bool TextNormal = true; // The string used to indent each line. SmallString<16> IndentStr; @@ -581,7 +625,8 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, OS << ' '; Column += 1; } - OS << Str.substr(WordStart, WordLength); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); Column += WordLength; continue; } @@ -590,22 +635,24 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, // line. OS << '\n'; OS.write(&IndentStr[0], Indentation); - OS << Str.substr(WordStart, WordLength); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); Column = Indentation + WordLength; Wrapped = true; } // Append any remaning text from the message with its existing formatting. - OS << Str.substr(Length); + applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); + + assert(TextNormal && "Text highlighted at end of diagnostic message."); return Wrapped; } TextDiagnostic::TextDiagnostic(raw_ostream &OS, - const SourceManager &SM, const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts) - : DiagnosticRenderer(SM, LangOpts, DiagOpts), OS(OS) {} + : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {} TextDiagnostic::~TextDiagnostic() {} @@ -615,11 +662,13 @@ TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, DiagOrStoredDiag D) { uint64_t StartOfLocationInfo = OS.tell(); // Emit the location of this particular diagnostic. - emitDiagnosticLoc(Loc, PLoc, Level, Ranges); + if (Loc.isValid()) + emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM); if (DiagOpts.ShowColors) OS.resetColor(); @@ -665,20 +714,27 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, StringRef Message, unsigned CurrentColumn, unsigned Columns, bool ShowColors) { + bool Bold = false; if (ShowColors) { // Print warnings, errors and fatal errors in bold, no color switch (Level) { - case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break; - case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break; - case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break; + case DiagnosticsEngine::Warning: + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + OS.changeColor(savedColor, true); + Bold = true; + break; default: break; //don't bold notes } } if (Columns) - printWordWrapped(OS, Message, Columns, CurrentColumn); - else - OS << Message; + printWordWrapped(OS, Message, Columns, CurrentColumn, Bold); + else { + bool Normal = true; + applyTemplateHighlighting(OS, Message, Normal, Bold); + assert(Normal && "Formatting should have returned to normal"); + } if (ShowColors) OS.resetColor(); @@ -693,7 +749,8 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, /// ranges necessary. void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, - ArrayRef<CharSourceRange> Ranges) { + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) { if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Loc); @@ -799,7 +856,8 @@ void TextDiagnostic::emitBasicNote(StringRef Message) { } void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, - PresumedLoc PLoc) { + PresumedLoc PLoc, + const SourceManager &SM) { if (DiagOpts.ShowLocation) OS << "In file included from " << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; @@ -817,7 +875,8 @@ void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, void TextDiagnostic::emitSnippetAndCaret( SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange>& Ranges, - ArrayRef<FixItHint> Hints) { + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { assert(!Loc.isInvalid() && "must have a valid source location here"); assert(Loc.isFileID() && "must have a file location here"); @@ -840,17 +899,12 @@ void TextDiagnostic::emitSnippetAndCaret( // Get information about the buffer it points into. bool Invalid = false; - StringRef BufData = SM.getBufferData(FID, &Invalid); + const char *BufStart = SM.getBufferData(FID, &Invalid).data(); if (Invalid) return; - const char *BufStart = BufData.data(); - const char *BufEnd = BufStart + BufData.size(); - unsigned LineNo = SM.getLineNumber(FID, FileOffset); unsigned ColNo = SM.getColumnNumber(FID, FileOffset); - unsigned CaretEndColNo - = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts); // Rewind from the current position to the start of the line. const char *TokPtr = BufStart+FileOffset; @@ -860,14 +914,9 @@ void TextDiagnostic::emitSnippetAndCaret( // Compute the line end. Scan forward from the error position to the end of // the line. const char *LineEnd = TokPtr; - while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd!=BufEnd) + while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') ++LineEnd; - // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past - // the source line length as currently being computed. See - // test/Misc/message-length.c. - CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); - // Copy the line of code into an std::string for ease of manipulation. std::string SourceLine(LineStart, LineEnd); @@ -881,7 +930,7 @@ void TextDiagnostic::emitSnippetAndCaret( for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) - highlightRange(*I, LineNo, FID, sourceColMap, CaretLine); + highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM); // Next, insert the caret itself. ColNo = sourceColMap.byteToColumn(ColNo-1); @@ -891,7 +940,7 @@ void TextDiagnostic::emitSnippetAndCaret( std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, sourceColMap, - Hints); + Hints, SM); // If the source line is too long for our terminal, select only the // "interesting" source region within that line. @@ -934,11 +983,10 @@ void TextDiagnostic::emitSnippetAndCaret( } // Print out any parseable fixit information requested by the options. - emitParseableFixits(Hints); + emitParseableFixits(Hints, SM); } -void TextDiagnostic::emitSnippet(StringRef line) -{ +void TextDiagnostic::emitSnippet(StringRef line) { if (line.empty()) return; @@ -952,8 +1000,7 @@ void TextDiagnostic::emitSnippet(StringRef line) = printableTextForNextCharacter(line, &i, DiagOpts.TabStop); bool was_printable = res.second; - if (DiagOpts.ShowColors - && was_printable==print_reversed) { + if (DiagOpts.ShowColors && was_printable == print_reversed) { if (print_reversed) OS.reverseColor(); OS << to_print; @@ -979,7 +1026,8 @@ void TextDiagnostic::emitSnippet(StringRef line) void TextDiagnostic::highlightRange(const CharSourceRange &R, unsigned LineNo, FileID FID, const SourceColumnMap &map, - std::string &CaretLine) { + std::string &CaretLine, + const SourceManager &SM) { if (!R.isValid()) return; SourceLocation Begin = SM.getExpansionLoc(R.getBegin()); @@ -1064,49 +1112,63 @@ void TextDiagnostic::highlightRange(const CharSourceRange &R, std::string TextDiagnostic::buildFixItInsertionLine( unsigned LineNo, const SourceColumnMap &map, - ArrayRef<FixItHint> Hints) { + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { std::string FixItInsertionLine; if (Hints.empty() || !DiagOpts.ShowFixits) return FixItInsertionLine; + unsigned PrevHintEndCol = 0; for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); I != E; ++I) { if (!I->CodeToInsert.empty()) { // We have an insertion hint. Determine whether the inserted - // code is on the same line as the caret. + // code contains no newlines and is on the same line as the caret. std::pair<FileID, unsigned> HintLocInfo = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); - if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) { + if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && + StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) { // Insert the new code into the line just below the code // that the user wrote. - unsigned HintColNo + // Note: When modifying this function, be very careful about what is a + // "column" (printed width, platform-dependent) and what is a + // "byte offset" (SourceManager "column"). + unsigned HintByteOffset = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; - // hint must start inside the source or right at the end - assert(HintColNo<static_cast<unsigned>(map.bytes())+1); - HintColNo = map.byteToColumn(HintColNo); - - // FIXME: if the fixit includes tabs or other characters that do not - // take up a single column per byte when displayed then - // I->CodeToInsert.size() is not a column number and we're mixing - // units (columns + bytes). We should get printable versions - // of each fixit before using them. - unsigned LastColumnModified - = HintColNo + I->CodeToInsert.size(); - - if (LastColumnModified > static_cast<unsigned>(map.bytes())) { - unsigned LastExistingColumn = map.byteToColumn(map.bytes()); - unsigned AddedColumns = LastColumnModified-LastExistingColumn; - LastColumnModified = LastExistingColumn + AddedColumns; - } else { - LastColumnModified = map.byteToColumn(LastColumnModified); - } + // The hint must start inside the source or right at the end + assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); + unsigned HintCol = map.byteToColumn(HintByteOffset); + + // If we inserted a long previous hint, push this one forwards, and add + // an extra space to show that this is not part of the previous + // completion. This is sort of the best we can do when two hints appear + // to overlap. + // + // Note that if this hint is located immediately after the previous + // hint, no space will be added, since the location is more important. + if (HintCol < PrevHintEndCol) + HintCol = PrevHintEndCol + 1; + + // FIXME: This function handles multibyte characters in the source, but + // not in the fixits. This assertion is intended to catch unintended + // use of multibyte characters in fixits. If we decide to do this, we'll + // have to track separate byte widths for the source and fixit lines. + assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) == + I->CodeToInsert.size()); + + // This relies on one byte per column in our fixit hints. + // This should NOT use HintByteOffset, because the source might have + // Unicode characters in earlier columns. + unsigned LastColumnModified = HintCol + I->CodeToInsert.size(); if (LastColumnModified > FixItInsertionLine.size()) FixItInsertionLine.resize(LastColumnModified, ' '); - assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size()); + std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), - FixItInsertionLine.begin() + HintColNo); + FixItInsertionLine.begin() + HintCol); + + PrevHintEndCol = LastColumnModified; } else { FixItInsertionLine.clear(); break; @@ -1119,7 +1181,8 @@ std::string TextDiagnostic::buildFixItInsertionLine( return FixItInsertionLine; } -void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints) { +void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, + const SourceManager &SM) { if (!DiagOpts.ShowParseableFixits) return; diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 6445a0cc79bb..382e1567c7f4 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -27,7 +27,7 @@ using namespace clang; TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, const DiagnosticOptions &diags, bool _OwnsOutputStream) - : OS(os), LangOpts(0), DiagOpts(&diags), SM(0), + : OS(os), DiagOpts(&diags), OwnsOutputStream(_OwnsOutputStream) { } @@ -38,11 +38,11 @@ TextDiagnosticPrinter::~TextDiagnosticPrinter() { void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) { - LangOpts = &LO; + // Build the TextDiagnostic utility. + TextDiag.reset(new TextDiagnostic(OS, LO, *DiagOpts)); } void TextDiagnosticPrinter::EndSourceFile() { - LangOpts = 0; TextDiag.reset(0); } @@ -79,16 +79,6 @@ static void printDiagnosticOptions(raw_ostream &OS, Started = true; } - // If the diagnostic is an extension diagnostic and not enabled by default - // then it must have been turned on with -pedantic. - bool EnabledByDefault; - if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(), - EnabledByDefault) && - !EnabledByDefault) { - OS << (Started ? "," : " [") << "-pedantic"; - Started = true; - } - StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { OS << (Started ? "," : " [") << "-W" << Opt; @@ -128,7 +118,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, llvm::raw_svector_ostream DiagMessageStream(OutStr); printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); - // Keeps track of the the starting position of the location + // Keeps track of the starting position of the location // information (e.g., "foo.c:10:4:") that precedes the error // message. We use this information to determine how long the // file+line+column number prefix is. @@ -152,22 +142,16 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, } // Assert that the rest of our infrastructure is setup properly. - assert(LangOpts && "Unexpected diagnostic outside source file processing"); assert(DiagOpts && "Unexpected diagnostic without options set"); assert(Info.hasSourceManager() && "Unexpected diagnostic with no source manager"); - - // Rebuild the TextDiagnostic utility if missing or the source manager has - // changed. - if (!TextDiag || SM != &Info.getSourceManager()) { - SM = &Info.getSourceManager(); - TextDiag.reset(new TextDiagnostic(OS, *SM, *LangOpts, *DiagOpts)); - } + assert(TextDiag && "Unexpected diagnostic outside source file processing"); TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), Info.getRanges(), llvm::makeArrayRef(Info.getFixItHints(), - Info.getNumFixItHints())); + Info.getNumFixItHints()), + &Info.getSourceManager()); OS.flush(); } diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp index 552282dafb34..a9378a117d6b 100644 --- a/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -11,58 +11,108 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/FileManager.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" -#include <climits> +#include <cctype> using namespace clang; +typedef VerifyDiagnosticConsumer::Directive Directive; +typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; +typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags) - : Diags(_Diags), PrimaryClient(Diags.getClient()), - OwnsPrimaryClient(Diags.ownsClient()), - Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0) + : Diags(_Diags), + PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()), + Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), + ActiveSourceFiles(0) { Diags.takeClient(); } VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { + assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); + assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); CheckDiagnostics(); Diags.takeClient(); if (OwnsPrimaryClient) delete PrimaryClient; } +#ifndef NDEBUG +namespace { +class VerifyFileTracker : public PPCallbacks { + typedef VerifyDiagnosticConsumer::FilesParsedForDirectivesSet ListType; + ListType &FilesList; + SourceManager &SM; + +public: + VerifyFileTracker(ListType &FilesList, SourceManager &SM) + : FilesList(FilesList), SM(SM) { } + + /// \brief Hook into the preprocessor and update the list of parsed + /// files when the preprocessor indicates a new file is entered. + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + if (const FileEntry *E = SM.getFileEntryForID(SM.getFileID(Loc))) + FilesList.insert(E); + } +}; +} // End anonymous namespace. +#endif + // DiagnosticConsumer interface. void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP) { - // FIXME: Const hack, we screw up the preprocessor but in practice its ok - // because it doesn't get reused. It would be better if we could make a copy - // though. - CurrentPreprocessor = const_cast<Preprocessor*>(PP); + // Attach comment handler on first invocation. + if (++ActiveSourceFiles == 1) { + if (PP) { + CurrentPreprocessor = PP; + const_cast<Preprocessor*>(PP)->addCommentHandler(this); +#ifndef NDEBUG + VerifyFileTracker *V = new VerifyFileTracker(FilesParsedForDirectives, + PP->getSourceManager()); + const_cast<Preprocessor*>(PP)->addPPCallbacks(V); +#endif + } + } + assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); PrimaryClient->BeginSourceFile(LangOpts, PP); } void VerifyDiagnosticConsumer::EndSourceFile() { - CheckDiagnostics(); - + assert(ActiveSourceFiles && "No active source files!"); PrimaryClient->EndSourceFile(); - CurrentPreprocessor = 0; + // Detach comment handler once last active source file completed. + if (--ActiveSourceFiles == 0) { + if (CurrentPreprocessor) + const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); + + // Check diagnostics once last file completed. + CheckDiagnostics(); + CurrentPreprocessor = 0; + } } void VerifyDiagnosticConsumer::HandleDiagnostic( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { - if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) { - const SourceManager &SM = Info.getSourceManager(); - FirstErrorFID = SM.getFileID(Info.getLocation()); +#ifndef NDEBUG + if (Info.hasSourceManager()) { + FileID FID = Info.getSourceManager().getFileID(Info.getLocation()); + if (!FID.isInvalid()) + FilesWithDiagnostics.insert(FID); } +#endif // Send the diagnostic to the buffer, we will check it once we reach the end // of the source file (or are destructed). Buffer->HandleDiagnostic(DiagLevel, Info); @@ -77,54 +127,21 @@ typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; namespace { -/// Directive - Abstract class representing a parsed verify directive. -/// -class Directive { -public: - static Directive* Create(bool RegexKind, const SourceLocation &Location, - const std::string &Text, unsigned Count); -public: - /// Constant representing one or more matches aka regex "+". - static const unsigned OneOrMoreCount = UINT_MAX; - - SourceLocation Location; - const std::string Text; - unsigned Count; - - virtual ~Directive() { } - - // Returns true if directive text is valid. - // Otherwise returns false and populates E. - virtual bool isValid(std::string &Error) = 0; - - // Returns true on match. - virtual bool Match(const std::string &S) = 0; - -protected: - Directive(const SourceLocation &Location, const std::string &Text, - unsigned Count) - : Location(Location), Text(Text), Count(Count) { } - -private: - Directive(const Directive&); // DO NOT IMPLEMENT - void operator=(const Directive&); // DO NOT IMPLEMENT -}; - /// StandardDirective - Directive with string matching. /// class StandardDirective : public Directive { public: - StandardDirective(const SourceLocation &Location, const std::string &Text, - unsigned Count) - : Directive(Location, Text, Count) { } + StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { } virtual bool isValid(std::string &Error) { // all strings are considered valid; even empty ones return true; } - virtual bool Match(const std::string &S) { - return S.find(Text) != std::string::npos; + virtual bool match(StringRef S) { + return S.find(Text) != StringRef::npos; } }; @@ -132,9 +149,9 @@ public: /// class RegexDirective : public Directive { public: - RegexDirective(const SourceLocation &Location, const std::string &Text, - unsigned Count) - : Directive(Location, Text, Count), Regex(Text) { } + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { } virtual bool isValid(std::string &Error) { if (Regex.isValid(Error)) @@ -142,7 +159,7 @@ public: return false; } - virtual bool Match(const std::string &S) { + virtual bool match(StringRef S) { return Regex.match(S); } @@ -150,30 +167,11 @@ private: llvm::Regex Regex; }; -typedef std::vector<Directive*> DirectiveList; - -/// ExpectedData - owns directive objects and deletes on destructor. -/// -struct ExpectedData { - DirectiveList Errors; - DirectiveList Warnings; - DirectiveList Notes; - - ~ExpectedData() { - DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 }; - for (DirectiveList **PL = Lists; *PL; ++PL) { - DirectiveList * const L = *PL; - for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I) - delete *I; - } - } -}; - class ParseHelper { public: - ParseHelper(const char *Begin, const char *End) - : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { } + ParseHelper(StringRef S) + : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { } // Return true if string literal is next. bool Next(StringRef S) { @@ -240,78 +238,134 @@ private: /// ParseDirective - Go through the comment and see if it indicates expected /// diagnostics. If so, then put them in the appropriate directive list. /// -static void ParseDirective(const char *CommentStart, unsigned CommentLen, - ExpectedData &ED, Preprocessor &PP, - SourceLocation Pos) { +/// Returns true if any valid directives were found. +static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, + SourceLocation Pos, DiagnosticsEngine &Diags) { // A single comment may contain multiple directives. - for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) { - // search for token: expected + bool FoundDirective = false; + for (ParseHelper PH(S); !PH.Done();) { + // Search for token: expected if (!PH.Search("expected")) break; PH.Advance(); - // next token: - + // Next token: - if (!PH.Next("-")) continue; PH.Advance(); - // next token: { error | warning | note } + // Next token: { error | warning | note } DirectiveList* DL = NULL; if (PH.Next("error")) - DL = &ED.Errors; + DL = ED ? &ED->Errors : NULL; else if (PH.Next("warning")) - DL = &ED.Warnings; + DL = ED ? &ED->Warnings : NULL; else if (PH.Next("note")) - DL = &ED.Notes; + DL = ED ? &ED->Notes : NULL; else continue; PH.Advance(); - // default directive kind + // If a directive has been found but we're not interested + // in storing the directive information, return now. + if (!DL) + return true; + + // Default directive kind. bool RegexKind = false; const char* KindStr = "string"; - // next optional token: - + // Next optional token: - if (PH.Next("-re")) { PH.Advance(); RegexKind = true; KindStr = "regex"; } - // skip optional whitespace + // Next optional token: @ + SourceLocation ExpectedLoc; + if (!PH.Next("@")) { + ExpectedLoc = Pos; + } else { + PH.Advance(); + unsigned Line = 0; + bool FoundPlus = PH.Next("+"); + if (FoundPlus || PH.Next("-")) { + // Relative to current line. + PH.Advance(); + bool Invalid = false; + unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); + if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { + if (FoundPlus) ExpectedLine += Line; + else ExpectedLine -= Line; + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); + } + } else { + // Absolute line number. + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); + } + + if (ExpectedLoc.isInvalid()) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_line) << KindStr; + continue; + } + PH.Advance(); + } + + // Skip optional whitespace. PH.SkipWhitespace(); - // next optional token: positive integer or a '+'. - unsigned Count = 1; - if (PH.Next(Count)) + // Next optional token: positive integer or a '+'. + unsigned Min = 1; + unsigned Max = 1; + if (PH.Next(Min)) { PH.Advance(); - else if (PH.Next("+")) { - Count = Directive::OneOrMoreCount; + // A positive integer can be followed by a '+' meaning min + // or more, or by a '-' meaning a range from min to max. + if (PH.Next("+")) { + Max = Directive::MaxCount; + PH.Advance(); + } else if (PH.Next("-")) { + PH.Advance(); + if (!PH.Next(Max) || Max < Min) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_invalid_range) << KindStr; + continue; + } + PH.Advance(); + } else { + Max = Min; + } + } else if (PH.Next("+")) { + // '+' on its own means "1 or more". + Max = Directive::MaxCount; PH.Advance(); } - // skip optional whitespace + // Skip optional whitespace. PH.SkipWhitespace(); - // next token: {{ + // Next token: {{ if (!PH.Next("{{")) { - PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin), - diag::err_verify_missing_start) << KindStr; + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_start) << KindStr; continue; } PH.Advance(); const char* const ContentBegin = PH.C; // mark content begin - // search for token: }} + // Search for token: }} if (!PH.Search("}}")) { - PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin), - diag::err_verify_missing_end) << KindStr; + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_end) << KindStr; continue; } const char* const ContentEnd = PH.P; // mark content end PH.Advance(); - // build directive text; convert \n to newlines + // Build directive text; convert \n to newlines. std::string Text; StringRef NewlineStr = "\\n"; StringRef Content(ContentBegin, ContentEnd-ContentBegin); @@ -325,25 +379,83 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, if (Text.empty()) Text.assign(ContentBegin, ContentEnd); - // construct new directive - Directive *D = Directive::Create(RegexKind, Pos, Text, Count); + // Construct new directive. + Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, + Min, Max); std::string Error; - if (D->isValid(Error)) + if (D->isValid(Error)) { DL->push_back(D); - else { - PP.Diag(Pos.getLocWithOffset(ContentBegin-PH.Begin), - diag::err_verify_invalid_content) + FoundDirective = true; + } else { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_invalid_content) << KindStr << Error; } } + + return FoundDirective; } -/// FindExpectedDiags - Lex the main source file to find all of the -// expected errors and warnings. -static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { +/// HandleComment - Hook into the preprocessor and extract comments containing +/// expected errors and warnings. +bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, + SourceRange Comment) { + SourceManager &SM = PP.getSourceManager(); + SourceLocation CommentBegin = Comment.getBegin(); + + const char *CommentRaw = SM.getCharacterData(CommentBegin); + StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); + + if (C.empty()) + return false; + + // Fold any "\<EOL>" sequences + size_t loc = C.find('\\'); + if (loc == StringRef::npos) { + ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics()); + return false; + } + + std::string C2; + C2.reserve(C.size()); + + for (size_t last = 0;; loc = C.find('\\', last)) { + if (loc == StringRef::npos || loc == C.size()) { + C2 += C.substr(last); + break; + } + C2 += C.substr(last, loc-last); + last = loc + 1; + + if (C[last] == '\n' || C[last] == '\r') { + ++last; + + // Escape \r\n or \n\r, but not \n\n. + if (last < C.size()) + if (C[last] == '\n' || C[last] == '\r') + if (C[last] != C[last-1]) + ++last; + } else { + // This was just a normal backslash. + C2 += '\\'; + } + } + + if (!C2.empty()) + ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics()); + return false; +} + +#ifndef NDEBUG +/// \brief Lex the specified source file to determine whether it contains +/// any expected-* directives. As a Lexer is used rather than a full-blown +/// Preprocessor, directives inside skipped #if blocks will still be found. +/// +/// \return true if any directives were found. +static bool findDirectives(const Preprocessor &PP, FileID FID) { // Create a raw lexer to pull all the comments out of FID. if (FID.isInvalid()) - return; + return false; SourceManager& SM = PP.getSourceManager(); // Create a lexer to lex all the tokens of the main file in raw mode. @@ -355,6 +467,7 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { Token Tok; Tok.setKind(tok::comment); + bool Found = false; while (Tok.isNot(tok::eof)) { RawLex.Lex(Tok); if (!Tok.is(tok::comment)) continue; @@ -363,19 +476,19 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { if (Comment.empty()) continue; // Find all expected errors/warnings/notes. - ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation()); - }; + Found |= ParseDirective(Comment, 0, SM, Tok.getLocation(), + PP.getDiagnostics()); + } + return Found; } - -/// PrintProblem - This takes a diagnostic map of the delta between expected and -/// seen diagnostics. If there's anything in it, then something unexpected -/// happened. Print the map out in a nice format and return "true". If the map -/// is empty and we're not going to print things, then return "false". -/// -static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, - const_diag_iterator diag_begin, - const_diag_iterator diag_end, - const char *Kind, bool Expected) { +#endif // !NDEBUG + +/// \brief Takes a list of diagnostics that have been generated but not matched +/// by an expected-* directive and produces a diagnostic to the user from this. +static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Kind) { if (diag_begin == diag_end) return 0; SmallString<256> Fmt; @@ -388,30 +501,32 @@ static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, OS << ": " << I->second; } - Diags.Report(diag::err_verify_inconsistent_diags) - << Kind << !Expected << OS.str(); + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/true << OS.str(); return std::distance(diag_begin, diag_end); } -static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, - DirectiveList &DL, const char *Kind, - bool Expected) { +/// \brief Takes a list of diagnostics that were expected to have been generated +/// but were not and produces a diagnostic to the user from this. +static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + DirectiveList &DL, const char *Kind) { if (DL.empty()) return 0; SmallString<256> Fmt; llvm::raw_svector_ostream OS(Fmt); for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { - Directive& D = **I; - if (D.Location.isInvalid() || !SourceMgr) - OS << "\n (frontend)"; - else - OS << "\n Line " << SourceMgr->getPresumedLineNumber(D.Location); + Directive &D = **I; + OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + if (D.DirectiveLoc != D.DiagnosticLoc) + OS << " (directive at " + << SourceMgr.getFilename(D.DirectiveLoc) << ":" + << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")"; OS << ": " << D.Text; } - Diags.Report(diag::err_verify_inconsistent_diags) - << Kind << !Expected << OS.str(); + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/false << OS.str(); return DL.size(); } @@ -428,10 +543,9 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { Directive& D = **I; - unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location); - bool FoundOnce = false; + unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); - for (unsigned i = 0; i < D.Count; ++i) { + for (unsigned i = 0; i < D.Max; ++i) { DiagList::iterator II, IE; for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); @@ -439,29 +553,22 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, continue; const std::string &RightText = II->second; - if (D.Match(RightText)) + if (D.match(RightText)) break; } if (II == IE) { - if (D.Count == D.OneOrMoreCount) { - if (!FoundOnce) - LeftOnly.push_back(*I); - // We are only interested in at least one match, so exit the loop. - break; - } // Not found. + if (i >= D.Min) break; LeftOnly.push_back(*I); } else { // Found. The same cannot be found twice. Right.erase(II); - FoundOnce = true; } } } // Now all that's left in Right are those that were not matched. - unsigned num = PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true); - num += PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), - Label, false); + unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); + num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); return num; } @@ -495,8 +602,6 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, } void VerifyDiagnosticConsumer::CheckDiagnostics() { - ExpectedData ED; - // Ensure any diagnostics go to the primary client. bool OwnsCurClient = Diags.ownsClient(); DiagnosticConsumer *CurClient = Diags.takeClient(); @@ -506,32 +611,38 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { // markers. If not then any diagnostics are unexpected. if (CurrentPreprocessor) { SourceManager &SM = CurrentPreprocessor->getSourceManager(); - // Extract expected-error strings from main file. - FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID()); - // Only check for expectations in other diagnostic locations - // if they are not the main file (via ID or FileEntry) - the main - // file has already been looked at, and its expectations must not - // be added twice. - if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID() - && (!SM.getFileEntryForID(FirstErrorFID) - || (SM.getFileEntryForID(FirstErrorFID) != - SM.getFileEntryForID(SM.getMainFileID())))) { - FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID); - FirstErrorFID = FileID(); + +#ifndef NDEBUG + // In a debug build, scan through any files that may have been missed + // during parsing and issue a fatal error if directives are contained + // within these files. If a fatal error occurs, this suggests that + // this file is being parsed separately from the main file. + HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); + for (FilesWithDiagnosticsSet::iterator I = FilesWithDiagnostics.begin(), + End = FilesWithDiagnostics.end(); + I != End; ++I) { + const FileEntry *E = SM.getFileEntryForID(*I); + // Don't check files already parsed or those handled as modules. + if (E && (FilesParsedForDirectives.count(E) + || HS.findModuleForHeader(E))) + continue; + + if (findDirectives(*CurrentPreprocessor, *I)) + llvm::report_fatal_error(Twine("-verify directives found after rather" + " than during normal parsing of ", + StringRef(E ? E->getName() : "(unknown)"))); } +#endif // Check that the expected diagnostics occurred. NumErrors += CheckResults(Diags, SM, *Buffer, ED); } else { - NumErrors += (PrintProblem(Diags, 0, - Buffer->err_begin(), Buffer->err_end(), - "error", false) + - PrintProblem(Diags, 0, - Buffer->warn_begin(), Buffer->warn_end(), - "warn", false) + - PrintProblem(Diags, 0, - Buffer->note_begin(), Buffer->note_end(), - "note", false)); + NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(), + Buffer->err_end(), "error") + + PrintUnexpected(Diags, 0, Buffer->warn_begin(), + Buffer->warn_end(), "warn") + + PrintUnexpected(Diags, 0, Buffer->note_begin(), + Buffer->note_end(), "note")); } Diags.takeClient(); @@ -539,6 +650,9 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { // Reset the buffer, we have processed all the diagnostics in it. Buffer.reset(new TextDiagnosticBuffer()); + ED.Errors.clear(); + ED.Warnings.clear(); + ED.Notes.clear(); } DiagnosticConsumer * @@ -549,9 +663,10 @@ VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const { return new VerifyDiagnosticConsumer(Diags); } -Directive* Directive::Create(bool RegexKind, const SourceLocation &Location, - const std::string &Text, unsigned Count) { +Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, StringRef Text, + unsigned Min, unsigned Max) { if (RegexKind) - return new RegexDirective(Location, Text, Count); - return new StandardDirective(Location, Text, Count); + return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); + return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); } diff --git a/lib/Frontend/Warnings.cpp b/lib/Frontend/Warnings.cpp index ec5fde0c3eff..b7d4a3b92538 100644 --- a/lib/Frontend/Warnings.cpp +++ b/lib/Frontend/Warnings.cpp @@ -53,7 +53,11 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings); Diags.setShowOverloads( static_cast<DiagnosticsEngine::OverloadsShown>(Opts.ShowOverloads)); - + + Diags.setElideType(Opts.ElideType); + Diags.setPrintTemplateTree(Opts.ShowTemplateTree); + Diags.setShowColors(Opts.ShowColors); + // Handle -ferror-limit if (Opts.ErrorLimit) Diags.setErrorLimit(Opts.ErrorLimit); @@ -83,6 +87,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, bool SetDiagnostic = (Report == 0); for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { StringRef Opt = Opts.Warnings[i]; + StringRef OrigOpt = Opts.Warnings[i]; // Treat -Wformat=0 as an alias for -Wno-format. if (Opt == "format=0") @@ -130,7 +135,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) { if (Report) Diags.Report(diag::warn_unknown_warning_specifier) - << "-Werror" << ("-W" + Opt.str()); + << "-Werror" << ("-W" + OrigOpt.str()); continue; } Specifier = Opt.substr(6); @@ -158,7 +163,7 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) { if (Report) Diags.Report(diag::warn_unknown_warning_specifier) - << "-Wfatal-errors" << ("-W" + Opt.str()); + << "-Wfatal-errors" << ("-W" + OrigOpt.str()); continue; } Specifier = Opt.substr(13); @@ -182,7 +187,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, if (Report) { if (DiagIDs->getDiagnosticsInGroup(Opt, _Diags)) - EmitUnknownDiagWarning(Diags, "-W", Opt, isPositive); + EmitUnknownDiagWarning(Diags, isPositive ? "-W" : "-Wno-", Opt, + isPositive); } else { Diags.setDiagnosticGroupMapping(Opt, Mapping); } diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 5270b1b03ed3..fe9d5896e728 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,11 +1,20 @@ -set(LLVM_USED_LIBS clangDriver clangFrontend clangRewrite clangCodeGen - clangStaticAnalyzerFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore - clangARCMigrate) - add_clang_library(clangFrontendTool ExecuteCompilerInvocation.cpp ) add_dependencies(clangFrontendTool - ClangCC1Options - ClangDiagnosticFrontend) + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangDriverOptions + ) + +target_link_libraries(clangFrontendTool + clangDriver + clangFrontend + clangRewrite + clangCodeGen + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore + clangARCMigrate + ) diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 07d2b8d19f24..bd50083bf1c9 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -16,7 +16,7 @@ #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "clang/ARCMigrate/ARCMTActions.h" #include "clang/CodeGen/CodeGenAction.h" -#include "clang/Driver/CC1Options.h" +#include "clang/Driver/Options.h" #include "clang/Driver/OptTable.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/CompilerInstance.h" @@ -32,6 +32,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { using namespace clang::frontend; switch (CI.getFrontendOpts().ProgramAction) { + case ASTDeclList: return new ASTDeclListAction(); case ASTDump: return new ASTDumpAction(); case ASTDumpXML: return new ASTDumpXMLAction(); case ASTPrint: return new ASTPrintAction(); @@ -71,7 +72,12 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case PrintDeclContext: return new DeclContextPrintAction(); case PrintPreamble: return new PrintPreambleAction(); - case PrintPreprocessedInput: return new PrintPreprocessedAction(); + case PrintPreprocessedInput: { + if (CI.getPreprocessorOutputOpts().RewriteIncludes) + return new RewriteIncludesAction(); + return new PrintPreprocessedAction(); + } + case RewriteMacros: return new RewriteMacrosAction(); case RewriteObjC: return new RewriteObjCAction(); case RewriteTest: return new RewriteTestAction(); @@ -129,7 +135,7 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { bool clang::ExecuteCompilerInvocation(CompilerInstance *Clang) { // Honor -help. if (Clang->getFrontendOpts().ShowHelp) { - OwningPtr<driver::OptTable> Opts(driver::createCC1OptTable()); + OwningPtr<driver::OptTable> Opts(driver::createDriverOptTable()); Opts->PrintHelp(llvm::outs(), "clang -cc1", "LLVM 'Clang' Compiler: http://clang.llvm.org"); return 0; diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt index 78141a3d07ff..6e9cc68540b6 100644 --- a/lib/Headers/CMakeLists.txt +++ b/lib/Headers/CMakeLists.txt @@ -1,5 +1,6 @@ set(files altivec.h + ammintrin.h avxintrin.h avx2intrin.h bmiintrin.h @@ -7,6 +8,7 @@ set(files emmintrin.h float.h fma4intrin.h + fmaintrin.h immintrin.h iso646.h limits.h @@ -29,6 +31,7 @@ set(files wmmintrin.h x86intrin.h xmmintrin.h + xopintrin.h cpuid.h unwind.h module.map diff --git a/lib/Headers/ammintrin.h b/lib/Headers/ammintrin.h new file mode 100644 index 000000000000..d87b9cde4405 --- /dev/null +++ b/lib/Headers/ammintrin.h @@ -0,0 +1,68 @@ +/*===---- ammintrin.h - SSE4a intrinsics -----------------------------------=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __AMMINTRIN_H +#define __AMMINTRIN_H + +#ifndef __SSE4A__ +#error "SSE4A instruction set not enabled" +#else + +#include <pmmintrin.h> + +#define _mm_extracti_si64(x, len, idx) \ + ((__m128i)__builtin_ia32_extrqi((__v2di)(__m128i)(x), \ + (char)(len), (char)(idx))) + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_extract_si64(__m128i __x, __m128i __y) +{ + return (__m128i)__builtin_ia32_extrq((__v2di)__x, (__v16qi)__y); +} + +#define _mm_inserti_si64(x, y, len, idx) \ + ((__m128i)__builtin_ia32_insertqi((__v2di)(__m128i)(x), \ + (__v2di)(__m128i)(y), \ + (char)(len), (char)(idx))) + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_insert_si64(__m128i __x, __m128i __y) +{ + return (__m128i)__builtin_ia32_insertq((__v2di)__x, (__v2di)__y); +} + +static __inline__ void __attribute__((__always_inline__, __nodebug__)) +_mm_stream_sd(double *__p, __m128d __a) +{ + __builtin_ia32_movntsd(__p, (__v2df)__a); +} + +static __inline__ void __attribute__((__always_inline__, __nodebug__)) +_mm_stream_ss(float *__p, __m128 __a) +{ + __builtin_ia32_movntss(__p, (__v4sf)__a); +} + +#endif /* __SSE4A__ */ + +#endif /* __AMMINTRIN_H */ diff --git a/lib/Headers/avx2intrin.h b/lib/Headers/avx2intrin.h index 884c46d4d689..2c53aedffd1d 100644 --- a/lib/Headers/avx2intrin.h +++ b/lib/Headers/avx2intrin.h @@ -959,3 +959,243 @@ _mm_srlv_epi64(__m128i __X, __m128i __Y) { return (__m128i)__builtin_ia32_psrlv2di(__X, __Y); } + +#define _mm_mask_i32gather_pd(a, m, i, mask, s) __extension__ ({ \ + __m128d __a = (a); \ + double const *__m = (m); \ + __m128i __i = (i); \ + __m128d __mask = (mask); \ + (__m128d)__builtin_ia32_gatherd_pd((__v2df)__a, (const __v2df *)__m, \ + (__v4si)__i, (__v2df)__mask, (s)); }) + +#define _mm256_mask_i32gather_pd(a, m, i, mask, s) __extension__ ({ \ + __m256d __a = (a); \ + double const *__m = (m); \ + __m128i __i = (i); \ + __m256d __mask = (mask); \ + (__m256d)__builtin_ia32_gatherd_pd256((__v4df)__a, (const __v4df *)__m, \ + (__v4si)__i, (__v4df)__mask, (s)); }) + +#define _mm_mask_i64gather_pd(a, m, i, mask, s) __extension__ ({ \ + __m128d __a = (a); \ + double const *__m = (m); \ + __m128i __i = (i); \ + __m128d __mask = (mask); \ + (__m128d)__builtin_ia32_gatherq_pd((__v2df)__a, (const __v2df *)__m, \ + (__v2di)__i, (__v2df)__mask, (s)); }) + +#define _mm256_mask_i64gather_pd(a, m, i, mask, s) __extension__ ({ \ + __m256d __a = (a); \ + double const *__m = (m); \ + __m256i __i = (i); \ + __m256d __mask = (mask); \ + (__m256d)__builtin_ia32_gatherq_pd256((__v4df)__a, (const __v4df *)__m, \ + (__v4di)__i, (__v4df)__mask, (s)); }) + +#define _mm_mask_i32gather_ps(a, m, i, mask, s) __extension__ ({ \ + __m128 __a = (a); \ + float const *__m = (m); \ + __m128i __i = (i); \ + __m128 __mask = (mask); \ + (__m128)__builtin_ia32_gatherd_ps((__v4sf)__a, (const __v4sf *)__m, \ + (__v4si)__i, (__v4sf)__mask, (s)); }) + +#define _mm256_mask_i32gather_ps(a, m, i, mask, s) __extension__ ({ \ + __m256 __a = (a); \ + float const *__m = (m); \ + __m256i __i = (i); \ + __m256 __mask = (mask); \ + (__m256)__builtin_ia32_gatherd_ps256((__v8sf)__a, (const __v8sf *)__m, \ + (__v8si)__i, (__v8sf)__mask, (s)); }) + +#define _mm_mask_i64gather_ps(a, m, i, mask, s) __extension__ ({ \ + __m128 __a = (a); \ + float const *__m = (m); \ + __m128i __i = (i); \ + __m128 __mask = (mask); \ + (__m128)__builtin_ia32_gatherq_ps((__v4sf)__a, (const __v4sf *)__m, \ + (__v2di)__i, (__v4sf)__mask, (s)); }) + +#define _mm256_mask_i64gather_ps(a, m, i, mask, s) __extension__ ({ \ + __m128 __a = (a); \ + float const *__m = (m); \ + __m256i __i = (i); \ + __m128 __mask = (mask); \ + (__m128)__builtin_ia32_gatherq_ps256((__v4sf)__a, (const __v4sf *)__m, \ + (__v4di)__i, (__v4sf)__mask, (s)); }) + +#define _mm_mask_i32gather_epi32(a, m, i, mask, s) __extension__ ({ \ + __m128i __a = (a); \ + int const *__m = (m); \ + __m128i __i = (i); \ + __m128i __mask = (mask); \ + (__m128i)__builtin_ia32_gatherd_d((__v4si)__a, (const __v4si *)__m, \ + (__v4si)__i, (__v4si)__mask, (s)); }) + +#define _mm256_mask_i32gather_epi32(a, m, i, mask, s) __extension__ ({ \ + __m256i __a = (a); \ + int const *__m = (m); \ + __m256i __i = (i); \ + __m256i __mask = (mask); \ + (__m256i)__builtin_ia32_gatherd_d256((__v8si)__a, (const __v8si *)__m, \ + (__v8si)__i, (__v8si)__mask, (s)); }) + +#define _mm_mask_i64gather_epi32(a, m, i, mask, s) __extension__ ({ \ + __m128i __a = (a); \ + int const *__m = (m); \ + __m128i __i = (i); \ + __m128i __mask = (mask); \ + (__m128i)__builtin_ia32_gatherq_d((__v4si)__a, (const __v4si *)__m, \ + (__v2di)__i, (__v4si)__mask, (s)); }) + +#define _mm256_mask_i64gather_epi32(a, m, i, mask, s) __extension__ ({ \ + __m128i __a = (a); \ + int const *__m = (m); \ + __m256i __i = (i); \ + __m128i __mask = (mask); \ + (__m128i)__builtin_ia32_gatherq_d256((__v4si)__a, (const __v4si *)__m, \ + (__v4di)__i, (__v4si)__mask, (s)); }) + +#define _mm_mask_i32gather_epi64(a, m, i, mask, s) __extension__ ({ \ + __m128i __a = (a); \ + int const *__m = (m); \ + __m128i __i = (i); \ + __m128i __mask = (mask); \ + (__m128i)__builtin_ia32_gatherd_q((__v2di)__a, (const __v2di *)__m, \ + (__v4si)__i, (__v2di)__mask, (s)); }) + +#define _mm256_mask_i32gather_epi64(a, m, i, mask, s) __extension__ ({ \ + __m256i __a = (a); \ + int const *__m = (m); \ + __m128i __i = (i); \ + __m256i __mask = (mask); \ + (__m256i)__builtin_ia32_gatherd_q256((__v4di)__a, (const __v4di *)__m, \ + (__v4si)__i, (__v4di)__mask, (s)); }) + +#define _mm_mask_i64gather_epi64(a, m, i, mask, s) __extension__ ({ \ + __m128i __a = (a); \ + int const *__m = (m); \ + __m128i __i = (i); \ + __m128i __mask = (mask); \ + (__m128i)__builtin_ia32_gatherq_q((__v2di)__a, (const __v2di *)__m, \ + (__v2di)__i, (__v2di)__mask, (s)); }) + +#define _mm256_mask_i64gather_epi64(a, m, i, mask, s) __extension__ ({ \ + __m256i __a = (a); \ + int const *__m = (m); \ + __m256i __i = (i); \ + __m256i __mask = (mask); \ + (__m256i)__builtin_ia32_gatherq_q256((__v4di)__a, (const __v4di *)__m, \ + (__v4di)__i, (__v4di)__mask, (s)); }) + +#define _mm_i32gather_pd(m, i, s) __extension__ ({ \ + double const *__m = (m); \ + __m128i __i = (i); \ + (__m128d)__builtin_ia32_gatherd_pd((__v2df)_mm_setzero_pd(), \ + (const __v2df *)__m, (__v4si)__i, \ + (__v2df)_mm_set1_pd((double)(long long int)-1), (s)); }) + +#define _mm256_i32gather_pd(m, i, s) __extension__ ({ \ + double const *__m = (m); \ + __m128i __i = (i); \ + (__m256d)__builtin_ia32_gatherd_pd256((__v4df)_mm256_setzero_pd(), \ + (const __v4df *)__m, (__v4si)__i, \ + (__v4df)_mm256_set1_pd((double)(long long int)-1), (s)); }) + +#define _mm_i64gather_pd(m, i, s) __extension__ ({ \ + double const *__m = (m); \ + __m128i __i = (i); \ + (__m128d)__builtin_ia32_gatherq_pd((__v2df)_mm_setzero_pd(), \ + (const __v2df *)__m, (__v2di)__i, \ + (__v2df)_mm_set1_pd((double)(long long int)-1), (s)); }) + +#define _mm256_i64gather_pd(m, i, s) __extension__ ({ \ + double const *__m = (m); \ + __m256i __i = (i); \ + (__m256d)__builtin_ia32_gatherq_pd256((__v4df)_mm256_setzero_pd(), \ + (const __v4df *)__m, (__v4di)__i, \ + (__v4df)_mm256_set1_pd((double)(long long int)-1), (s)); }) + +#define _mm_i32gather_ps(m, i, s) __extension__ ({ \ + float const *__m = (m); \ + __m128i __i = (i); \ + (__m128)__builtin_ia32_gatherd_ps((__v4sf)_mm_setzero_ps(), \ + (const __v4sf *)__m, (__v4si)__i, \ + (__v4sf)_mm_set1_ps((float)(int)-1), (s)); }) + +#define _mm256_i32gather_ps(m, i, s) __extension__ ({ \ + float const *__m = (m); \ + __m256i __i = (i); \ + (__m256)__builtin_ia32_gatherd_ps256((__v8sf)_mm256_setzero_ps(), \ + (const __v8sf *)__m, (__v8si)__i, \ + (__v8sf)_mm256_set1_ps((float)(int)-1), (s)); }) + +#define _mm_i64gather_ps(m, i, s) __extension__ ({ \ + float const *__m = (m); \ + __m128i __i = (i); \ + (__m128)__builtin_ia32_gatherq_ps((__v4sf)_mm_setzero_ps(), \ + (const __v4sf *)__m, (__v2di)__i, \ + (__v4sf)_mm_set1_ps((float)(int)-1), (s)); }) + +#define _mm256_i64gather_ps(m, i, s) __extension__ ({ \ + float const *__m = (m); \ + __m256i __i = (i); \ + (__m128)__builtin_ia32_gatherq_ps256((__v4sf)_mm_setzero_ps(), \ + (const __v4sf *)__m, (__v4di)__i, \ + (__v4sf)_mm_set1_ps((float)(int)-1), (s)); }) + +#define _mm_i32gather_epi32(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m128i __i = (i); \ + (__m128i)__builtin_ia32_gatherd_d((__v4si)_mm_setzero_si128(), \ + (const __v4si *)__m, (__v4si)__i, \ + (__v4si)_mm_set1_epi32(-1), (s)); }) + +#define _mm256_i32gather_epi32(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m256i __i = (i); \ + (__m256i)__builtin_ia32_gatherd_d256((__v8si)_mm256_setzero_si256(), \ + (const __v8si *)__m, (__v8si)__i, \ + (__v8si)_mm256_set1_epi32(-1), (s)); }) + +#define _mm_i64gather_epi32(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m128i __i = (i); \ + (__m128i)__builtin_ia32_gatherq_d((__v4si)_mm_setzero_si128(), \ + (const __v4si *)__m, (__v2di)__i, \ + (__v4si)_mm_set1_epi32(-1), (s)); }) + +#define _mm256_i64gather_epi32(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m256i __i = (i); \ + (__m128i)__builtin_ia32_gatherq_d256((__v4si)_mm_setzero_si128(), \ + (const __v4si *)__m, (__v4di)__i, \ + (__v4si)_mm_set1_epi32(-1), (s)); }) + +#define _mm_i32gather_epi64(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m128i __i = (i); \ + (__m128i)__builtin_ia32_gatherd_q((__v2di)_mm_setzero_si128(), \ + (const __v2di *)__m, (__v4si)__i, \ + (__v2di)_mm_set1_epi64x(-1), (s)); }) + +#define _mm256_i32gather_epi64(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m128i __i = (i); \ + (__m256i)__builtin_ia32_gatherd_q256((__v4di)_mm256_setzero_si256(), \ + (const __v4di *)__m, (__v4si)__i, \ + (__v4di)_mm256_set1_epi64x(-1), (s)); }) + +#define _mm_i64gather_epi64(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m128i __i = (i); \ + (__m128i)__builtin_ia32_gatherq_q((__v2di)_mm_setzero_si128(), \ + (const __v2di *)__m, (__v2di)__i, \ + (__v2di)_mm_set1_epi64x(-1), (s)); }) + +#define _mm256_i64gather_epi64(m, i, s) __extension__ ({ \ + int const *__m = (m); \ + __m256i __i = (i); \ + (__m256i)__builtin_ia32_gatherq_q256((__v4di)_mm256_setzero_si256(), \ + (const __v4di *)__m, (__v4di)__i, \ + (__v4di)_mm256_set1_epi64x(-1), (s)); }) diff --git a/lib/Headers/bmiintrin.h b/lib/Headers/bmiintrin.h index 2f7db73a7d98..8cb00f51d3ef 100644 --- a/lib/Headers/bmiintrin.h +++ b/lib/Headers/bmiintrin.h @@ -33,7 +33,7 @@ #define __BMIINTRIN_H static __inline__ unsigned short __attribute__((__always_inline__, __nodebug__)) -__tzcnt16(unsigned short __X) +__tzcnt_u16(unsigned short __X) { return __builtin_ctzs(__X); } @@ -69,7 +69,7 @@ __blsr_u32(unsigned int __X) } static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__)) -__tzcnt32(unsigned int __X) +__tzcnt_u32(unsigned int __X) { return __builtin_ctz(__X); } @@ -106,7 +106,7 @@ __blsr_u64(unsigned long long __X) } static __inline__ unsigned long long __attribute__((__always_inline__, __nodebug__)) -__tzcnt64(unsigned long long __X) +__tzcnt_u64(unsigned long long __X) { return __builtin_ctzll(__X); } diff --git a/lib/Headers/emmintrin.h b/lib/Headers/emmintrin.h index e10b77d1b234..91395ed16f7f 100644 --- a/lib/Headers/emmintrin.h +++ b/lib/Headers/emmintrin.h @@ -1186,7 +1186,10 @@ _mm_maskmoveu_si128(__m128i d, __m128i n, char *p) static __inline__ void __attribute__((__always_inline__, __nodebug__)) _mm_storel_epi64(__m128i *p, __m128i a) { - __builtin_ia32_storelv4si((__v2si *)p, a); + struct __mm_storel_epi64_struct { + long long u; + } __attribute__((__packed__, __may_alias__)); + ((struct __mm_storel_epi64_struct*)p)->u = a[0]; } static __inline__ void __attribute__((__always_inline__, __nodebug__)) diff --git a/lib/Headers/float.h b/lib/Headers/float.h index 65b517d556f8..2cb13d3ed551 100644 --- a/lib/Headers/float.h +++ b/lib/Headers/float.h @@ -28,7 +28,7 @@ * additional definitions provided for Windows. * For more details see http://msdn.microsoft.com/en-us/library/y0ybw9fy.aspx */ -#if defined(__MINGW32__) && \ +#if (defined(__MINGW32__) || defined(_MSC_VER)) && \ defined(__has_include_next) && __has_include_next(<float.h>) # include_next <float.h> diff --git a/lib/Headers/fmaintrin.h b/lib/Headers/fmaintrin.h new file mode 100644 index 000000000000..6bfd5a88b3ce --- /dev/null +++ b/lib/Headers/fmaintrin.h @@ -0,0 +1,229 @@ +/*===---- fma4intrin.h - FMA4 intrinsics -----------------------------------=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __IMMINTRIN_H +#error "Never use <fmaintrin.h> directly; include <immintrin.h> instead." +#endif + +#ifndef __FMAINTRIN_H +#define __FMAINTRIN_H + +#ifndef __FMA__ +# error "FMA instruction set is not enabled" +#else + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmadd_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmaddps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmadd_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmaddpd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmadd_ss(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmaddss(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmadd_sd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmaddsd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmsub_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmsubps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmsub_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmsubpd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmsub_ss(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmsubss(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmsub_sd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmsubsd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fnmadd_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfnmaddps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fnmadd_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfnmaddpd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fnmadd_ss(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfnmaddss(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fnmadd_sd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfnmaddsd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fnmsub_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfnmsubps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fnmsub_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfnmsubpd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fnmsub_ss(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfnmsubss(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fnmsub_sd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfnmsubsd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmaddsub_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmaddsubps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmaddsub_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmaddsubpd(__A, __B, __C); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_fmsubadd_ps(__m128 __A, __m128 __B, __m128 __C) +{ + return (__m128)__builtin_ia32_vfmsubaddps(__A, __B, __C); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_fmsubadd_pd(__m128d __A, __m128d __B, __m128d __C) +{ + return (__m128d)__builtin_ia32_vfmsubaddpd(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fmadd_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfmaddps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fmadd_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfmaddpd256(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fmsub_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfmsubps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fmsub_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfmsubpd256(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fnmadd_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfnmaddps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fnmadd_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfnmaddpd256(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fnmsub_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfnmsubps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fnmsub_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfnmsubpd256(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fmaddsub_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfmaddsubps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fmaddsub_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfmaddsubpd256(__A, __B, __C); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_fmsubadd_ps(__m256 __A, __m256 __B, __m256 __C) +{ + return (__m256)__builtin_ia32_vfmsubaddps256(__A, __B, __C); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_fmsubadd_pd(__m256d __A, __m256d __B, __m256d __C) +{ + return (__m256d)__builtin_ia32_vfmsubaddpd256(__A, __B, __C); +} + +#endif /* __FMA__ */ + +#endif /* __FMAINTRIN_H */ diff --git a/lib/Headers/immintrin.h b/lib/Headers/immintrin.h index 16055251dd0a..15b65f3fd8c6 100644 --- a/lib/Headers/immintrin.h +++ b/lib/Headers/immintrin.h @@ -72,4 +72,30 @@ #include <lzcntintrin.h> #endif +#ifdef __FMA__ +#include <fmaintrin.h> +#endif + +#ifdef __RDRND__ +static __inline__ int __attribute__((__always_inline__, __nodebug__)) +_rdrand16_step(unsigned short *__p) +{ + return __builtin_ia32_rdrand16_step(__p); +} + +static __inline__ int __attribute__((__always_inline__, __nodebug__)) +_rdrand32_step(unsigned int *__p) +{ + return __builtin_ia32_rdrand32_step(__p); +} + +#ifdef __x86_64__ +static __inline__ int __attribute__((__always_inline__, __nodebug__)) +_rdrand64_step(unsigned long long *__p) +{ + return __builtin_ia32_rdrand64_step(__p); +} +#endif +#endif /* __RDRND__ */ + #endif /* __IMMINTRIN_H */ diff --git a/lib/Headers/stddef.h b/lib/Headers/stddef.h index 9e87ee89b3b9..eb919b57bcb9 100644 --- a/lib/Headers/stddef.h +++ b/lib/Headers/stddef.h @@ -43,10 +43,20 @@ typedef __WCHAR_TYPE__ wchar_t; #undef NULL #ifdef __cplusplus -#undef __null // VC++ hack. -#define NULL __null +# if !defined(__MINGW32__) && !defined(_MSC_VER) +# define NULL __null +# else +# define NULL 0 +# endif #else -#define NULL ((void*)0) +# define NULL ((void*)0) +#endif + +#ifdef __cplusplus +#if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED) +namespace std { typedef decltype(nullptr) nullptr_t; } +using ::std::nullptr_t; +#endif #endif #define offsetof(t, d) __builtin_offsetof(t, d) diff --git a/lib/Headers/wmmintrin.h b/lib/Headers/wmmintrin.h index 8f588507ee56..dca896fd65d8 100644 --- a/lib/Headers/wmmintrin.h +++ b/lib/Headers/wmmintrin.h @@ -24,11 +24,13 @@ #ifndef _WMMINTRIN_H #define _WMMINTRIN_H -#if !defined (__AES__) -# error "AES instructions not enabled" +#include <emmintrin.h> + +#if !defined (__AES__) && !defined (__PCLMUL__) +# error "AES/PCLMUL instructions not enabled" #else -#include <xmmintrin.h> +#ifdef __AES__ static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) _mm_aesenc_si128(__m128i __V, __m128i __R) @@ -64,4 +66,14 @@ _mm_aesimc_si128(__m128i __V) __builtin_ia32_aeskeygenassist128((C), (R)) #endif /* __AES__ */ + +#ifdef __PCLMUL__ + +#define _mm_clmulepi64_si128(__X, __Y, __I) \ + ((__m128i)__builtin_ia32_pclmulqdq128((__v2di)(__m128i)(__X), \ + (__v2di)(__m128i)(__Y), (char)(__I))) + +#endif /* __PCLMUL__ */ + +#endif /* __AES__ || __PCLMUL__ */ #endif /* _WMMINTRIN_H */ diff --git a/lib/Headers/x86intrin.h b/lib/Headers/x86intrin.h index f5e4d883e8b2..556cd011f05d 100644 --- a/lib/Headers/x86intrin.h +++ b/lib/Headers/x86intrin.h @@ -46,10 +46,18 @@ #include <popcntintrin.h> #endif +#ifdef __SSE4A__ +#include <ammintrin.h> +#endif + #ifdef __FMA4__ #include <fma4intrin.h> #endif -// FIXME: SSE4A, XOP, LWP, ABM +#ifdef __XOP__ +#include <xopintrin.h> +#endif + +// FIXME: LWP #endif /* __X86INTRIN_H */ diff --git a/lib/Headers/xopintrin.h b/lib/Headers/xopintrin.h new file mode 100644 index 000000000000..d107be4a426d --- /dev/null +++ b/lib/Headers/xopintrin.h @@ -0,0 +1,411 @@ +/*===---- xopintrin.h - FMA4 intrinsics ------------------------------------=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __X86INTRIN_H +#error "Never use <fma4intrin.h> directly; include <x86intrin.h> instead." +#endif + +#ifndef __XOPINTRIN_H +#define __XOPINTRIN_H + +#ifndef __XOP__ +# error "XOP instruction set is not enabled" +#else + +#include <fma4intrin.h> + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccs_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacssww((__v8hi)__A, (__v8hi)__B, (__v8hi)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_macc_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacsww((__v8hi)__A, (__v8hi)__B, (__v8hi)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccsd_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacsswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccd_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccs_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacssdd((__v4si)__A, (__v4si)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_macc_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacsdd((__v4si)__A, (__v4si)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccslo_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacssdql((__v4si)__A, (__v4si)__B, (__v2di)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_macclo_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacsdql((__v4si)__A, (__v4si)__B, (__v2di)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maccshi_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacssdqh((__v4si)__A, (__v4si)__B, (__v2di)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_macchi_epi32(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmacsdqh((__v4si)__A, (__v4si)__B, (__v2di)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maddsd_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmadcsswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_maddd_epi16(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpmadcswd((__v8hi)__A, (__v8hi)__B, (__v4si)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddw_epi8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddbw((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddd_epi8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddbd((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epi8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddbq((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddd_epi16(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddwd((__v8hi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epi16(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddwq((__v8hi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epi32(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphadddq((__v4si)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddw_epu8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddubw((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddd_epu8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddubd((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epu8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddubq((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddd_epu16(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphadduwd((__v8hi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epu16(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphadduwq((__v8hi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_haddq_epu32(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphaddudq((__v4si)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_hsubw_epi8(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphsubbw((__v16qi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_hsubd_epi16(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphsubwd((__v8hi)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_hsubq_epi32(__m128i __A) +{ + return (__m128i)__builtin_ia32_vphsubdq((__v4si)__A); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_cmov_si128(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpcmov(__A, __B, __C); +} + +static __inline__ __m256i __attribute__((__always_inline__, __nodebug__)) +_mm256_cmov_si256(__m256i __A, __m256i __B, __m256i __C) +{ + return (__m256i)__builtin_ia32_vpcmov_256(__A, __B, __C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_perm_epi8(__m128i __A, __m128i __B, __m128i __C) +{ + return (__m128i)__builtin_ia32_vpperm((__v16qi)__A, (__v16qi)__B, (__v16qi)__C); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_rot_epi8(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vprotb((__v16qi)__A, (__v16qi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_rot_epi16(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vprotw((__v8hi)__A, (__v8hi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_rot_epi32(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vprotd((__v4si)__A, (__v4si)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_rot_epi64(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vprotq((__v2di)__A, (__v2di)__B); +} + +#define _mm_roti_epi8(A, N) __extension__ ({ \ + __m128i __A = (A); \ + (__m128i)__builtin_ia32_vprotbi((__v16qi)__A, (N)); }) + +#define _mm_roti_epi16(A, N) __extension__ ({ \ + __m128i __A = (A); \ + (__m128i)__builtin_ia32_vprotwi((__v8hi)__A, (N)); }) + +#define _mm_roti_epi32(A, N) __extension__ ({ \ + __m128i __A = (A); \ + (__m128i)__builtin_ia32_vprotdi((__v4si)__A, (N)); }) + +#define _mm_roti_epi64(A, N) __extension__ ({ \ + __m128i __A = (A); \ + (__m128i)__builtin_ia32_vprotqi((__v2di)__A, (N)); }) + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_shl_epi8(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshlb((__v16qi)__A, (__v16qi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_shl_epi16(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshlw((__v8hi)__A, (__v8hi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_shl_epi32(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshld((__v4si)__A, (__v4si)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_shl_epi64(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshlq((__v2di)__A, (__v2di)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_sha_epi8(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshab((__v16qi)__A, (__v16qi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_sha_epi16(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshaw((__v8hi)__A, (__v8hi)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_sha_epi32(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshad((__v4si)__A, (__v4si)__B); +} + +static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) +_mm_sha_epi64(__m128i __A, __m128i __B) +{ + return (__m128i)__builtin_ia32_vpshaq((__v2di)__A, (__v2di)__B); +} + +#define _mm_com_epu8(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomub((__v16qi)__A, (__v16qi)__B, (N)); }) + +#define _mm_com_epu16(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomuw((__v8hi)__A, (__v8hi)__B, (N)); }) + +#define _mm_com_epu32(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomud((__v4si)__A, (__v4si)__B, (N)); }) + +#define _mm_com_epu64(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomuq((__v2di)__A, (__v2di)__B, (N)); }) + +#define _mm_com_epi8(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomb((__v16qi)__A, (__v16qi)__B, (N)); }) + +#define _mm_com_epi16(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomw((__v8hi)__A, (__v8hi)__B, (N)); }) + +#define _mm_com_epi32(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomd((__v4si)__A, (__v4si)__B, (N)); }) + +#define _mm_com_epi64(A, B, N) __extension__ ({ \ + __m128i __A = (A); \ + __m128i __B = (B); \ + (__m128i)__builtin_ia32_vpcomq((__v2di)__A, (__v2di)__B, (N)); }) + +#define _mm_permute2_pd(X, Y, C, I) __extension__ ({ \ + __m128d __X = (X); \ + __m128d __Y = (Y); \ + __m128i __C = (C); \ + (__m128d)__builtin_ia32_vpermil2pd((__v2df)__X, (__v2df)__Y, \ + (__v2di)__C, (I)); }) + +#define _mm256_permute2_pd(X, Y, C, I) __extension__ ({ \ + __m256d __X = (X); \ + __m256d __Y = (Y); \ + __m256i __C = (C); \ + (__m256d)__builtin_ia32_vpermil2pd256((__v4df)__X, (__v4df)__Y, \ + (__v4di)__C, (I)); }) + +#define _mm_permute2_ps(X, Y, C, I) __extension__ ({ \ + __m128 __X = (X); \ + __m128 __Y = (Y); \ + __m128i __C = (C); \ + (__m128)__builtin_ia32_vpermil2ps((__v4sf)__X, (__v4sf)__Y, \ + (__v4si)__C, (I)); }) + +#define _mm256_permute2_ps(X, Y, C, I) __extension__ ({ \ + __m256 __X = (X); \ + __m256 __Y = (Y); \ + __m256i __C = (C); \ + (__m256)__builtin_ia32_vpermil2ps256((__v8sf)__X, (__v8sf)__Y, \ + (__v8si)__C, (I)); }) + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_frcz_ss(__m128 __A) +{ + return (__m128)__builtin_ia32_vfrczss((__v4sf)__A); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_frcz_sd(__m128d __A) +{ + return (__m128d)__builtin_ia32_vfrczsd((__v2df)__A); +} + +static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) +_mm_frcz_ps(__m128 __A) +{ + return (__m128)__builtin_ia32_vfrczps((__v4sf)__A); +} + +static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) +_mm_frcz_pd(__m128d __A) +{ + return (__m128d)__builtin_ia32_vfrczpd((__v2df)__A); +} + +static __inline__ __m256 __attribute__((__always_inline__, __nodebug__)) +_mm256_frcz_ps(__m256 __A) +{ + return (__m256)__builtin_ia32_vfrczps256((__v8sf)__A); +} + +static __inline__ __m256d __attribute__((__always_inline__, __nodebug__)) +_mm256_frcz_pd(__m256d __A) +{ + return (__m256d)__builtin_ia32_vfrczpd256((__v4df)__A); +} + +#endif /* __XOP__ */ + +#endif /* __XOPINTRIN_H */ diff --git a/lib/Lex/CMakeLists.txt b/lib/Lex/CMakeLists.txt index 0a2ffdbf232d..241abbc4bc68 100644 --- a/lib/Lex/CMakeLists.txt +++ b/lib/Lex/CMakeLists.txt @@ -2,8 +2,6 @@ set(LLVM_LINK_COMPONENTS support) -set(LLVM_USED_LIBS clangBasic) - add_clang_library(clangLex HeaderMap.cpp HeaderSearch.cpp @@ -28,4 +26,12 @@ add_clang_library(clangLex TokenLexer.cpp ) -add_dependencies(clangLex ClangDiagnosticLex ClangAttrSpellings) +add_dependencies(clangLex + ClangAttrSpellings + ClangDiagnosticCommon + ClangDiagnosticLex + ) + +target_link_libraries(clangLex + clangBasic + ) diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index d688e23fa405..bb3a67378ad5 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -84,7 +84,7 @@ void HeaderSearch::PrintStats() { } /// CreateHeaderMap - This method returns a HeaderMap for the specified -/// FileEntry, uniquing them through the the 'HeaderMaps' datastructure. +/// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) { // We expect the number of headermaps to be small, and almost always empty. // If it ever grows, use of a linear search should be re-evaluated. @@ -390,10 +390,10 @@ void HeaderSearch::setTarget(const TargetInfo &Target) { //===----------------------------------------------------------------------===// -/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file, +/// LookupFile - Given a "foo" or \<foo> reference, look up the indicated file, /// return null on failure. isAngled indicates whether the file reference is -/// for system #include's or not (i.e. using <> instead of ""). CurFileEnt, if -/// non-null, indicates where the #including file is, in case a relative search +/// for system \#include's or not (i.e. using <> instead of ""). CurFileEnt, if +/// non-null, indicates where the \#including file is, in case a relative search /// is needed. const FileEntry *HeaderSearch::LookupFile( StringRef Filename, @@ -442,11 +442,19 @@ const FileEntry *HeaderSearch::LookupFile( // Leave CurDir unset. // This file is a system header or C++ unfriendly if the old file is. // - // Note that the temporary 'DirInfo' is required here, as either call to - // getFileInfo could resize the vector and we don't want to rely on order - // of evaluation. - unsigned DirInfo = getFileInfo(CurFileEnt).DirInfo; - getFileInfo(FE).DirInfo = DirInfo; + // Note that we only use one of FromHFI/ToHFI at once, due to potential + // reallocation of the underlying vector potentially making the first + // reference binding dangling. + HeaderFileInfo &FromHFI = getFileInfo(CurFileEnt); + unsigned DirInfo = FromHFI.DirInfo; + bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader; + StringRef Framework = FromHFI.Framework; + + HeaderFileInfo &ToHFI = getFileInfo(FE); + ToHFI.DirInfo = DirInfo; + ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader; + ToHFI.Framework = Framework; + if (SearchPath != NULL) { StringRef SearchPathRef(CurFileEnt->getDir()->getName()); SearchPath->clear(); @@ -510,6 +518,16 @@ const FileEntry *HeaderSearch::LookupFile( if (HFI.DirInfo == SrcMgr::C_User && InUserSpecifiedSystemFramework) HFI.DirInfo = SrcMgr::C_System; + // If the filename matches a known system header prefix, override + // whether the file is a system header. + for (unsigned j = SystemHeaderPrefixes.size(); j; --j) { + if (Filename.startswith(SystemHeaderPrefixes[j-1].first)) { + HFI.DirInfo = SystemHeaderPrefixes[j-1].second ? SrcMgr::C_System + : SrcMgr::C_User; + break; + } + } + // If this file is found in a header map and uses the framework style of // includes, then this header is part of a framework we're building. if (CurDir->isIndexHeaderMap()) { @@ -556,7 +574,7 @@ const FileEntry *HeaderSearch::LookupFile( } /// LookupSubframeworkHeader - Look up a subframework for the specified -/// #include file. For example, if #include'ing <HIToolbox/HIToolbox.h> from +/// \#include file. For example, if \#include'ing <HIToolbox/HIToolbox.h> from /// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox /// is a subframework within Carbon.framework. If so, return the FileEntry /// for the designated file, otherwise return null. @@ -739,9 +757,6 @@ void HeaderSearch::setHeaderFileInfoForUID(HeaderFileInfo HFI, unsigned UID) { FileInfo[UID] = HFI; } -/// ShouldEnterIncludeFile - Mark the specified file as a target of of a -/// #include, #include_next, or #import directive. Return false if #including -/// the file will have no effect or true if we should include it. bool HeaderSearch::ShouldEnterIncludeFile(const FileEntry *File, bool isImport){ ++NumIncluded; // Count # of attempted #includes. @@ -1032,4 +1047,3 @@ void HeaderSearch::collectAllModules(llvm::SmallVectorImpl<Module *> &Modules) { Modules.push_back(M->getValue()); } } - diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 535a852429b5..5212dd86e7a1 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -127,7 +127,7 @@ Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *InputFile, Preprocessor &PP) } /// Lexer constructor - Create a new raw lexer object. This object is only -/// suitable for calls to 'LexRawToken'. This lexer assumes that the text +/// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the text /// range will outlive it, so it doesn't take ownership of it. Lexer::Lexer(SourceLocation fileloc, const LangOptions &langOpts, const char *BufStart, const char *BufPtr, const char *BufEnd) @@ -140,7 +140,7 @@ Lexer::Lexer(SourceLocation fileloc, const LangOptions &langOpts, } /// Lexer constructor - Create a new raw lexer object. This object is only -/// suitable for calls to 'LexRawToken'. This lexer assumes that the text +/// suitable for calls to 'LexFromRawLexer'. This lexer assumes that the text /// range will outlive it, so it doesn't take ownership of it. Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *FromFile, const SourceManager &SM, const LangOptions &langOpts) @@ -544,7 +544,6 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, if (InPreprocessorDirective) { // If we've hit the end of the file, we're done. if (TheTok.getKind() == tok::eof) { - InPreprocessorDirective = false; break; } @@ -820,10 +819,6 @@ static CharSourceRange makeRangeFromFileLocs(CharSourceRange Range, return CharSourceRange::getCharRange(Begin, End); } -/// \brief Accepts a range and returns a character range with file locations. -/// -/// Returns a null range if a part of the range resides inside a macro -/// expansion or the range does not reside on the same FileID. CharSourceRange Lexer::makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts) { @@ -1091,20 +1086,21 @@ static inline bool isIdentifierBody(unsigned char c) { } /// isHorizontalWhitespace - Return true if this character is horizontal -/// whitespace: ' ', '\t', '\f', '\v'. Note that this returns false for '\0'. +/// whitespace: ' ', '\\t', '\\f', '\\v'. Note that this returns false for +/// '\\0'. static inline bool isHorizontalWhitespace(unsigned char c) { return (CharInfo[c] & CHAR_HORZ_WS) ? true : false; } /// isVerticalWhitespace - Return true if this character is vertical -/// whitespace: '\n', '\r'. Note that this returns false for '\0'. +/// whitespace: '\\n', '\\r'. Note that this returns false for '\\0'. static inline bool isVerticalWhitespace(unsigned char c) { return (CharInfo[c] & CHAR_VERT_WS) ? true : false; } /// isWhitespace - Return true if this character is horizontal or vertical -/// whitespace: ' ', '\t', '\f', '\v', '\n', '\r'. Note that this returns false -/// for '\0'. +/// whitespace: ' ', '\\t', '\\f', '\\v', '\\n', '\\r'. Note that this returns +/// false for '\\0'. static inline bool isWhitespace(unsigned char c) { return (CharInfo[c] & (CHAR_HORZ_WS|CHAR_VERT_WS)) ? true : false; } @@ -1124,6 +1120,11 @@ static inline bool isRawStringDelimBody(unsigned char c) { true : false; } +// Allow external clients to make use of CharInfo. +bool Lexer::isIdentifierBodyChar(char c, const LangOptions &LangOpts) { + return isIdentifierBody(c) || (c == '$' && LangOpts.DollarIdents); +} + //===----------------------------------------------------------------------===// // Diagnostics forwarding code. @@ -1564,8 +1565,20 @@ void Lexer::LexNumericConstant(Token &Result, const char *CurPtr) { } // If we have a hex FP constant, continue. - if ((C == '-' || C == '+') && (PrevCh == 'P' || PrevCh == 'p')) - return LexNumericConstant(Result, ConsumeChar(CurPtr, Size, Result)); + if ((C == '-' || C == '+') && (PrevCh == 'P' || PrevCh == 'p')) { + // Outside C99, we accept hexadecimal floating point numbers as a + // not-quite-conforming extension. Only do so if this looks like it's + // actually meant to be a hexfloat, and not if it has a ud-suffix. + bool IsHexFloat = true; + if (!LangOpts.C99) { + if (!isHexaLiteral(BufferPtr, LangOpts)) + IsHexFloat = false; + else if (std::find(BufferPtr, CurPtr, '_') != CurPtr) + IsHexFloat = false; + } + if (IsHexFloat) + return LexNumericConstant(Result, ConsumeChar(CurPtr, Size, Result)); + } // Update the location of token as well as BufferPtr. const char *TokStart = BufferPtr; @@ -1635,7 +1648,7 @@ void Lexer::LexStringLiteral(Token &Result, const char *CurPtr, if (C == '\n' || C == '\r' || // Newline. (C == 0 && CurPtr-1 == BufferEnd)) { // End of file. if (!isLexingRawMode() && !LangOpts.AsmPreprocessor) - Diag(BufferPtr, diag::warn_unterminated_string); + Diag(BufferPtr, diag::ext_unterminated_string); FormTokenWithChars(Result, CurPtr-1, tok::unknown); return; } @@ -1755,7 +1768,7 @@ void Lexer::LexAngledStringLiteral(Token &Result, const char *CurPtr) { // Skip escaped characters. if (C == '\\') { // Skip the escaped character. - C = getAndAdvanceChar(CurPtr, Result); + getAndAdvanceChar(CurPtr, Result); } else if (C == '\n' || C == '\r' || // Newline. (C == 0 && (CurPtr-1 == BufferEnd || // End of file. isCodeCompletionPoint(CurPtr-1)))) { @@ -1793,7 +1806,7 @@ void Lexer::LexCharConstant(Token &Result, const char *CurPtr, char C = getAndAdvanceChar(CurPtr, Result); if (C == '\'') { if (!isLexingRawMode() && !LangOpts.AsmPreprocessor) - Diag(BufferPtr, diag::err_empty_character); + Diag(BufferPtr, diag::ext_empty_character); FormTokenWithChars(Result, CurPtr, tok::unknown); return; } @@ -1803,11 +1816,11 @@ void Lexer::LexCharConstant(Token &Result, const char *CurPtr, if (C == '\\') { // Skip the escaped character. // FIXME: UCN's - C = getAndAdvanceChar(CurPtr, Result); + getAndAdvanceChar(CurPtr, Result); } else if (C == '\n' || C == '\r' || // Newline. (C == 0 && CurPtr-1 == BufferEnd)) { // End of file. if (!isLexingRawMode() && !LangOpts.AsmPreprocessor) - Diag(BufferPtr, diag::warn_unterminated_char); + Diag(BufferPtr, diag::ext_unterminated_char); FormTokenWithChars(Result, CurPtr-1, tok::unknown); return; } else if (C == 0) { @@ -1924,8 +1937,6 @@ bool Lexer::SkipBCPLComment(Token &Result, const char *CurPtr) { CurPtr = EscapePtr-2; else break; // This is a newline, we're done. - - C = *CurPtr; } // Otherwise, this is a hard case. Fall back on getAndAdvanceChar to @@ -2022,7 +2033,7 @@ bool Lexer::SaveBCPLComment(Token &Result, const char *CurPtr) { // directly. FormTokenWithChars(Result, CurPtr, tok::comment); - if (!ParsingPreprocessorDirective) + if (!ParsingPreprocessorDirective || LexingRawMode) return true; // If this BCPL-style comment is in a macro definition, transmogrify it into @@ -2043,8 +2054,8 @@ bool Lexer::SaveBCPLComment(Token &Result, const char *CurPtr) { } /// isBlockCommentEndOfEscapedNewLine - Return true if the specified newline -/// character (either \n or \r) is part of an escaped newline sequence. Issue a -/// diagnostic if so. We know that the newline is inside of a block comment. +/// character (either \\n or \\r) is part of an escaped newline sequence. Issue +/// a diagnostic if so. We know that the newline is inside of a block comment. static bool isEndOfBlockCommentWithEscapedNewLine(const char *CurPtr, Lexer *L) { assert(CurPtr[0] == '\n' || CurPtr[0] == '\r'); @@ -2110,12 +2121,12 @@ static bool isEndOfBlockCommentWithEscapedNewLine(const char *CurPtr, #undef bool #endif -/// SkipBlockComment - We have just read the /* characters from input. Read -/// until we find the */ characters that terminate the comment. Note that we -/// don't bother decoding trigraphs or escaped newlines in block comments, -/// because they cannot cause the comment to end. The only thing that can -/// happen is the comment could end with an escaped newline between the */ end -/// of comment. +/// We have just read from input the / and * characters that started a comment. +/// Read until we find the * and / characters that terminate the comment. +/// Note that we don't bother decoding trigraphs or escaped newlines in block +/// comments, because they cannot cause the comment to end. The only thing +/// that can happen is the comment could end with an escaped newline between +/// the terminating * and /. /// /// If we're in KeepCommentMode or any CommentHandler has inserted /// some tokens, this will store the first token and return true. @@ -2286,10 +2297,9 @@ bool Lexer::SkipBlockComment(Token &Result, const char *CurPtr) { /// ReadToEndOfLine - Read the rest of the current preprocessor line as an /// uninterpreted string. This switches the lexer out of directive mode. -std::string Lexer::ReadToEndOfLine() { +void Lexer::ReadToEndOfLine(SmallVectorImpl<char> *Result) { assert(ParsingPreprocessorDirective && ParsingFilename == false && "Must be in a preprocessing directive!"); - std::string Result; Token Tmp; // CurPtr - Cache BufferPtr in an automatic variable. @@ -2298,7 +2308,8 @@ std::string Lexer::ReadToEndOfLine() { char Char = getAndAdvanceChar(CurPtr, Tmp); switch (Char) { default: - Result += Char; + if (Result) + Result->push_back(Char); break; case 0: // Null. // Found end of file? @@ -2306,11 +2317,12 @@ std::string Lexer::ReadToEndOfLine() { if (isCodeCompletionPoint(CurPtr-1)) { PP->CodeCompleteNaturalLanguage(); cutOffLexing(); - return Result; + return; } // Nope, normal character, continue. - Result += Char; + if (Result) + Result->push_back(Char); break; } // FALL THROUGH. @@ -2329,8 +2341,8 @@ std::string Lexer::ReadToEndOfLine() { } assert(Tmp.is(tok::eod) && "Unexpected token!"); - // Finally, we're done, return the string we found. - return Result; + // Finally, we're done; + return; } } } @@ -2383,7 +2395,7 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) { BufferPtr = CurPtr; // Finally, let the preprocessor handle this. - return PP->HandleEndOfFile(Result); + return PP->HandleEndOfFile(Result, isPragmaLexer()); } /// isNextPPTokenLParen - Return 1 if the next unexpanded token lexed from @@ -2418,7 +2430,7 @@ unsigned Lexer::isNextPPTokenLParen() { return Tok.is(tok::l_paren); } -/// FindConflictEnd - Find the end of a version control conflict marker. +/// \brief Find the end of a version control conflict marker. static const char *FindConflictEnd(const char *CurPtr, const char *BufferEnd, ConflictMarkerKind CMK) { const char *Terminator = CMK == CMK_Perforce ? "<<<<\n" : ">>>>>>>"; @@ -2625,7 +2637,8 @@ LexNextToken: ParsingPreprocessorDirective = false; // Restore comment saving mode, in case it was disabled for directive. - SetCommentRetentionState(PP->getCommentRetentionState()); + if (PP) + SetCommentRetentionState(PP->getCommentRetentionState()); // Since we consumed a newline, we are back at the start of a line. IsAtStartOfLine = true; diff --git a/lib/Lex/LiteralSupport.cpp b/lib/Lex/LiteralSupport.cpp index c1d228b87989..9e3c7786a732 100644 --- a/lib/Lex/LiteralSupport.cpp +++ b/lib/Lex/LiteralSupport.cpp @@ -250,6 +250,39 @@ static bool ProcessUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf, return true; } +/// MeasureUCNEscape - Determine the number of bytes within the resulting string +/// which this UCN will occupy. +static int MeasureUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf, + const char *ThisTokEnd, unsigned CharByteWidth, + const LangOptions &Features, bool &HadError) { + // UTF-32: 4 bytes per escape. + if (CharByteWidth == 4) + return 4; + + uint32_t UcnVal = 0; + unsigned short UcnLen = 0; + FullSourceLoc Loc; + + if (!ProcessUCNEscape(ThisTokBegin, ThisTokBuf, ThisTokEnd, UcnVal, + UcnLen, Loc, 0, Features, true)) { + HadError = true; + return 0; + } + + // UTF-16: 2 bytes for BMP, 4 bytes otherwise. + if (CharByteWidth == 2) + return UcnVal <= 0xFFFF ? 2 : 4; + + // UTF-8. + if (UcnVal < 0x80) + return 1; + if (UcnVal < 0x800) + return 2; + if (UcnVal < 0x10000) + return 3; + return 4; +} + /// EncodeUCNEscape - Read the Universal Character Name, check constraints and /// convert the UTF32 to UTF8 or UTF16. This is a subroutine of /// StringLiteralParser. When we decide to implement UCN's for identifiers, @@ -265,7 +298,7 @@ static void EncodeUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf, unsigned short UcnLen = 0; if (!ProcessUCNEscape(ThisTokBegin, ThisTokBuf, ThisTokEnd, UcnVal, UcnLen, Loc, Diags, Features, true)) { - HadError = 1; + HadError = true; return; } @@ -289,7 +322,7 @@ static void EncodeUCNEscape(const char *ThisTokBegin, const char *&ThisTokBuf, // using reinterpret_cast. UTF16 *ResultPtr = reinterpret_cast<UTF16*>(ResultBuf); - if (UcnVal < (UTF32)0xFFFF) { + if (UcnVal <= (UTF32)0xFFFF) { *ResultPtr = UcnVal; ResultBuf += 2; return; @@ -756,6 +789,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) { } +/// \verbatim /// user-defined-character-literal: [C++11 lex.ext] /// character-literal ud-suffix /// ud-suffix: @@ -791,6 +825,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) { /// \U hex-quad hex-quad /// hex-quad: /// hex-digit hex-digit hex-digit hex-digit +/// \endverbatim /// CharLiteralParser::CharLiteralParser(const char *begin, const char *end, SourceLocation Loc, Preprocessor &PP, @@ -971,7 +1006,7 @@ CharLiteralParser::CharLiteralParser(const char *begin, const char *end, Value = (signed char)Value; } - +/// \verbatim /// string-literal: [C++0x lex.string] /// encoding-prefix " [s-char-sequence] " /// encoding-prefix R raw-string @@ -1023,6 +1058,7 @@ CharLiteralParser::CharLiteralParser(const char *begin, const char *end, /// \U hex-quad hex-quad /// hex-quad: /// hex-digit hex-digit hex-digit hex-digit +/// \endverbatim /// StringLiteralParser:: StringLiteralParser(const Token *StringToks, unsigned NumStringToks, @@ -1037,10 +1073,8 @@ StringLiteralParser(const Token *StringToks, unsigned NumStringToks, void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){ // The literal token may have come from an invalid source location (e.g. due // to a PCH error), in which case the token length will be 0. - if (NumStringToks == 0 || StringToks[0].getLength() < 2) { - hadError = true; - return; - } + if (NumStringToks == 0 || StringToks[0].getLength() < 2) + return DiagnoseLexingError(SourceLocation()); // Scan all of the string portions, remember the max individual token length, // computing a bound on the concatenated string length, and see whether any @@ -1057,10 +1091,8 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){ // Implement Translation Phase #6: concatenation of string literals /// (C99 5.1.1.2p1). The common case is only one string fragment. for (unsigned i = 1; i != NumStringToks; ++i) { - if (StringToks[i].getLength() < 2) { - hadError = true; - return; - } + if (StringToks[i].getLength() < 2) + return DiagnoseLexingError(StringToks[i].getLocation()); // The string could be shorter than this if it needs cleaning, but this is a // reasonable bound, which is all we need. @@ -1123,10 +1155,8 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){ unsigned ThisTokLen = Lexer::getSpelling(StringToks[i], ThisTokBuf, SM, Features, &StringInvalid); - if (StringInvalid) { - hadError = true; - continue; - } + if (StringInvalid) + return DiagnoseLexingError(StringToks[i].getLocation()); const char *ThisTokBegin = ThisTokBuf; const char *ThisTokEnd = ThisTokBuf+ThisTokLen; @@ -1192,7 +1222,11 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){ if (DiagnoseBadString(StringToks[i])) hadError = true; } else { - assert(ThisTokBuf[0] == '"' && "Expected quote, lexer broken?"); + if (ThisTokBuf[0] != '"') { + // The file may have come from PCH and then changed after loading the + // PCH; Fail gracefully. + return DiagnoseLexingError(StringToks[i].getLocation()); + } ++ThisTokBuf; // skip " // Check if this is a pascal string @@ -1296,45 +1330,10 @@ void StringLiteralParser::init(const Token *StringToks, unsigned NumStringToks){ } } - /// copyStringFragment - This function copies from Start to End into ResultPtr. /// Performs widening for multi-byte characters. bool StringLiteralParser::CopyStringFragment(StringRef Fragment) { - assert(CharByteWidth==1 || CharByteWidth==2 || CharByteWidth==4); - ConversionResult result = conversionOK; - // Copy the character span over. - if (CharByteWidth == 1) { - if (!isLegalUTF8String(reinterpret_cast<const UTF8*>(Fragment.begin()), - reinterpret_cast<const UTF8*>(Fragment.end()))) - result = sourceIllegal; - memcpy(ResultPtr, Fragment.data(), Fragment.size()); - ResultPtr += Fragment.size(); - } else if (CharByteWidth == 2) { - UTF8 const *sourceStart = (UTF8 const *)Fragment.data(); - // FIXME: Make the type of the result buffer correct instead of - // using reinterpret_cast. - UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr); - ConversionFlags flags = strictConversion; - result = ConvertUTF8toUTF16( - &sourceStart,sourceStart + Fragment.size(), - &targetStart,targetStart + 2*Fragment.size(),flags); - if (result==conversionOK) - ResultPtr = reinterpret_cast<char*>(targetStart); - } else if (CharByteWidth == 4) { - UTF8 const *sourceStart = (UTF8 const *)Fragment.data(); - // FIXME: Make the type of the result buffer correct instead of - // using reinterpret_cast. - UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr); - ConversionFlags flags = strictConversion; - result = ConvertUTF8toUTF32( - &sourceStart,sourceStart + Fragment.size(), - &targetStart,targetStart + 4*Fragment.size(),flags); - if (result==conversionOK) - ResultPtr = reinterpret_cast<char*>(targetStart); - } - assert((result != targetExhausted) - && "ConvertUTF8toUTFXX exhausted target buffer"); - return result != conversionOK; + return !ConvertUTF8toWide(CharByteWidth, Fragment, ResultPtr); } bool StringLiteralParser::DiagnoseBadString(const Token &Tok) { @@ -1349,6 +1348,12 @@ bool StringLiteralParser::DiagnoseBadString(const Token &Tok) { return !NoErrorOnBadEncoding; } +void StringLiteralParser::DiagnoseLexingError(SourceLocation Loc) { + hadError = true; + if (Diags) + Diags->Report(Loc, diag::err_lexing_string); +} + /// getOffsetOfStringByte - This function returns the offset of the /// specified byte of the string data represented by Token. This handles /// advancing over escape sequences in the string. @@ -1365,14 +1370,31 @@ unsigned StringLiteralParser::getOffsetOfStringByte(const Token &Tok, if (StringInvalid) return 0; + const char *SpellingStart = SpellingPtr; + const char *SpellingEnd = SpellingPtr+TokLen; + + // Handle UTF-8 strings just like narrow strings. + if (SpellingPtr[0] == 'u' && SpellingPtr[1] == '8') + SpellingPtr += 2; + assert(SpellingPtr[0] != 'L' && SpellingPtr[0] != 'u' && SpellingPtr[0] != 'U' && "Doesn't handle wide or utf strings yet"); + // For raw string literals, this is easy. + if (SpellingPtr[0] == 'R') { + assert(SpellingPtr[1] == '"' && "Should be a raw string literal!"); + // Skip 'R"'. + SpellingPtr += 2; + while (*SpellingPtr != '(') { + ++SpellingPtr; + assert(SpellingPtr < SpellingEnd && "Missing ( for raw string literal"); + } + // Skip '('. + ++SpellingPtr; + return SpellingPtr - SpellingStart + ByteNo; + } - const char *SpellingStart = SpellingPtr; - const char *SpellingEnd = SpellingPtr+TokLen; - - // Skip over the leading quote. + // Skip over the leading quote assert(SpellingPtr[0] == '"' && "Should be a string literal!"); ++SpellingPtr; @@ -1389,11 +1411,23 @@ unsigned StringLiteralParser::getOffsetOfStringByte(const Token &Tok, // Otherwise, this is an escape character. Advance over it. bool HadError = false; - ProcessCharEscape(SpellingPtr, SpellingEnd, HadError, - FullSourceLoc(Tok.getLocation(), SM), - CharByteWidth*8, Diags); + if (SpellingPtr[1] == 'u' || SpellingPtr[1] == 'U') { + const char *EscapePtr = SpellingPtr; + unsigned Len = MeasureUCNEscape(SpellingStart, SpellingPtr, SpellingEnd, + 1, Features, HadError); + if (Len > ByteNo) { + // ByteNo is somewhere within the escape sequence. + SpellingPtr = EscapePtr; + break; + } + ByteNo -= Len; + } else { + ProcessCharEscape(SpellingPtr, SpellingEnd, HadError, + FullSourceLoc(Tok.getLocation(), SM), + CharByteWidth*8, Diags); + --ByteNo; + } assert(!HadError && "This method isn't valid on erroneous strings"); - --ByteNo; } return SpellingPtr-SpellingStart; diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 625a204af995..74b9cbc881ac 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -6,9 +6,10 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This file implements # directive processing for the Preprocessor. -// +/// +/// \file +/// \brief Implements # directive processing for the Preprocessor. +/// //===----------------------------------------------------------------------===// #include "clang/Lex/Preprocessor.h" @@ -61,8 +62,8 @@ MacroInfo *Preprocessor::CloneMacroInfo(const MacroInfo &MacroToClone) { return MI; } -/// ReleaseMacroInfo - Release the specified MacroInfo. This memory will -/// be reused for allocating new MacroInfo objects. +/// \brief Release the specified MacroInfo to be reused for allocating +/// new MacroInfo objects. void Preprocessor::ReleaseMacroInfo(MacroInfo *MI) { MacroInfoChain *MIChain = (MacroInfoChain*) MI; if (MacroInfoChain *Prev = MIChain->Prev) { @@ -82,8 +83,8 @@ void Preprocessor::ReleaseMacroInfo(MacroInfo *MI) { MI->Destroy(); } -/// DiscardUntilEndOfDirective - Read and discard all tokens remaining on the -/// current line until the tok::eod token is found. +/// \brief Read and discard all tokens remaining on the current line until +/// the tok::eod token is found. void Preprocessor::DiscardUntilEndOfDirective() { Token Tmp; do { @@ -92,11 +93,13 @@ void Preprocessor::DiscardUntilEndOfDirective() { } while (Tmp.isNot(tok::eod)); } -/// ReadMacroName - Lex and validate a macro name, which occurs after a -/// #define or #undef. This sets the token kind to eod and discards the rest -/// of the macro line if the macro name is invalid. isDefineUndef is 1 if -/// this is due to a a #define, 2 if #undef directive, 0 if it is something -/// else (e.g. #ifdef). +/// \brief Lex and validate a macro name, which occurs after a +/// \#define or \#undef. +/// +/// This sets the token kind to eod and discards the rest +/// of the macro line if the macro name is invalid. \p isDefineUndef is 1 if +/// this is due to a a \#define, 2 if \#undef directive, 0 if it is something +/// else (e.g. \#ifdef). void Preprocessor::ReadMacroName(Token &MacroNameTok, char isDefineUndef) { // Read the token, don't allow macro expansion on it. LexUnexpandedToken(MacroNameTok); @@ -157,8 +160,9 @@ void Preprocessor::ReadMacroName(Token &MacroNameTok, char isDefineUndef) { return DiscardUntilEndOfDirective(); } -/// CheckEndOfDirective - Ensure that the next token is a tok::eod token. If -/// not, emit a diagnostic and consume up until the eod. If EnableMacros is +/// \brief Ensure that the next token is a tok::eod token. +/// +/// If not, emit a diagnostic and consume up until the eod. If EnableMacros is /// true, then we consider macros that expand to zero tokens as being ok. void Preprocessor::CheckEndOfDirective(const char *DirType, bool EnableMacros) { Token Tmp; @@ -191,14 +195,14 @@ void Preprocessor::CheckEndOfDirective(const char *DirType, bool EnableMacros) { -/// SkipExcludedConditionalBlock - We just read a #if or related directive and -/// decided that the subsequent tokens are in the #if'd out portion of the -/// file. Lex the rest of the file, until we see an #endif. If +/// SkipExcludedConditionalBlock - We just read a \#if or related directive and +/// decided that the subsequent tokens are in the \#if'd out portion of the +/// file. Lex the rest of the file, until we see an \#endif. If /// FoundNonSkipPortion is true, then we have already emitted code for part of -/// this #if directive, so #else/#elif blocks should never be entered. If ElseOk -/// is true, then #else directives are ok, if not, then we have already seen one -/// so a #else directive is a duplicate. When this returns, the caller can lex -/// the first valid token. +/// this \#if directive, so \#else/\#elif blocks should never be entered. +/// If ElseOk is true, then \#else directives are ok, if not, then we have +/// already seen one so a \#else directive is a duplicate. When this returns, +/// the caller can lex the first valid token. void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc, bool FoundNonSkipPortion, bool FoundElse, @@ -317,7 +321,6 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc, } else if (Directive[0] == 'e') { StringRef Sub = Directive.substr(1); if (Sub == "ndif") { // "endif" - CheckEndOfDirective("endif"); PPConditionalInfo CondInfo; CondInfo.WasSkipping = true; // Silence bogus warning. bool InCond = CurPPLexer->popConditionalLevel(CondInfo); @@ -326,9 +329,16 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc, // If we popped the outermost skipping block, we're done skipping! if (!CondInfo.WasSkipping) { + // Restore the value of LexingRawMode so that trailing comments + // are handled correctly, if we've reached the outermost block. + CurPPLexer->LexingRawMode = false; + CheckEndOfDirective("endif"); + CurPPLexer->LexingRawMode = true; if (Callbacks) Callbacks->Endif(Tok.getLocation(), CondInfo.IfLoc); break; + } else { + DiscardUntilEndOfDirective(); } } else if (Sub == "lse") { // "else". // #else directive in a skipping conditional. If not in some other @@ -346,7 +356,11 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc, // entered, enter the #else block now. if (!CondInfo.WasSkipping && !CondInfo.FoundNonSkip) { CondInfo.FoundNonSkip = true; + // Restore the value of LexingRawMode so that trailing comments + // are handled correctly. + CurPPLexer->LexingRawMode = false; CheckEndOfDirective("else"); + CurPPLexer->LexingRawMode = true; if (Callbacks) Callbacks->Else(Tok.getLocation(), CondInfo.IfLoc); break; @@ -484,9 +498,6 @@ void Preprocessor::PTHSkipExcludedConditionalBlock() { } } -/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file, -/// return null on failure. isAngled indicates whether the file reference is -/// for system #include's or not (i.e. using <> instead of ""). const FileEntry *Preprocessor::LookupFile( StringRef Filename, bool isAngled, @@ -553,6 +564,21 @@ const FileEntry *Preprocessor::LookupFile( // Preprocessor Directive Handling. //===----------------------------------------------------------------------===// +class Preprocessor::ResetMacroExpansionHelper { +public: + ResetMacroExpansionHelper(Preprocessor *pp) + : PP(pp), save(pp->DisableMacroExpansion) { + if (pp->MacroExpansionInDirectivesOverride) + pp->DisableMacroExpansion = false; + } + ~ResetMacroExpansionHelper() { + PP->DisableMacroExpansion = save; + } +private: + Preprocessor *PP; + bool save; +}; + /// HandleDirective - This callback is invoked when the lexer sees a # token /// at the start of a line. This consumes the directive, modifies the /// lexer/preprocessor state, and advances the lexer(s) so that the next token @@ -604,6 +630,10 @@ void Preprocessor::HandleDirective(Token &Result) { Diag(Result, diag::ext_embedded_directive); } + // Temporarily enable macro expansion if set so + // and reset to previous state when returning from this function. + ResetMacroExpansionHelper helper(this); + TryAgain: switch (Result.getKind()) { case tok::eod: @@ -774,23 +804,19 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val, Val = NextVal; } - // Reject 0, this is needed both by #line numbers and flags. - if (Val == 0) { - PP.Diag(DigitTok, DiagID); - PP.DiscardUntilEndOfDirective(); - return true; - } - - if (DigitTokBegin[0] == '0') + if (DigitTokBegin[0] == '0' && Val) PP.Diag(DigitTok.getLocation(), diag::warn_pp_line_decimal); return false; } -/// HandleLineDirective - Handle #line directive: C99 6.10.4. The two -/// acceptable forms are: +/// \brief Handle a \#line directive: C99 6.10.4. +/// +/// The two acceptable forms are: +/// \verbatim /// # line digit-sequence /// # line digit-sequence "s-char-sequence" +/// \endverbatim void Preprocessor::HandleLineDirective(Token &Tok) { // Read the line # and string argument. Per C99 6.10.4p5, these tokens are // expanded. @@ -801,6 +827,9 @@ void Preprocessor::HandleLineDirective(Token &Tok) { unsigned LineNo; if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer,*this)) return; + + if (LineNo == 0) + Diag(DigitTok, diag::ext_pp_line_zero); // Enforce C99 6.10.4p3: "The digit sequence shall not specify ... a // number greater than 2147483647". C90 requires that the line # be <= 32767. @@ -1018,15 +1047,13 @@ void Preprocessor::HandleUserDiagnosticDirective(Token &Tok, // tokens. For example, this is allowed: "#warning ` 'foo". GCC does // collapse multiple consequtive white space between tokens, but this isn't // specified by the standard. - std::string Message = CurLexer->ReadToEndOfLine(); + SmallString<128> Message; + CurLexer->ReadToEndOfLine(&Message); // Find the first non-whitespace character, so that we can make the // diagnostic more succinct. - StringRef Msg(Message); - size_t i = Msg.find_first_not_of(' '); - if (i < Msg.size()) - Msg = Msg.substr(i); - + StringRef Msg = Message.str().ltrim(" "); + if (isWarning) Diag(Tok, diag::pp_hash_warning) << Msg; else @@ -1135,7 +1162,7 @@ void Preprocessor::HandleMacroPrivateDirective(Token &Tok) { //===----------------------------------------------------------------------===// /// GetIncludeFilenameSpelling - Turn the specified lexer token into a fully -/// checked and spelled filename, e.g. as an operand of #include. This returns +/// checked and spelled filename, e.g. as an operand of \#include. This returns /// true if the input filename was in <>'s or false if it were in ""'s. The /// caller is expected to provide a buffer that is large enough to hold the /// spelling of the filename, but is also expected to handle the case when @@ -1179,11 +1206,14 @@ bool Preprocessor::GetIncludeFilenameSpelling(SourceLocation Loc, return isAngled; } -/// ConcatenateIncludeName - Handle cases where the #include name is expanded -/// from a macro as multiple tokens, which need to be glued together. This -/// occurs for code like: -/// #define FOO <a/b.h> -/// #include FOO +/// \brief Handle cases where the \#include name is expanded from a macro +/// as multiple tokens, which need to be glued together. +/// +/// This occurs for code like: +/// \code +/// \#define FOO <a/b.h> +/// \#include FOO +/// \endcode /// because in this case, "<a/b.h>" is returned as 7 tokens, not one. /// /// This code concatenates and consumes tokens up to the '>' token. It returns @@ -1238,10 +1268,10 @@ bool Preprocessor::ConcatenateIncludeName( return true; } -/// HandleIncludeDirective - The "#include" tokens have just been read, read the -/// file to be included from the lexer, then include it! This is a common -/// routine with functionality shared between #include, #include_next and -/// #import. LookupFrom is set when this is a #include_next directive, it +/// HandleIncludeDirective - The "\#include" tokens have just been read, read +/// the file to be included from the lexer, then include it! This is a common +/// routine with functionality shared between \#include, \#include_next and +/// \#import. LookupFrom is set when this is a \#include_next directive, it /// specifies the file to start searching from. void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, Token &IncludeTok, @@ -1360,9 +1390,28 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, } if (File == 0) { - if (!SuppressIncludeNotFoundError) - Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; - return; + if (!SuppressIncludeNotFoundError) { + // If the file could not be located and it was included via angle + // brackets, we can attempt a lookup as though it were a quoted path to + // provide the user with a possible fixit. + if (isAngled) { + File = LookupFile(Filename, false, LookupFrom, CurDir, + Callbacks ? &SearchPath : 0, + Callbacks ? &RelativePath : 0, + getLangOpts().Modules ? &SuggestedModule : 0); + if (File) { + SourceRange Range(FilenameTok.getLocation(), CharEnd); + Diag(FilenameTok, diag::err_pp_file_not_found_not_fatal) << + Filename << + FixItHint::CreateReplacement(Range, "\"" + Filename.str() + "\""); + } + } + // If the file is still not found, just go with the vanilla diagnostic + if (!File) + Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; + } + if (!File) + return; } // If we are supposed to import a module rather than including the header, @@ -1465,7 +1514,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, EnterSourceFile(FID, CurDir, FilenameTok.getLocation()); } -/// HandleIncludeNextDirective - Implements #include_next. +/// HandleIncludeNextDirective - Implements \#include_next. /// void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc, Token &IncludeNextTok) { @@ -1488,7 +1537,7 @@ void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc, return HandleIncludeDirective(HashLoc, IncludeNextTok, Lookup); } -/// HandleMicrosoftImportDirective - Implements #import for Microsoft Mode +/// HandleMicrosoftImportDirective - Implements \#import for Microsoft Mode void Preprocessor::HandleMicrosoftImportDirective(Token &Tok) { // The Microsoft #import directive takes a type library and generates header // files from it, and includes those. This is beyond the scope of what clang @@ -1502,7 +1551,7 @@ void Preprocessor::HandleMicrosoftImportDirective(Token &Tok) { DiscardUntilEndOfDirective(); } -/// HandleImportDirective - Implements #import. +/// HandleImportDirective - Implements \#import. /// void Preprocessor::HandleImportDirective(SourceLocation HashLoc, Token &ImportTok) { @@ -1634,7 +1683,7 @@ bool Preprocessor::ReadMacroDefinitionArgList(MacroInfo *MI, Token &Tok) { } } -/// HandleDefineDirective - Implements #define. This consumes the entire macro +/// HandleDefineDirective - Implements \#define. This consumes the entire macro /// line then lets the caller lex the next real token. void Preprocessor::HandleDefineDirective(Token &DefineTok) { ++NumDefined; @@ -1841,7 +1890,7 @@ void Preprocessor::HandleDefineDirective(Token &DefineTok) { Callbacks->MacroDefined(MacroNameTok, MI); } -/// HandleUndefDirective - Implements #undef. +/// HandleUndefDirective - Implements \#undef. /// void Preprocessor::HandleUndefDirective(Token &UndefTok) { ++NumUndefined; @@ -1882,10 +1931,10 @@ void Preprocessor::HandleUndefDirective(Token &UndefTok) { // Preprocessor Conditional Directive Handling. //===----------------------------------------------------------------------===// -/// HandleIfdefDirective - Implements the #ifdef/#ifndef directive. isIfndef is -/// true when this is a #ifndef directive. ReadAnyTokensBeforeDirective is true -/// if any tokens have been returned or pp-directives activated before this -/// #ifndef has been lexed. +/// HandleIfdefDirective - Implements the \#ifdef/\#ifndef directive. isIfndef +/// is true when this is a \#ifndef directive. ReadAnyTokensBeforeDirective is +/// true if any tokens have been returned or pp-directives activated before this +/// \#ifndef has been lexed. /// void Preprocessor::HandleIfdefDirective(Token &Result, bool isIfndef, bool ReadAnyTokensBeforeDirective) { @@ -1947,7 +1996,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result, bool isIfndef, } } -/// HandleIfDirective - Implements the #if directive. +/// HandleIfDirective - Implements the \#if directive. /// void Preprocessor::HandleIfDirective(Token &IfToken, bool ReadAnyTokensBeforeDirective) { @@ -1984,7 +2033,7 @@ void Preprocessor::HandleIfDirective(Token &IfToken, } } -/// HandleEndifDirective - Implements the #endif directive. +/// HandleEndifDirective - Implements the \#endif directive. /// void Preprocessor::HandleEndifDirective(Token &EndifToken) { ++NumEndif; @@ -2010,7 +2059,7 @@ void Preprocessor::HandleEndifDirective(Token &EndifToken) { Callbacks->Endif(EndifToken.getLocation(), CondInfo.IfLoc); } -/// HandleElseDirective - Implements the #else directive. +/// HandleElseDirective - Implements the \#else directive. /// void Preprocessor::HandleElseDirective(Token &Result) { ++NumElse; @@ -2039,7 +2088,7 @@ void Preprocessor::HandleElseDirective(Token &Result) { /*FoundElse*/true, Result.getLocation()); } -/// HandleElifDirective - Implements the #elif directive. +/// HandleElifDirective - Implements the \#elif directive. /// void Preprocessor::HandleElifDirective(Token &ElifToken) { ++NumElse; diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index b6689df18684..e824320cf732 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -31,7 +31,7 @@ PPCallbacks::~PPCallbacks() {} //===----------------------------------------------------------------------===// /// isInPrimaryFile - Return true if we're in the top-level file, not in a -/// #include. This looks through macro expansions and active _Pragma lexers. +/// \#include. This looks through macro expansions and active _Pragma lexers. bool Preprocessor::isInPrimaryFile() const { if (IsFileLexer()) return IncludeMacroStack.empty(); diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index fe7058570efa..ebdb6446d167 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -215,7 +215,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // If this is a function-like macro, read the arguments. if (MI->isFunctionLike()) { - // C99 6.10.3p10: If the preprocessing token immediately after the the macro + // C99 6.10.3p10: If the preprocessing token immediately after the macro // name isn't a '(', this macro should not be expanded. if (!isNextPPTokenLParen()) return true; @@ -242,9 +242,27 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // Remember where the token is expanded. SourceLocation ExpandLoc = Identifier.getLocation(); - - if (Callbacks) Callbacks->MacroExpands(Identifier, MI, - SourceRange(ExpandLoc, ExpansionEnd)); + SourceRange ExpansionRange(ExpandLoc, ExpansionEnd); + + if (Callbacks) { + if (InMacroArgs) { + // We can have macro expansion inside a conditional directive while + // reading the function macro arguments. To ensure, in that case, that + // MacroExpands callbacks still happen in source order, queue this + // callback to have it happen after the function macro callback. + DelayedMacroExpandsCallbacks.push_back( + MacroExpandsInfo(Identifier, MI, ExpansionRange)); + } else { + Callbacks->MacroExpands(Identifier, MI, ExpansionRange); + if (!DelayedMacroExpandsCallbacks.empty()) { + for (unsigned i=0, e = DelayedMacroExpandsCallbacks.size(); i!=e; ++i) { + MacroExpandsInfo &Info = DelayedMacroExpandsCallbacks[i]; + Callbacks->MacroExpands(Info.Tok, Info.MI, Info.Range); + } + DelayedMacroExpandsCallbacks.clear(); + } + } + } // If we started lexing a macro, enter the macro expansion body. @@ -469,10 +487,12 @@ MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName, } else if (MI->isVariadic() && (NumActuals+1 == MinArgsExpected || // A(x, ...) -> A(X) (NumActuals == 0 && MinArgsExpected == 2))) {// A(x,...) -> A() - // Varargs where the named vararg parameter is missing: ok as extension. - // #define A(x, ...) - // A("blah") + // Varargs where the named vararg parameter is missing: OK as extension. + // #define A(x, ...) + // A("blah") Diag(Tok, diag::ext_missing_varargs_arg); + Diag(MI->getDefinitionLoc(), diag::note_macro_here) + << MacroName.getIdentifierInfo(); // Remember this occurred, allowing us to elide the comma when used for // cases like: @@ -599,6 +619,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("address_sanitizer", LangOpts.AddressSanitizer) .Case("attribute_analyzer_noreturn", true) .Case("attribute_availability", true) + .Case("attribute_availability_with_message", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) .Case("attribute_deprecated_with_message", true) @@ -612,6 +633,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_objc_method_family", true) .Case("attribute_overloadable", true) .Case("attribute_unavailable_with_message", true) + .Case("attribute_unused_on_fields", true) .Case("blocks", LangOpts.Blocks) .Case("cxx_exceptions", LangOpts.Exceptions) .Case("cxx_rtti", LangOpts.RTTI) @@ -625,15 +647,16 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_fixed_enum", LangOpts.ObjC2) .Case("objc_instancetype", LangOpts.ObjC2) .Case("objc_modules", LangOpts.ObjC2 && LangOpts.Modules) - .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) - .Case("objc_weak_class", LangOpts.ObjCNonFragileABI) + .Case("objc_nonfragile_abi", LangOpts.ObjCRuntime.isNonFragile()) + .Case("objc_weak_class", LangOpts.ObjCRuntime.hasWeakClassImport()) .Case("ownership_holds", true) .Case("ownership_returns", true) .Case("ownership_takes", true) .Case("objc_bool", true) - .Case("objc_subscripting", LangOpts.ObjCNonFragileABI) + .Case("objc_subscripting", LangOpts.ObjCRuntime.isNonFragile()) .Case("objc_array_literals", LangOpts.ObjC2) .Case("objc_dictionary_literals", LangOpts.ObjC2) + .Case("objc_boxed_expressions", LangOpts.ObjC2) .Case("arc_cf_code_audited", true) // C11 features .Case("c_alignas", LangOpts.C11) @@ -772,6 +795,7 @@ static bool HasAttribute(const IdentifierInfo *II) { if (Name.startswith("__") && Name.endswith("__") && Name.size() >= 4) Name = Name.substr(2, Name.size() - 4); + // FIXME: Do we need to handle namespaces here? return llvm::StringSwitch<bool>(Name) #include "clang/Lex/AttrSpellings.inc" .Default(false); @@ -1030,7 +1054,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { if (Tok.is(tok::l_paren)) { // Read the identifier Lex(Tok); - if (Tok.is(tok::identifier)) { + if (Tok.is(tok::identifier) || Tok.is(tok::kw_const)) { FeatureII = Tok.getIdentifierInfo(); // Read the ')'. diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index e2a192b01f28..c9cc4adf4019 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -100,9 +100,12 @@ void PragmaNamespace::HandlePragma(Preprocessor &PP, // Preprocessor Pragma Directive Handling. //===----------------------------------------------------------------------===// -/// HandlePragmaDirective - The "#pragma" directive has been parsed. Lex the +/// HandlePragmaDirective - The "\#pragma" directive has been parsed. Lex the /// rest of the pragma, passing it to the registered pragma handlers. void Preprocessor::HandlePragmaDirective(unsigned Introducer) { + if (!PragmasEnabled) + return; + ++NumPragma; // Invoke the first level of pragma handlers which reads the namespace id. @@ -314,7 +317,7 @@ void Preprocessor::HandleMicrosoft__pragma(Token &Tok) { return Lex(Tok); } -/// HandlePragmaOnce - Handle #pragma once. OnceTok is the 'once'. +/// HandlePragmaOnce - Handle \#pragma once. OnceTok is the 'once'. /// void Preprocessor::HandlePragmaOnce(Token &OnceTok) { if (isInPrimaryFile()) { @@ -336,7 +339,7 @@ void Preprocessor::HandlePragmaMark() { } -/// HandlePragmaPoison - Handle #pragma GCC poison. PoisonTok is the 'poison'. +/// HandlePragmaPoison - Handle \#pragma GCC poison. PoisonTok is the 'poison'. /// void Preprocessor::HandlePragmaPoison(Token &PoisonTok) { Token Tok; @@ -378,7 +381,7 @@ void Preprocessor::HandlePragmaPoison(Token &PoisonTok) { } } -/// HandlePragmaSystemHeader - Implement #pragma GCC system_header. We know +/// HandlePragmaSystemHeader - Implement \#pragma GCC system_header. We know /// that the whole directive has been parsed. void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { if (isInPrimaryFile()) { @@ -411,7 +414,7 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { false, false, true, false); } -/// HandlePragmaDependency - Handle #pragma GCC dependency "foo" blah. +/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. /// void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { Token FilenameTok; @@ -464,9 +467,12 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { } } -/// HandlePragmaComment - Handle the microsoft #pragma comment extension. The -/// syntax is: -/// #pragma comment(linker, "foo") +/// \brief Handle the microsoft \#pragma comment extension. +/// +/// The syntax is: +/// \code +/// \#pragma comment(linker, "foo") +/// \endcode /// 'linker' is one of five identifiers: compiler, exestr, lib, linker, user. /// "foo" is a string, which is fully macro expanded, and permits string /// concatenation, embedded escape characters etc. See MSDN for more details. @@ -552,11 +558,15 @@ void Preprocessor::HandlePragmaComment(Token &Tok) { Callbacks->PragmaComment(CommentLoc, II, ArgumentString); } -/// HandlePragmaMessage - Handle the microsoft and gcc #pragma message +/// HandlePragmaMessage - Handle the microsoft and gcc \#pragma message /// extension. The syntax is: -/// #pragma message(string) +/// \code +/// \#pragma message(string) +/// \endcode /// OR, in GCC mode: -/// #pragma message string +/// \code +/// \#pragma message string +/// \endcode /// string is a string, which is fully macro expanded, and permits string /// concatenation, embedded escape characters, etc... See MSDN for more details. void Preprocessor::HandlePragmaMessage(Token &Tok) { @@ -679,9 +689,12 @@ IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) { return LookUpIdentifierInfo(MacroTok); } -/// HandlePragmaPushMacro - Handle #pragma push_macro. +/// \brief Handle \#pragma push_macro. +/// /// The syntax is: -/// #pragma push_macro("macro") +/// \code +/// \#pragma push_macro("macro") +/// \endcode void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) { // Parse the pragma directive and get the macro IdentifierInfo*. IdentifierInfo *IdentInfo = ParsePragmaPushOrPopMacro(PushMacroTok); @@ -703,9 +716,12 @@ void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) { PragmaPushMacroInfo[IdentInfo].push_back(MacroCopyToPush); } -/// HandlePragmaPopMacro - Handle #pragma pop_macro. +/// \brief Handle \#pragma pop_macro. +/// /// The syntax is: +/// \code /// #pragma pop_macro("macro") +/// \endcode void Preprocessor::HandlePragmaPopMacro(Token &PopMacroTok) { SourceLocation MessageLoc = PopMacroTok.getLocation(); @@ -931,7 +947,7 @@ bool Preprocessor::LexOnOffSwitch(tok::OnOffSwitch &Result) { } namespace { -/// PragmaOnceHandler - "#pragma once" marks the file as atomically included. +/// PragmaOnceHandler - "\#pragma once" marks the file as atomically included. struct PragmaOnceHandler : public PragmaHandler { PragmaOnceHandler() : PragmaHandler("once") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -941,7 +957,7 @@ struct PragmaOnceHandler : public PragmaHandler { } }; -/// PragmaMarkHandler - "#pragma mark ..." is ignored by the compiler, and the +/// PragmaMarkHandler - "\#pragma mark ..." is ignored by the compiler, and the /// rest of the line is not lexed. struct PragmaMarkHandler : public PragmaHandler { PragmaMarkHandler() : PragmaHandler("mark") {} @@ -951,7 +967,7 @@ struct PragmaMarkHandler : public PragmaHandler { } }; -/// PragmaPoisonHandler - "#pragma poison x" marks x as not usable. +/// PragmaPoisonHandler - "\#pragma poison x" marks x as not usable. struct PragmaPoisonHandler : public PragmaHandler { PragmaPoisonHandler() : PragmaHandler("poison") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -960,7 +976,7 @@ struct PragmaPoisonHandler : public PragmaHandler { } }; -/// PragmaSystemHeaderHandler - "#pragma system_header" marks the current file +/// PragmaSystemHeaderHandler - "\#pragma system_header" marks the current file /// as a system header, which silences warnings in it. struct PragmaSystemHeaderHandler : public PragmaHandler { PragmaSystemHeaderHandler() : PragmaHandler("system_header") {} @@ -994,6 +1010,10 @@ struct PragmaDebugHandler : public PragmaHandler { llvm_unreachable("This is an assertion!"); } else if (II->isStr("crash")) { *(volatile int*) 0x11 = 0; + } else if (II->isStr("parser_crash")) { + Token Crasher; + Crasher.setKind(tok::annot_pragma_parser_crash); + PP.EnterToken(Crasher); } else if (II->isStr("llvm_fatal_error")) { llvm::report_fatal_error("#pragma clang __debug llvm_fatal_error"); } else if (II->isStr("llvm_unreachable")) { @@ -1023,7 +1043,7 @@ struct PragmaDebugHandler : public PragmaHandler { }; -/// PragmaDiagnosticHandler - e.g. '#pragma GCC diagnostic ignored "-Wformat"' +/// PragmaDiagnosticHandler - e.g. '\#pragma GCC diagnostic ignored "-Wformat"' struct PragmaDiagnosticHandler : public PragmaHandler { private: const char *Namespace; @@ -1117,7 +1137,7 @@ public: } }; -/// PragmaCommentHandler - "#pragma comment ...". +/// PragmaCommentHandler - "\#pragma comment ...". struct PragmaCommentHandler : public PragmaHandler { PragmaCommentHandler() : PragmaHandler("comment") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1126,7 +1146,7 @@ struct PragmaCommentHandler : public PragmaHandler { } }; -/// PragmaIncludeAliasHandler - "#pragma include_alias("...")". +/// PragmaIncludeAliasHandler - "\#pragma include_alias("...")". struct PragmaIncludeAliasHandler : public PragmaHandler { PragmaIncludeAliasHandler() : PragmaHandler("include_alias") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1135,7 +1155,7 @@ struct PragmaIncludeAliasHandler : public PragmaHandler { } }; -/// PragmaMessageHandler - "#pragma message("...")". +/// PragmaMessageHandler - "\#pragma message("...")". struct PragmaMessageHandler : public PragmaHandler { PragmaMessageHandler() : PragmaHandler("message") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1144,7 +1164,7 @@ struct PragmaMessageHandler : public PragmaHandler { } }; -/// PragmaPushMacroHandler - "#pragma push_macro" saves the value of the +/// PragmaPushMacroHandler - "\#pragma push_macro" saves the value of the /// macro on the top of the stack. struct PragmaPushMacroHandler : public PragmaHandler { PragmaPushMacroHandler() : PragmaHandler("push_macro") {} @@ -1155,7 +1175,7 @@ struct PragmaPushMacroHandler : public PragmaHandler { }; -/// PragmaPopMacroHandler - "#pragma pop_macro" sets the value of the +/// PragmaPopMacroHandler - "\#pragma pop_macro" sets the value of the /// macro to the value on the top of the stack. struct PragmaPopMacroHandler : public PragmaHandler { PragmaPopMacroHandler() : PragmaHandler("pop_macro") {} @@ -1167,7 +1187,7 @@ struct PragmaPopMacroHandler : public PragmaHandler { // Pragma STDC implementations. -/// PragmaSTDC_FENV_ACCESSHandler - "#pragma STDC FENV_ACCESS ...". +/// PragmaSTDC_FENV_ACCESSHandler - "\#pragma STDC FENV_ACCESS ...". struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler { PragmaSTDC_FENV_ACCESSHandler() : PragmaHandler("FENV_ACCESS") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1180,7 +1200,7 @@ struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler { } }; -/// PragmaSTDC_CX_LIMITED_RANGEHandler - "#pragma STDC CX_LIMITED_RANGE ...". +/// PragmaSTDC_CX_LIMITED_RANGEHandler - "\#pragma STDC CX_LIMITED_RANGE ...". struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler { PragmaSTDC_CX_LIMITED_RANGEHandler() : PragmaHandler("CX_LIMITED_RANGE") {} @@ -1191,7 +1211,7 @@ struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler { } }; -/// PragmaSTDC_UnknownHandler - "#pragma STDC ...". +/// PragmaSTDC_UnknownHandler - "\#pragma STDC ...". struct PragmaSTDC_UnknownHandler : public PragmaHandler { PragmaSTDC_UnknownHandler() {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1202,7 +1222,7 @@ struct PragmaSTDC_UnknownHandler : public PragmaHandler { }; /// PragmaARCCFCodeAuditedHandler - -/// #pragma clang arc_cf_code_audited begin/end +/// \#pragma clang arc_cf_code_audited begin/end struct PragmaARCCFCodeAuditedHandler : public PragmaHandler { PragmaARCCFCodeAuditedHandler() : PragmaHandler("arc_cf_code_audited") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, @@ -1259,7 +1279,7 @@ struct PragmaARCCFCodeAuditedHandler : public PragmaHandler { /// RegisterBuiltinPragmas - Install the standard preprocessor pragmas: -/// #pragma GCC poison/system_header/dependency and #pragma once. +/// \#pragma GCC poison/system_header/dependency and \#pragma once. void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler(new PragmaOnceHandler()); AddPragmaHandler(new PragmaMarkHandler()); diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp index 89d19fd52adc..dfdeba3f9cd9 100644 --- a/lib/Lex/PreprocessingRecord.cpp +++ b/lib/Lex/PreprocessingRecord.cpp @@ -48,7 +48,7 @@ PreprocessingRecord::PreprocessingRecord(SourceManager &SM, } /// \brief Returns a pair of [Begin, End) iterators of preprocessed entities -/// that source range \arg R encompasses. +/// that source range \p Range encompasses. std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> PreprocessingRecord::getPreprocessedEntitiesInRange(SourceRange Range) { if (Range.isInvalid()) @@ -89,7 +89,7 @@ static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID, /// /// Can be used to avoid implicit deserializations of preallocated /// preprocessed entities if we only care about entities of a specific file -/// and not from files #included in the range given at +/// and not from files \#included in the range given at /// \see getPreprocessedEntitiesInRange. bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) { if (FID.isInvalid()) diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 06e5685a8b67..614530cf387f 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -66,54 +66,6 @@ Preprocessor::Preprocessor(DiagnosticsEngine &diags, LangOptions &opts, Record(0), MIChainHead(0), MICache(0) { OwnsHeaderSearch = OwnsHeaders; - - if (!DelayInitialization) { - assert(Target && "Must provide target information for PP initialization"); - Initialize(*Target); - } -} - -Preprocessor::~Preprocessor() { - assert(BacktrackPositions.empty() && "EnableBacktrack/Backtrack imbalance!"); - - while (!IncludeMacroStack.empty()) { - delete IncludeMacroStack.back().TheLexer; - delete IncludeMacroStack.back().TheTokenLexer; - IncludeMacroStack.pop_back(); - } - - // Free any macro definitions. - for (MacroInfoChain *I = MIChainHead ; I ; I = I->Next) - I->MI.Destroy(); - - // Free any cached macro expanders. - for (unsigned i = 0, e = NumCachedTokenLexers; i != e; ++i) - delete TokenLexerCache[i]; - - // Free any cached MacroArgs. - for (MacroArgs *ArgList = MacroArgCache; ArgList; ) - ArgList = ArgList->deallocate(); - - // Release pragma information. - delete PragmaHandlers; - - // Delete the scratch buffer info. - delete ScratchBuf; - - // Delete the header search info, if we own it. - if (OwnsHeaderSearch) - delete &HeaderInfo; - - delete Callbacks; -} - -void Preprocessor::Initialize(const TargetInfo &Target) { - assert((!this->Target || this->Target == &Target) && - "Invalid override of target information"); - this->Target = &Target; - - // Initialize information about built-ins. - BuiltinInfo.InitializeTarget(Target); ScratchBuf = new ScratchBuffer(SourceMgr); CounterValue = 0; // __COUNTER__ starts at 0. @@ -134,10 +86,12 @@ void Preprocessor::Initialize(const TargetInfo &Target) { // Macro expansion is enabled. DisableMacroExpansion = false; + MacroExpansionInDirectivesOverride = false; InMacroArgs = false; InMacroArgPreExpansion = false; NumCachedTokenLexers = 0; - + PragmasEnabled = true; + CachedLexPos = 0; // We haven't read anything from the external source. @@ -170,7 +124,54 @@ void Preprocessor::Initialize(const TargetInfo &Target) { Ident___exception_info = Ident___exception_code = Ident___abnormal_termination = 0; Ident_GetExceptionInfo = Ident_GetExceptionCode = Ident_AbnormalTermination = 0; } + + if (!DelayInitialization) { + assert(Target && "Must provide target information for PP initialization"); + Initialize(*Target); + } +} + +Preprocessor::~Preprocessor() { + assert(BacktrackPositions.empty() && "EnableBacktrack/Backtrack imbalance!"); + + while (!IncludeMacroStack.empty()) { + delete IncludeMacroStack.back().TheLexer; + delete IncludeMacroStack.back().TheTokenLexer; + IncludeMacroStack.pop_back(); + } + + // Free any macro definitions. + for (MacroInfoChain *I = MIChainHead ; I ; I = I->Next) + I->MI.Destroy(); + + // Free any cached macro expanders. + for (unsigned i = 0, e = NumCachedTokenLexers; i != e; ++i) + delete TokenLexerCache[i]; + + // Free any cached MacroArgs. + for (MacroArgs *ArgList = MacroArgCache; ArgList; ) + ArgList = ArgList->deallocate(); + + // Release pragma information. + delete PragmaHandlers; + + // Delete the scratch buffer info. + delete ScratchBuf; + + // Delete the header search info, if we own it. + if (OwnsHeaderSearch) + delete &HeaderInfo; + + delete Callbacks; +} + +void Preprocessor::Initialize(const TargetInfo &Target) { + assert((!this->Target || this->Target == &Target) && + "Invalid override of target information"); + this->Target = &Target; + // Initialize information about built-ins. + BuiltinInfo.InitializeTarget(Target); HeaderInfo.setTarget(Target); } @@ -236,6 +237,20 @@ void Preprocessor::PrintStats() { llvm::errs() << (NumFastTokenPaste+NumTokenPaste) << " token paste (##) operations performed, " << NumFastTokenPaste << " on the fast path.\n"; + + llvm::errs() << "\nPreprocessor Memory: " << getTotalMemory() << "B total"; + + llvm::errs() << "\n BumpPtr: " << BP.getTotalMemory(); + llvm::errs() << "\n Macro Expanded Tokens: " + << llvm::capacity_in_bytes(MacroExpandedTokens); + llvm::errs() << "\n Predefines Buffer: " << Predefines.capacity(); + llvm::errs() << "\n Macros: " << llvm::capacity_in_bytes(Macros); + llvm::errs() << "\n #pragma push_macro Info: " + << llvm::capacity_in_bytes(PragmaPushMacroInfo); + llvm::errs() << "\n Poison Reasons: " + << llvm::capacity_in_bytes(PoisonReasons); + llvm::errs() << "\n Comment Handlers: " + << llvm::capacity_in_bytes(CommentHandlers) << "\n"; } Preprocessor::macro_iterator @@ -514,9 +529,19 @@ void Preprocessor::HandleIdentifier(Token &Identifier) { // If the information about this identifier is out of date, update it from // the external source. + // We have to treat __VA_ARGS__ in a special way, since it gets + // serialized with isPoisoned = true, but our preprocessor may have + // unpoisoned it if we're defining a C99 macro. if (II.isOutOfDate()) { + bool CurrentIsPoisoned = false; + if (&II == Ident__VA_ARGS__) + CurrentIsPoisoned = Ident__VA_ARGS__->isPoisoned(); + ExternalSource->updateOutOfDateIdentifier(II); Identifier.setKind(II.getTokenID()); + + if (&II == Ident__VA_ARGS__) + II.setIsPoisoned(CurrentIsPoisoned); } // If this identifier was poisoned, and if it was not produced from a macro @@ -622,14 +647,14 @@ void Preprocessor::LexAfterModuleImport(Token &Result) { /*IsIncludeDirective=*/false); } -void Preprocessor::AddCommentHandler(CommentHandler *Handler) { +void Preprocessor::addCommentHandler(CommentHandler *Handler) { assert(Handler && "NULL comment handler"); assert(std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler) == CommentHandlers.end() && "Comment handler already registered"); CommentHandlers.push_back(Handler); } -void Preprocessor::RemoveCommentHandler(CommentHandler *Handler) { +void Preprocessor::removeCommentHandler(CommentHandler *Handler) { std::vector<CommentHandler *>::iterator Pos = std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler); assert(Pos != CommentHandlers.end() && "Comment handler not registered"); diff --git a/lib/Lex/PreprocessorLexer.cpp b/lib/Lex/PreprocessorLexer.cpp index a72bbca6bcff..a64c84d6bbd3 100644 --- a/lib/Lex/PreprocessorLexer.cpp +++ b/lib/Lex/PreprocessorLexer.cpp @@ -27,7 +27,7 @@ PreprocessorLexer::PreprocessorLexer(Preprocessor *pp, FileID fid) InitialNumSLocEntries = pp->getSourceManager().local_sloc_entry_size(); } -/// LexIncludeFilename - After the preprocessor has parsed a #include, lex and +/// \brief After the preprocessor has parsed a \#include, lex and /// (potentially) macro expand the filename. void PreprocessorLexer::LexIncludeFilename(Token &FilenameTok) { assert(ParsingPreprocessorDirective && diff --git a/lib/Lex/TokenConcatenation.cpp b/lib/Lex/TokenConcatenation.cpp index 84a46ed3779b..dd7ebb0ce337 100644 --- a/lib/Lex/TokenConcatenation.cpp +++ b/lib/Lex/TokenConcatenation.cpp @@ -14,6 +14,7 @@ #include "clang/Lex/TokenConcatenation.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/ErrorHandling.h" +#include <cctype> using namespace clang; diff --git a/lib/Lex/TokenLexer.cpp b/lib/Lex/TokenLexer.cpp index 696754c74167..ade40dad77d9 100644 --- a/lib/Lex/TokenLexer.cpp +++ b/lib/Lex/TokenLexer.cpp @@ -252,9 +252,9 @@ void TokenLexer::ExpandFunctionArguments() { const Token *ArgToks = ActualArgs->getUnexpArgument(ArgNo); unsigned NumToks = MacroArgs::getArgLength(ArgToks); if (NumToks) { // Not an empty argument? - // If this is the GNU ", ## __VA_ARG__" extension, and we just learned - // that __VA_ARG__ expands to multiple tokens, avoid a pasting error when - // the expander trys to paste ',' with the first token of the __VA_ARG__ + // If this is the GNU ", ## __VA_ARGS__" extension, and we just learned + // that __VA_ARGS__ expands to multiple tokens, avoid a pasting error when + // the expander trys to paste ',' with the first token of the __VA_ARGS__ // expansion. if (PasteBefore && ResultToks.size() >= 2 && ResultToks[ResultToks.size()-2].is(tok::comma) && @@ -568,8 +568,8 @@ bool TokenLexer::PasteTokens(Token &Tok) { << Buffer.str(); } - // Do not consume the RHS. - --CurToken; + // An error has occurred so exit loop. + break; } // Turn ## into 'unknown' to avoid # ## # from looking like a paste @@ -578,7 +578,7 @@ bool TokenLexer::PasteTokens(Token &Tok) { Result.setKind(tok::unknown); } - // Transfer properties of the LHS over the the Result. + // Transfer properties of the LHS over the Result. Result.setFlagValue(Token::StartOfLine , Tok.isAtStartOfLine()); Result.setFlagValue(Token::LeadingSpace, Tok.hasLeadingSpace()); diff --git a/lib/Makefile b/lib/Makefile index 2eb72a91e68b..1f14aa078e4d 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,7 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL := .. -PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ +PARALLEL_DIRS = Headers Basic Lex Parse AST ASTMatchers Sema CodeGen Analysis \ StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \ FrontendTool Tooling Driver diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 6c980ced7ee4..55e2aebca870 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -1,5 +1,3 @@ -set(LLVM_USED_LIBS clangBasic clangAST clangLex clangSema) - add_clang_library(clangParse ParseAST.cpp ParseCXXInlineMethods.cpp @@ -16,4 +14,21 @@ add_clang_library(clangParse Parser.cpp ) -add_dependencies(clangParse ClangAttrClasses ClangAttrList ClangDeclNodes ClangDiagnosticParse ClangStmtNodes ClangAttrLateParsed) +add_dependencies(clangParse + ClangAttrClasses + ClangAttrLateParsed + ClangAttrList + ClangAttrParsedAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticParse + ClangStmtNodes + ) + +target_link_libraries(clangParse + clangBasic + clangAST + clangLex + clangSema + ) diff --git a/lib/Parse/ParseAST.cpp b/lib/Parse/ParseAST.cpp index d1c2624f8cb3..bd4f85952120 100644 --- a/lib/Parse/ParseAST.cpp +++ b/lib/Parse/ParseAST.cpp @@ -12,11 +12,13 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/ParseAST.h" +#include "clang/Parse/ParseDiagnostic.h" #include "clang/Sema/Sema.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/Stmt.h" @@ -77,28 +79,29 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) { S.getPreprocessor().EnterMainSourceFile(); P.Initialize(); S.Initialize(); - - if (ExternalASTSource *External = S.getASTContext().getExternalSource()) - External->StartTranslationUnit(Consumer); - - bool Abort = false; + + // C11 6.9p1 says translation units must have at least one top-level + // declaration. C++ doesn't have this restriction. We also don't want to + // complain if we have a precompiled header, although technically if the PCH + // is empty we should still emit the (pedantic) diagnostic. Parser::DeclGroupPtrTy ADecl; - - while (!P.ParseTopLevelDecl(ADecl)) { // Not end of file. - // If we got a null return and something *was* parsed, ignore it. This - // is due to a top-level semicolon, an action override, or a parse error - // skipping something. - if (ADecl) { - if (!Consumer->HandleTopLevelDecl(ADecl.get())) { - Abort = true; - break; - } - } - }; - - if (Abort) - return; - + ExternalASTSource *External = S.getASTContext().getExternalSource(); + if (External) + External->StartTranslationUnit(Consumer); + + if (P.ParseTopLevelDecl(ADecl)) { + if (!External && !S.getLangOpts().CPlusPlus) + P.Diag(diag::ext_empty_translation_unit); + } else { + do { + // If we got a null return and something *was* parsed, ignore it. This + // is due to a top-level semicolon, an action override, or a parse error + // skipping something. + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return; + } while (!P.ParseTopLevelDecl(ADecl)); + } + // Process any TopLevelDecls generated by #pragma weak. for (SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(), diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index c7b29d9ba28e..abce27c3f820 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -16,6 +16,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" #include "clang/AST/DeclTemplate.h" +#include "RAIIObjectsForParser.h" using namespace clang; /// ParseCXXInlineMethodDef - We parsed and verified that the specified @@ -45,7 +46,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, else { FnD = Actions.ActOnCXXMemberDeclarator(getCurScope(), AS, D, move(TemplateParams), 0, - VS, /*HasDeferredInit=*/false); + VS, ICIS_NoInit); if (FnD) { Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs, false, true); @@ -108,6 +109,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, // or if we are about to parse function member template then consume // the tokens and store them for parsing at the end of the translation unit. if (getLangOpts().DelayedTemplateParsing && + DefinitionKind == FDK_Definition && ((Actions.CurContext->isDependentContext() || TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) && !Actions.IsInsideALocalClassWithinATemplateFunction())) { @@ -458,7 +460,7 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) { Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), Class.TagOrTemplate); - { + if (!Class.LateParsedDeclarations.empty()) { // C++11 [expr.prim.general]p4: // Otherwise, if a member-declarator declares a non-static data member // (9.2) of a class X, the expression this is a prvalue of type "pointer @@ -492,7 +494,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { ConsumeAnyToken(); SourceLocation EqualLoc; - + ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false, EqualLoc); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7995e68d3f27..b830d9ccfd9f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -14,6 +14,7 @@ #include "clang/Parse/Parser.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Basic/OpenCL.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/PrettyDeclStackTrace.h" @@ -37,6 +38,8 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, AccessSpecifier AS, Decl **OwnedType) { DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context); + if (DSC == DSC_normal) + DSC = DSC_type_specifier; // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); @@ -156,7 +159,7 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, } } else { attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); + 0, SourceLocation(), 0, 0, AttributeList::AS_GNU); } } if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) @@ -272,67 +275,175 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, if (!ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) { AttributeList *attr = Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), 0, AttrNameLoc, - ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size()); - if (BuiltinType && attr->getKind() == AttributeList::AT_iboutletcollection) + ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size(), + AttributeList::AS_GNU); + if (BuiltinType && attr->getKind() == AttributeList::AT_IBOutletCollection) Diag(Tok, diag::err_iboutletcollection_builtintype); } } +/// \brief Parses a single argument for a declspec, including the +/// surrounding parens. +void Parser::ParseMicrosoftDeclSpecWithSingleArg(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs) +{ + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + AttrName->getNameStart(), tok::r_paren)) + return; + + ExprResult ArgExpr(ParseConstantExpression()); + if (ArgExpr.isInvalid()) { + T.skipToEnd(); + return; + } + Expr *ExprList = ArgExpr.take(); + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), + &ExprList, 1, AttributeList::AS_Declspec); + + T.consumeClose(); +} + +/// \brief Determines whether a declspec is a "simple" one requiring no +/// arguments. +bool Parser::IsSimpleMicrosoftDeclSpec(IdentifierInfo *Ident) { + return llvm::StringSwitch<bool>(Ident->getName()) + .Case("dllimport", true) + .Case("dllexport", true) + .Case("noreturn", true) + .Case("nothrow", true) + .Case("noinline", true) + .Case("naked", true) + .Case("appdomain", true) + .Case("process", true) + .Case("jitintrinsic", true) + .Case("noalias", true) + .Case("restrict", true) + .Case("novtable", true) + .Case("selectany", true) + .Case("thread", true) + .Default(false); +} + +/// \brief Attempts to parse a declspec which is not simple (one that takes +/// parameters). Will return false if we properly handled the declspec, or +/// true if it is an unknown declspec. +void Parser::ParseComplexMicrosoftDeclSpec(IdentifierInfo *Ident, + SourceLocation Loc, + ParsedAttributes &Attrs) { + // Try to handle the easy case first -- these declspecs all take a single + // parameter as their argument. + if (llvm::StringSwitch<bool>(Ident->getName()) + .Case("uuid", true) + .Case("align", true) + .Case("allocate", true) + .Default(false)) { + ParseMicrosoftDeclSpecWithSingleArg(Ident, Loc, Attrs); + } else if (Ident->getName() == "deprecated") { + // The deprecated declspec has an optional single argument, so we will + // check for a l-paren to decide whether we should parse an argument or + // not. + if (Tok.getKind() == tok::l_paren) + ParseMicrosoftDeclSpecWithSingleArg(Ident, Loc, Attrs); + else + Attrs.addNew(Ident, Loc, 0, Loc, 0, SourceLocation(), 0, 0, + AttributeList::AS_Declspec); + } else if (Ident->getName() == "property") { + // The property declspec is more complex in that it can take one or two + // assignment expressions as a parameter, but the lhs of the assignment + // must be named get or put. + // + // For right now, we will just skip to the closing right paren of the + // property expression. + // + // FIXME: we should deal with __declspec(property) at some point because it + // is used in the platform SDK headers for the Parallel Patterns Library + // and ATL. + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + Ident->getNameStart(), tok::r_paren)) + return; + T.skipToEnd(); + } else { + // We don't recognize this as a valid declspec, but instead of creating the + // attribute and allowing sema to warn about it, we will warn here instead. + // This is because some attributes have multiple spellings, but we need to + // disallow that for declspecs (such as align vs aligned). If we made the + // attribute, we'd have to split the valid declspec spelling logic into + // both locations. + Diag(Loc, diag::warn_ms_declspec_unknown) << Ident; + + // If there's an open paren, we should eat the open and close parens under + // the assumption that this unknown declspec has parameters. + BalancedDelimiterTracker T(*this, tok::l_paren); + if (!T.consumeOpen()) + T.skipToEnd(); + } +} -/// ParseMicrosoftDeclSpec - Parse an __declspec construct -/// /// [MS] decl-specifier: /// __declspec ( extended-decl-modifier-seq ) /// /// [MS] extended-decl-modifier-seq: /// extended-decl-modifier[opt] /// extended-decl-modifier extended-decl-modifier-seq - -void Parser::ParseMicrosoftDeclSpec(ParsedAttributes &attrs) { +void Parser::ParseMicrosoftDeclSpec(ParsedAttributes &Attrs) { assert(Tok.is(tok::kw___declspec) && "Not a declspec!"); ConsumeToken(); - if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, - "declspec")) { - SkipUntil(tok::r_paren, true); // skip until ) or ; + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__declspec", + tok::r_paren)) return; - } - while (Tok.getIdentifierInfo()) { - IdentifierInfo *AttrName = Tok.getIdentifierInfo(); - SourceLocation AttrNameLoc = ConsumeToken(); - - // FIXME: Remove this when we have proper __declspec(property()) support. - // Just skip everything inside property(). - if (AttrName->getName() == "property") { - ConsumeParen(); - SkipUntil(tok::r_paren); + // An empty declspec is perfectly legal and should not warn. Additionally, + // you can specify multiple attributes per declspec. + while (Tok.getKind() != tok::r_paren) { + // We expect either a well-known identifier or a generic string. Anything + // else is a malformed declspec. + bool IsString = Tok.getKind() == tok::string_literal ? true : false; + if (!IsString && Tok.getKind() != tok::identifier && + Tok.getKind() != tok::kw_restrict) { + Diag(Tok, diag::err_ms_declspec_type); + T.skipToEnd(); + return; } - if (Tok.is(tok::l_paren)) { - ConsumeParen(); - // FIXME: This doesn't parse __declspec(property(get=get_func_name)) - // correctly. - ExprResult ArgExpr(ParseAssignmentExpression()); - if (!ArgExpr.isInvalid()) { - Expr *ExprList = ArgExpr.take(); - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, - SourceLocation(), &ExprList, 1, true); + + IdentifierInfo *AttrName; + SourceLocation AttrNameLoc; + if (IsString) { + SmallString<8> StrBuffer; + bool Invalid = false; + StringRef Str = PP.getSpelling(Tok, StrBuffer, &Invalid); + if (Invalid) { + T.skipToEnd(); + return; } - if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) - SkipUntil(tok::r_paren, false); + AttrName = PP.getIdentifierInfo(Str); + AttrNameLoc = ConsumeStringToken(); } else { - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0, true); + AttrName = Tok.getIdentifierInfo(); + AttrNameLoc = ConsumeToken(); } + + if (IsString || IsSimpleMicrosoftDeclSpec(AttrName)) + // If we have a generic string, we will allow it because there is no + // documented list of allowable string declspecs, but we know they exist + // (for instance, SAL declspecs in older versions of MSVC). + // + // Alternatively, if the identifier is a simple one, then it requires no + // arguments and can be turned into an attribute directly. + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), + 0, 0, AttributeList::AS_Declspec); + else + ParseComplexMicrosoftDeclSpec(AttrName, AttrNameLoc, Attrs); } - if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) - SkipUntil(tok::r_paren, false); - return; + T.consumeClose(); } void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) { // Treat these like attributes - // FIXME: Allow Sema to distinguish between these and real attributes! while (Tok.is(tok::kw___fastcall) || Tok.is(tok::kw___stdcall) || Tok.is(tok::kw___thiscall) || Tok.is(tok::kw___cdecl) || Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) || @@ -340,12 +451,8 @@ void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) { Tok.is(tok::kw___unaligned)) { IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); - if (Tok.is(tok::kw___ptr64) || Tok.is(tok::kw___w64) || - Tok.is(tok::kw___ptr32)) - // FIXME: Support these properly! - continue; attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, - SourceLocation(), 0, 0, true); + SourceLocation(), 0, 0, AttributeList::AS_MSTypespec); } } @@ -355,7 +462,7 @@ void Parser::ParseBorlandTypeAttributes(ParsedAttributes &attrs) { IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, - SourceLocation(), 0, 0, true); + SourceLocation(), 0, 0, AttributeList::AS_MSTypespec); } } @@ -365,7 +472,7 @@ void Parser::ParseOpenCLAttributes(ParsedAttributes &attrs) { SourceLocation AttrNameLoc = ConsumeToken(); attrs.addNew(PP.getIdentifierInfo("opencl_kernel_function"), AttrNameLoc, 0, AttrNameLoc, 0, - SourceLocation(), 0, 0, false); + SourceLocation(), 0, 0, AttributeList::AS_GNU); } } @@ -374,42 +481,42 @@ void Parser::ParseOpenCLQualifiers(DeclSpec &DS) { switch(Tok.getKind()) { // OpenCL qualifiers: case tok::kw___private: - case tok::kw_private: + case tok::kw_private: DS.getAttributes().addNewInteger( - Actions.getASTContext(), + Actions.getASTContext(), PP.getIdentifierInfo("address_space"), Loc, 0); break; - + case tok::kw___global: DS.getAttributes().addNewInteger( Actions.getASTContext(), PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_global); break; - + case tok::kw___local: DS.getAttributes().addNewInteger( Actions.getASTContext(), PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_local); break; - + case tok::kw___constant: DS.getAttributes().addNewInteger( Actions.getASTContext(), PP.getIdentifierInfo("address_space"), Loc, LangAS::opencl_constant); break; - + case tok::kw___read_only: DS.getAttributes().addNewInteger( - Actions.getASTContext(), + Actions.getASTContext(), PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_read_only); break; - + case tok::kw___write_only: DS.getAttributes().addNewInteger( - Actions.getASTContext(), + Actions.getASTContext(), PP.getIdentifierInfo("opencl_image_access"), Loc, CLIA_write_only); break; - + case tok::kw___read_write: DS.getAttributes().addNewInteger( Actions.getASTContext(), @@ -490,21 +597,21 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { if (AfterMinor == ActualLength) { ConsumeToken(); - + // We had major.minor. if (Major == 0 && Minor == 0) { Diag(Tok, diag::err_zero_version); return VersionTuple(); } - return VersionTuple(Major, Minor); + return VersionTuple(Major, Minor); } // If what follows is not a '.', we have a problem. if (ThisTokBegin[AfterMinor] != '.') { Diag(Tok, diag::err_expected_version); SkipUntil(tok::comma, tok::r_paren, true, true, true); - return VersionTuple(); + return VersionTuple(); } // Parse the subminor version. @@ -599,7 +706,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, if (UnavailableLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) << Keyword << SourceRange(UnavailableLoc); - } + } UnavailableLoc = KeywordLoc; if (Tok.isNot(tok::comma)) @@ -607,8 +714,8 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, ConsumeToken(); continue; - } - + } + if (Tok.isNot(tok::equal)) { Diag(Tok, diag::err_expected_equal_after) << Keyword; @@ -625,10 +732,10 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, MessageExpr = ParseStringLiteralExpression(); break; } - + SourceRange VersionRange; VersionTuple Version = ParseVersionTuple(VersionRange); - + if (Version.empty()) { SkipUntil(tok::r_paren); return; @@ -641,13 +748,13 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Index = Deprecated; else if (Keyword == Ident_obsoleted) Index = Obsoleted; - else + else Index = Unknown; if (Index < Unknown) { if (!Changes[Index].KeywordLoc.isInvalid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword + << Keyword << SourceRange(Changes[Index].KeywordLoc, Changes[Index].VersionRange.getEnd()); } @@ -693,15 +800,15 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, } // Record this attribute - attrs.addNew(&Availability, - SourceRange(AvailabilityLoc, T.getCloseLocation()), + attrs.addNew(&Availability, + SourceRange(AvailabilityLoc, T.getCloseLocation()), 0, AvailabilityLoc, Platform, PlatformLoc, Changes[Introduced], Changes[Deprecated], - Changes[Obsoleted], + Changes[Obsoleted], UnavailableLoc, MessageExpr.take(), - false, false); + AttributeList::AS_GNU); } @@ -739,16 +846,16 @@ void Parser::ParseLexedAttributes(ParsingClass &Class) { if (!AlreadyHasClassScope) Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), Class.TagOrTemplate); - { + if (!Class.LateParsedDeclarations.empty()) { // Allow 'this' within late-parsed attributes. - Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate, + Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate, /*TypeQuals=*/0); - + for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){ Class.LateParsedDeclarations[i]->ParseLexedAttributes(); } } - + if (!AlreadyHasClassScope) Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), Class.TagOrTemplate); @@ -770,7 +877,7 @@ void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, /// \brief Finish parsing an attribute for which parsing was delayed. /// This will be called at the end of parsing a class declaration /// for each LateParsedAttribute. We consume the saved tokens and -/// create an attribute with the arguments filled in. We add this +/// create an attribute with the arguments filled in. We add this /// to the Attribute list for the decl. void Parser::ParseLexedAttribute(LateParsedAttribute &LA, bool EnterScope, bool OnDefinition) { @@ -885,10 +992,10 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); - + ExprVector ArgExprs(Actions); bool ArgExprsOk = true; - + // now parse the list of expressions while (Tok.isNot(tok::r_paren)) { ExprResult ArgExpr(ParseAssignmentExpression()); @@ -906,7 +1013,7 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, // Match the ')'. if (ArgExprsOk && !T.consumeClose()) { Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), - ArgExprs.take(), ArgExprs.size()); + ArgExprs.take(), ArgExprs.size(), AttributeList::AS_GNU); } if (EndLoc) *EndLoc = T.getCloseLocation(); @@ -975,7 +1082,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, // Must temporarily exit the objective-c container scope for // parsing c none objective-c decls. ObjCDeclContextSwitch ObjCDC(*this); - + Decl *SingleDecl = 0; Decl *OwnedType = 0; switch (Tok.getKind()) { @@ -992,7 +1099,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, SingleDecl = ParseNamespace(Context, DeclEnd, InlineLoc); break; } - return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs, + return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs, true); case tok::kw_namespace: ProhibitAttributes(attrs); @@ -1010,7 +1117,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, default: return ParseSimpleDeclaration(Stmts, Context, DeclEnd, attrs, true); } - + // This routine returns a DeclGroup, if the thing we parsed only contains a // single decl, convert it now. Alias declarations can also declare a type; // include that too if it is present. @@ -1019,10 +1126,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, /// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl] /// declaration-specifiers init-declarator-list[opt] ';' +/// [C++11] attribute-specifier-seq decl-specifier-seq[opt] +/// init-declarator-list ';' ///[C90/C++]init-declarator-list ';' [TODO] /// [OMP] threadprivate-directive [TODO] /// -/// for-range-declaration: [C++0x 6.5p1: stmt.ranged] +/// for-range-declaration: [C++11 6.5p1: stmt.ranged] /// attribute-specifier-seq[opt] type-specifier-seq declarator /// /// If RequireSemi is false, this does not check for a ';' at the end of the @@ -1031,12 +1140,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, /// If FRI is non-null, we might be parsing a for-range-declaration instead /// of a simple-declaration. If we find that we are, we also parse the /// for-range-initializer, and place it here. -Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, - unsigned Context, - SourceLocation &DeclEnd, - ParsedAttributes &attrs, - bool RequireSemi, - ForRangeInit *FRI) { +Parser::DeclGroupPtrTy +Parser::ParseSimpleDeclaration(StmtVector &Stmts, unsigned Context, + SourceLocation &DeclEnd, + ParsedAttributesWithRange &attrs, + bool RequireSemi, ForRangeInit *FRI) { // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this); DS.takeAttributesFrom(attrs); @@ -1047,14 +1155,15 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.is(tok::semi)) { + DeclEnd = Tok.getLocation(); if (RequireSemi) ConsumeToken(); Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS); DS.complete(TheDecl); return Actions.ConvertDeclToDeclGroup(TheDecl); } - - return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI); + + return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI); } /// Returns true if this might be the start of a declarator, or a common typo @@ -1161,15 +1270,33 @@ void Parser::SkipMalformedDecl() { case tok::kw_inline: // 'inline namespace' at the start of a line is almost certainly - // a good place to pick back up parsing. - if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace)) + // a good place to pick back up parsing, except in an Objective-C + // @interface context. + if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace) && + (!ParsingInObjCContainer || CurParsedObjCImpl)) return; break; case tok::kw_namespace: // 'namespace' at the start of a line is almost certainly a good - // place to pick back up parsing. - if (Tok.isAtStartOfLine()) + // place to pick back up parsing, except in an Objective-C + // @interface context. + if (Tok.isAtStartOfLine() && + (!ParsingInObjCContainer || CurParsedObjCImpl)) + return; + break; + + case tok::at: + // @end is very much like } in Objective-C contexts. + if (NextToken().isObjCAtKeyword(tok::objc_end) && + ParsingInObjCContainer) + return; + break; + + case tok::minus: + case tok::plus: + // - and + probably start new method declarations in Objective-C contexts. + if (Tok.isAtStartOfLine() && ParsingInObjCContainer) return; break; @@ -1214,7 +1341,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // declaration. We have to check this because __attribute__ might be the // start of a function definition in GCC-extended K&R C. !isDeclarationAfterDeclarator()) { - + if (isStartOfFunctionDefinition(D)) { if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { Diag(Tok, diag::err_function_declared_typedef); @@ -1227,7 +1354,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs); return Actions.ConvertDeclToDeclGroup(TheDecl); } - + if (isDeclarationSpecifier()) { // If there is an invalid declaration specifier right after the function // prototype, then we must be in a missing semicolon case where this isn't @@ -1269,7 +1396,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclsInGroup.push_back(FirstDecl); bool ExpectSemi = Context != Declarator::ForContext; - + // If we don't have a comma, it is either the end of the list (a ';') or an // error, bail out. while (Tok.is(tok::comma)) { @@ -1303,7 +1430,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, Decl *ThisDecl = ParseDeclarationAfterDeclarator(D); D.complete(ThisDecl); if (ThisDecl) - DeclsInGroup.push_back(ThisDecl); + DeclsInGroup.push_back(ThisDecl); } } @@ -1311,10 +1438,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, *DeclEnd = Tok.getLocation(); if (ExpectSemi && - ExpectAndConsume(tok::semi, - Context == Declarator::FileContext - ? diag::err_invalid_token_after_toplevel_declarator - : diag::err_expected_semi_declaration)) { + ExpectAndConsumeSemi(Context == Declarator::FileContext + ? diag::err_invalid_token_after_toplevel_declarator + : diag::err_expected_semi_declaration)) { // Okay, there was no semicolon and one was expected. If we see a // declaration specifier, just assume it was missing and continue parsing. // Otherwise things are very confused and we skip to recover. @@ -1388,7 +1514,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, case ParsedTemplateInfo::NonTemplate: ThisDecl = Actions.ActOnDeclarator(getCurScope(), D); break; - + case ParsedTemplateInfo::Template: case ParsedTemplateInfo::ExplicitSpecialization: ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(), @@ -1397,9 +1523,9 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, TemplateInfo.TemplateParams->size()), D); break; - + case ParsedTemplateInfo::ExplicitInstantiation: { - DeclResult ThisRes + DeclResult ThisRes = Actions.ActOnExplicitInstantiation(getCurScope(), TemplateInfo.ExternLoc, TemplateInfo.TemplateLoc, @@ -1408,7 +1534,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, SkipUntil(tok::semi, true, true); return 0; } - + ThisDecl = ThisRes.get(); break; } @@ -1441,10 +1567,11 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, if (Tok.is(tok::code_completion)) { Actions.CodeCompleteInitializer(getCurScope(), ThisDecl); + Actions.FinalizeDeclaration(ThisDecl); cutOffParsing(); return 0; } - + ExprResult Init(ParseInitializer()); if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { @@ -1497,7 +1624,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, Actions.AddInitializerToDecl(ThisDecl, Initializer.take(), /*DirectInit=*/true, TypeContainsAuto); } - } else if (getLangOpts().CPlusPlus0x && Tok.is(tok::l_brace)) { + } else if (getLangOpts().CPlusPlus0x && Tok.is(tok::l_brace) && + (!CurParsedObjCImpl || !D.isFunctionDeclarator())) { // Parse C++0x braced-init-list. Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); @@ -1543,7 +1671,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS, // Validate declspec for type-name. unsigned Specs = DS.getParsedSpecifiers(); - if (DSC == DSC_type_specifier && !DS.hasTypeSpecifier()) { + if ((DSC == DSC_type_specifier || DSC == DSC_trailing) && + !DS.hasTypeSpecifier()) { Diag(Tok, diag::err_expected_type); DS.SetTypeSpecError(); } else if (Specs == DeclSpec::PQ_None && !DS.getNumProtocolQualifiers() && @@ -1635,12 +1764,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, assert(!DS.hasTypeSpecifier() && "Type specifier checked above"); // Since we know that this either implicit int (which is rare) or an - // error, do lookahead to try to do better recovery. This never applies within - // a type specifier. - // FIXME: Don't bail out here in languages with no implicit int (like - // C++ with no -fms-extensions). This is much more likely to be an undeclared - // type or typo than a use of implicit int. - if (DSC != DSC_type_specifier && + // error, do lookahead to try to do better recovery. This never applies + // within a type specifier. Outside of C++, we allow this even if the + // language doesn't "officially" support implicit int -- we support + // implicit int as an extension in C99 and C11. Allegedly, MS also + // supports implicit int in C++ mode. + if (DSC != DSC_type_specifier && DSC != DSC_trailing && + (!getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt) && isValidAfterIdentifierInDeclarator(NextToken())) { // If this token is valid for implicit int, e.g. "static x = 4", then // we just avoid eating the identifier, so it will be parsed as the @@ -1648,6 +1778,13 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, return false; } + if (getLangOpts().CPlusPlus && + DS.getStorageClassSpec() == DeclSpec::SCS_auto) { + // Don't require a type specifier if we have the 'auto' storage class + // specifier in C++98 -- we'll promote it to a type specifier. + return false; + } + // Otherwise, if we don't consume this token, we are going to emit an // error anyway. Try to recover from various common problems. Check // to see if this was a reference to a tag name without a tag specified. @@ -1671,9 +1808,20 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, } if (TagName) { + IdentifierInfo *TokenName = Tok.getIdentifierInfo(); + LookupResult R(Actions, TokenName, SourceLocation(), + Sema::LookupOrdinaryName); + Diag(Loc, diag::err_use_of_tag_name_without_tag) - << Tok.getIdentifierInfo() << TagName << getLangOpts().CPlusPlus - << FixItHint::CreateInsertion(Tok.getLocation(),FixitTagName); + << TokenName << TagName << getLangOpts().CPlusPlus + << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); + + if (Actions.LookupParsedName(R, getCurScope(), SS)) { + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); + I != IEnd; ++I) + Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) + << TokenName << TagName; + } // Parse this as a tag as if the missing tag were present. if (TagKind == tok::kw_enum) @@ -1685,11 +1833,55 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, } } - // This is almost certainly an invalid type name. Let the action emit a + // Determine whether this identifier could plausibly be the name of something + // being declared (with a missing type). + if (DSC != DSC_type_specifier && DSC != DSC_trailing && + (!SS || DSC == DSC_top_level || DSC == DSC_class)) { + // Look ahead to the next token to try to figure out what this declaration + // was supposed to be. + switch (NextToken().getKind()) { + case tok::comma: + case tok::equal: + case tok::kw_asm: + case tok::l_brace: + case tok::l_square: + case tok::semi: + // This looks like a variable declaration. The type is probably missing. + // We're done parsing decl-specifiers. + return false; + + case tok::l_paren: { + // static x(4); // 'x' is not a type + // x(int n); // 'x' is not a type + // x (*p)[]; // 'x' is a type + // + // Since we're in an error case (or the rare 'implicit int in C++' MS + // extension), we can afford to perform a tentative parse to determine + // which case we're in. + TentativeParsingAction PA(*this); + ConsumeToken(); + TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false); + PA.Revert(); + if (TPR == TPResult::False()) + return false; + // The identifier is followed by a parenthesized declarator. + // It's supposed to be a type. + break; + } + + default: + // This is probably supposed to be a type. This includes cases like: + // int f(itn); + // struct S { unsinged : 4; }; + break; + } + } + + // This is almost certainly an invalid type name. Let the action emit a // diagnostic and attempt to recover. ParsedType T; - if (Actions.DiagnoseUnknownTypeName(*Tok.getIdentifierInfo(), Loc, - getCurScope(), SS, T)) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + if (Actions.DiagnoseUnknownTypeName(II, Loc, getCurScope(), SS, T)) { // The action emitted a diagnostic, so we don't have to. if (T) { // The action has suggested that the type T could be used. Set that as @@ -1700,11 +1892,15 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, T); DS.SetRangeEnd(Tok.getLocation()); ConsumeToken(); - + // There may be other declaration specifiers after this. + return true; + } else if (II != Tok.getIdentifierInfo()) { + // If no type was suggested, the correction is to a keyword + Tok.setKind(II->getTokenID()); // There may be other declaration specifiers after this. return true; } - + // Fall through; the action had no suggestion for us. } else { // The action did not emit a diagnostic, so emit one now. @@ -1729,7 +1925,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, /// /// \param Context the declarator context, which is one of the /// Declarator::TheContext enumerator values. -Parser::DeclSpecContext +Parser::DeclSpecContext Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) { if (Context == Declarator::MemberContext) return DSC_class; @@ -1806,8 +2002,12 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, ExprVector ArgExprs(Actions); ArgExprs.push_back(ArgExpr.release()); + // FIXME: This should not be GNU, but we since the attribute used is + // based on the spelling, and there is no true spelling for + // C++11 attributes, this isn't accepted. Attrs.addNew(PP.getIdentifierInfo("aligned"), KWLoc, 0, KWLoc, - 0, T.getOpenLocation(), ArgExprs.take(), 1, false, true); + 0, T.getOpenLocation(), ArgExprs.take(), 1, + AttributeList::AS_GNU); } /// ParseDeclarationSpecifiers @@ -1845,8 +2045,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DS.SetRangeStart(Tok.getLocation()); DS.SetRangeEnd(Tok.getLocation()); } - + bool EnteringContext = (DSContext == DSC_class || DSContext == DSC_top_level); + bool AttrsLastTime = false; + ParsedAttributesWithRange attrs(AttrFactory); while (1) { bool isInvalid = false; const char *PrevSpec = 0; @@ -1857,14 +2059,32 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, switch (Tok.getKind()) { default: DoneWithDeclSpec: - // [C++0x] decl-specifier-seq: decl-specifier attribute-specifier-seq[opt] - MaybeParseCXX0XAttributes(DS.getAttributes()); + if (!AttrsLastTime) + ProhibitAttributes(attrs); + else + DS.takeAttributesFrom(attrs); // If this is not a declaration specifier token, we're done reading decl // specifiers. First verify that DeclSpec's are consistent. DS.Finish(Diags, PP); return; + case tok::l_square: + case tok::kw_alignas: + if (!isCXX11AttributeSpecifier()) + goto DoneWithDeclSpec; + + ProhibitAttributes(attrs); + // FIXME: It would be good to recover by accepting the attributes, + // but attempting to do that now would cause serious + // madness in terms of diagnostics. + attrs.clear(); + attrs.Range = SourceRange(); + + ParseCXX11Attributes(attrs); + AttrsLastTime = true; + continue; + case tok::code_completion: { Sema::ParserCompletionContext CCC = Sema::PCC_Namespace; if (DS.hasTypeSpecifier()) { @@ -1875,25 +2095,25 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Scope::FunctionPrototypeScope | Scope::AtCatchScope)) == 0; bool AllowNestedNameSpecifiers - = DSContext == DSC_top_level || + = DSContext == DSC_top_level || (DSContext == DSC_class && DS.isFriendSpecified()); Actions.CodeCompleteDeclSpec(getCurScope(), DS, - AllowNonIdentifiers, + AllowNonIdentifiers, AllowNestedNameSpecifiers); return cutOffParsing(); - } - + } + if (getCurScope()->getFnParent() || getCurScope()->getBlockParent()) CCC = Sema::PCC_LocalDeclarationSpecifiers; else if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) - CCC = DSContext == DSC_class? Sema::PCC_MemberTemplate + CCC = DSContext == DSC_class? Sema::PCC_MemberTemplate : Sema::PCC_Template; else if (DSContext == DSC_class) CCC = Sema::PCC_Class; else if (CurParsedObjCImpl) CCC = Sema::PCC_ObjCImplementation; - + Actions.CodeCompleteOrdinaryName(getCurScope(), CCC); return cutOffParsing(); } @@ -1910,7 +2130,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; case tok::annot_cxxscope: { - if (DS.hasTypeSpecifier()) + if (DS.hasTypeSpecifier() || DS.isTypeAltiVecVector()) goto DoneWithDeclSpec; CXXScopeSpec SS; @@ -1940,10 +2160,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // // the name is instead considered to name the constructor of // class C. - // + // // Thus, if the template-name is actually the constructor // name, then the code is ill-formed; this interpretation is - // reinforced by the NAD status of core issue 635. + // reinforced by the NAD status of core issue 635. TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next); if ((DSContext == DSC_top_level || (DSContext == DSC_class && DS.isFriendSpecified())) && @@ -1980,7 +2200,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if (Tok.getAnnotationValue()) { ParsedType T = getTypeAnnotation(Tok); isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, - Tok.getAnnotationEndLoc(), + Tok.getAnnotationEndLoc(), PrevSpec, DiagID, T); } else @@ -1996,7 +2216,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // check whether this is a constructor declaration. if ((DSContext == DSC_top_level || (DSContext == DSC_class && DS.isFriendSpecified())) && - Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(), + Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(), &SS)) { if (isConstructorDeclarator()) goto DoneWithDeclSpec; @@ -2049,7 +2269,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DiagID, T); } else DS.SetTypeSpecError(); - + if (isInvalid) break; @@ -2058,10 +2278,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id' // is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an - // Objective-C interface. + // Objective-C interface. if (Tok.is(tok::less) && getLangOpts().ObjC1) ParseObjCProtocolQualifiers(DS); - + continue; } @@ -2082,7 +2302,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // We're done with the declaration-specifiers. goto DoneWithDeclSpec; - + // typedef-name case tok::kw_decltype: case tok::identifier: { @@ -2108,6 +2328,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid)) break; + // [AltiVec] 2.2: [If the 'vector' specifier is used] The syntax does not + // allow the use of a typedef name as a type specifier. + if (DS.isTypeAltiVecVector()) + goto DoneWithDeclSpec; + ParsedType TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope()); @@ -2136,10 +2361,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id' // is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an - // Objective-C interface. + // Objective-C interface. if (Tok.is(tok::less) && getLangOpts().ObjC1) ParseObjCProtocolQualifiers(DS); - + // Need to support trailing type qualifiers (e.g. "id<p> const"). // If a type specifier follows, it will be diagnosed elsewhere. continue; @@ -2179,9 +2404,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; // Microsoft single token adornments. - case tok::kw___forceinline: - // FIXME: Add handling here! - break; + case tok::kw___forceinline: { + isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec, DiagID); + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = ConsumeToken(); + // FIXME: This does not work correctly if it is set to be a declspec + // attribute, and a GNU attribute is simply incorrect. + DS.getAttributes().addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, + SourceLocation(), 0, 0, AttributeList::AS_GNU); + continue; + } case tok::kw___ptr64: case tok::kw___ptr32: @@ -2266,7 +2498,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // alignment-specifier case tok::kw__Alignas: if (!getLangOpts().C11) - Diag(Tok, diag::ext_c11_alignas); + Diag(Tok, diag::ext_c11_alignment) << Tok.getName(); ParseAlignmentSpecifier(DS.getAttributes()); continue; @@ -2285,7 +2517,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, case tok::kw___module_private__: isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID); break; - + // constexpr case tok::kw_constexpr: isInvalid = DS.SetConstexprSpec(Loc, PrevSpec, DiagID); @@ -2422,15 +2654,15 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // cv-qualifier: case tok::kw_const: isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/true); break; case tok::kw_volatile: isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/true); break; case tok::kw_restrict: isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/true); break; // C++ typename-specifier: @@ -2461,7 +2693,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; // OpenCL qualifiers: - case tok::kw_private: + case tok::kw_private: if (!getLangOpts().OpenCL) goto DoneWithDeclSpec; case tok::kw___private: @@ -2473,7 +2705,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, case tok::kw___read_write: ParseOpenCLQualifiers(DS); break; - + case tok::less: // GCC ObjC supports types like "<SomeProtocol>" as a synonym for // "id<SomeProtocol>". This is hopelessly old fashioned and dangerous, @@ -2485,7 +2717,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Diag(Loc, diag::warn_objc_protocol_qualifier_missing_id) << FixItHint::CreateInsertion(Loc, "id") << SourceRange(Loc, DS.getSourceRange().getEnd()); - + // Need to support trailing type qualifiers (e.g. "id<p> const"). // If a type specifier follows, it will be diagnosed elsewhere. continue; @@ -2494,7 +2726,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if (isInvalid) { assert(PrevSpec && "Method did not return previous specifier!"); assert(DiagID); - + if (DiagID == diag::ext_duplicate_declspec) Diag(Tok, DiagID) << PrevSpec << FixItHint::CreateRemoval(Tok.getLocation()); @@ -2505,6 +2737,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DS.SetRangeEnd(Tok.getLocation()); if (DiagID != diag::err_bool_redeclaration) ConsumeToken(); + + AttrsLastTime = false; } } @@ -2526,8 +2760,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, /// [GNU] declarator[opt] ':' constant-expression attributes[opt] /// void Parser:: -ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { - +ParseStructDeclaration(ParsingDeclSpec &DS, FieldCallback &Fields) { + if (Tok.is(tok::kw___extension__)) { // __extension__ silences extension warnings in the subexpression. ExtensionRAIIObject O(Diags); // Use RAII to do this. @@ -2541,7 +2775,9 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { // If there are no declarators, this is a free-standing declaration // specifier. Let the actions module cope with it. if (Tok.is(tok::semi)) { - Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS); + Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, + DS); + DS.complete(TheDecl); return; } @@ -2549,8 +2785,7 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { bool FirstDeclarator = true; SourceLocation CommaLoc; while (1) { - ParsingDeclRAIIObject PD(*this); - FieldDeclarator DeclaratorInfo(DS); + ParsingFieldDeclarator DeclaratorInfo(*this, DS); DeclaratorInfo.D.setCommaLoc(CommaLoc); // Attributes are only allowed here on successive declarators. @@ -2578,8 +2813,7 @@ ParseStructDeclaration(DeclSpec &DS, FieldCallback &Fields) { MaybeParseGNUAttributes(DeclaratorInfo.D); // We're done with this declarator; invoke the callback. - Decl *D = Fields.invoke(DeclaratorInfo); - PD.complete(D); + Fields.invoke(DeclaratorInfo); // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. @@ -2630,16 +2864,10 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // Check for extraneous top-level semicolon. if (Tok.is(tok::semi)) { - Diag(Tok, diag::ext_extra_struct_semi) - << DeclSpec::getSpecifierName((DeclSpec::TST)TagType) - << FixItHint::CreateRemoval(Tok.getLocation()); - ConsumeToken(); + ConsumeExtraSemi(InsideStruct, TagType); continue; } - // Parse all the comma separated declarators. - DeclSpec DS(AttrFactory); - if (!Tok.is(tok::at)) { struct CFieldCallback : FieldCallback { Parser &P; @@ -2650,16 +2878,18 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, SmallVectorImpl<Decl *> &FieldDecls) : P(P), TagDecl(TagDecl), FieldDecls(FieldDecls) {} - virtual Decl *invoke(FieldDeclarator &FD) { + void invoke(ParsingFieldDeclarator &FD) { // Install the declarator into the current TagDecl. Decl *Field = P.Actions.ActOnField(P.getCurScope(), TagDecl, FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D, FD.BitfieldSize); FieldDecls.push_back(Field); - return Field; + FD.complete(Field); } } Callback(*this, TagDecl, FieldDecls); + // Parse all the comma separated declarators. + ParsingDeclSpec DS(*this); ParseStructDeclaration(DS, Callback); } else { // Handle @defs ConsumeToken(); @@ -2752,30 +2982,46 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, return cutOffParsing(); } + // If attributes exist after tag, parse them. + ParsedAttributesWithRange attrs(AttrFactory); + MaybeParseGNUAttributes(attrs); + MaybeParseCXX0XAttributes(attrs); + + // If declspecs exist after tag, parse them. + while (Tok.is(tok::kw___declspec)) + ParseMicrosoftDeclSpec(attrs); + SourceLocation ScopedEnumKWLoc; bool IsScopedUsingClassTag = false; + // In C++11, recognize 'enum class' and 'enum struct'. if (getLangOpts().CPlusPlus0x && (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) { Diag(Tok, diag::warn_cxx98_compat_scoped_enum); IsScopedUsingClassTag = Tok.is(tok::kw_class); ScopedEnumKWLoc = ConsumeToken(); - } - // C++11 [temp.explicit]p12: The usual access controls do not apply to names - // used to specify explicit instantiations. We extend this to also cover - // explicit specializations. - Sema::SuppressAccessChecksRAII SuppressAccess(Actions, - TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || - TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); + // Attributes are not allowed between these keywords. Diagnose, + // but then just treat them like they appeared in the right place. + ProhibitAttributes(attrs); - // If attributes exist after tag, parse them. - ParsedAttributes attrs(AttrFactory); - MaybeParseGNUAttributes(attrs); + // They are allowed afterwards, though. + MaybeParseGNUAttributes(attrs); + MaybeParseCXX0XAttributes(attrs); + while (Tok.is(tok::kw___declspec)) + ParseMicrosoftDeclSpec(attrs); + } - // If declspecs exist after tag, parse them. - while (Tok.is(tok::kw___declspec)) - ParseMicrosoftDeclSpec(attrs); + // C++11 [temp.explicit]p12: + // The usual access controls do not apply to names used to specify + // explicit instantiations. + // We extend this to also cover explicit specializations. Note that + // we don't suppress if this turns out to be an elaborated type + // specifier. + bool shouldDelayDiagsInTag = + (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); + SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); // Enum definitions should not be parsed in a trailing-return-type. bool AllowDeclaration = DSC != DSC_trailing; @@ -2789,8 +3035,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // "enum foo : bar;" is not a potential typo for "enum foo::bar;" // if a fixed underlying type is allowed. ColonProtectionRAIIObject X(*this, AllowFixedUnderlyingType); - - if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false)) return; @@ -2831,32 +3077,35 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, IsScopedUsingClassTag = false; } - // Stop suppressing access control now we've parsed the enum name. - SuppressAccess.done(); + // Okay, end the suppression area. We'll decide whether to emit the + // diagnostics in a second. + if (shouldDelayDiagsInTag) + diagsFromTag.done(); TypeResult BaseType; // Parse the fixed underlying type. + bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope; if (AllowFixedUnderlyingType && Tok.is(tok::colon)) { bool PossibleBitfield = false; - if (getCurScope()->getFlags() & Scope::ClassScope) { + if (CanBeBitfield) { // If we're in class scope, this can either be an enum declaration with // an underlying type, or a declaration of a bitfield member. We try to // use a simple disambiguation scheme first to catch the common cases - // (integer literal, sizeof); if it's still ambiguous, we then consider - // anything that's a simple-type-specifier followed by '(' as an - // expression. This suffices because function types are not valid + // (integer literal, sizeof); if it's still ambiguous, we then consider + // anything that's a simple-type-specifier followed by '(' as an + // expression. This suffices because function types are not valid // underlying types anyway. TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind()); - // If the next token starts an expression, we know we're parsing a + // If the next token starts an expression, we know we're parsing a // bit-field. This is the common case. if (TPR == TPResult::True()) PossibleBitfield = true; // If the next token starts a type-specifier-seq, it may be either a // a fixed underlying type or the start of a function-style cast in C++; - // lookahead one more token to see if it's obvious that we have a + // lookahead one more token to see if it's obvious that we have a // fixed underlying type. - else if (TPR == TPResult::False() && + else if (TPR == TPResult::False() && GetLookAheadToken(2).getKind() == tok::semi) { // Consume the ':'. ConsumeToken(); @@ -2894,7 +3143,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, if (!PossibleBitfield) { SourceRange Range; BaseType = ParseTypeName(&Range); - + if (!getLangOpts().CPlusPlus0x && !getLangOpts().ObjC2) Diag(StartLoc, diag::ext_ms_enum_fixed_underlying_type) << Range; @@ -2914,16 +3163,39 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // enum foo {..}; void bar() { enum foo x; } <- use of old foo. // Sema::TagUseKind TUK; - if (DS.isFriendSpecified()) - TUK = Sema::TUK_Friend; - else if (!AllowDeclaration) + if (!AllowDeclaration) { TUK = Sema::TUK_Reference; - else if (Tok.is(tok::l_brace)) - TUK = Sema::TUK_Definition; - else if (Tok.is(tok::semi) && DSC != DSC_type_specifier) - TUK = Sema::TUK_Declaration; - else + } else if (Tok.is(tok::l_brace)) { + if (DS.isFriendSpecified()) { + Diag(Tok.getLocation(), diag::err_friend_decl_defines_type) + << SourceRange(DS.getFriendSpecLoc()); + ConsumeBrace(); + SkipUntil(tok::r_brace); + TUK = Sema::TUK_Friend; + } else { + TUK = Sema::TUK_Definition; + } + } else if (DSC != DSC_type_specifier && + (Tok.is(tok::semi) || + (Tok.isAtStartOfLine() && + !isValidAfterTypeSpecifier(CanBeBitfield)))) { + TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration; + if (Tok.isNot(tok::semi)) { + // A semicolon was missing after this declaration. Diagnose and recover. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, + "enum"); + PP.EnterToken(Tok); + Tok.setKind(tok::semi); + } + } else { TUK = Sema::TUK_Reference; + } + + // If this is an elaborated type specifier, and we delayed + // diagnostics before, just merge them into the current pool. + if (TUK == Sema::TUK_Reference && shouldDelayDiagsInTag) { + diagsFromTag.redelay(); + } MultiTemplateParamsArg TParams; if (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate && @@ -2947,6 +3219,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TemplateInfo.TemplateParams->size()); } + if (TUK == Sema::TUK_Reference) + ProhibitAttributes(attrs); + if (!Name && TUK != Sema::TUK_Definition) { Diag(Tok, diag::err_enumerator_unnamed_no_def); @@ -2966,52 +3241,44 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, IsScopedUsingClassTag, BaseType); if (IsDependent) { - // This enum has a dependent nested-name-specifier. Handle it as a + // This enum has a dependent nested-name-specifier. Handle it as a // dependent tag. if (!Name) { DS.SetTypeSpecError(); Diag(Tok, diag::err_expected_type_name_after_typename); return; } - + TypeResult Type = Actions.ActOnDependentTag(getCurScope(), DeclSpec::TST_enum, - TUK, SS, Name, StartLoc, + TUK, SS, Name, StartLoc, NameLoc); if (Type.isInvalid()) { DS.SetTypeSpecError(); return; } - + if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, DiagID, Type.get())) Diag(StartLoc, DiagID) << PrevSpec; - + return; } if (!TagDecl) { - // The action failed to produce an enumeration tag. If this is a + // The action failed to produce an enumeration tag. If this is a // definition, consume the entire definition. if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) { ConsumeBrace(); SkipUntil(tok::r_brace); } - + DS.SetTypeSpecError(); return; } - if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) { - if (TUK == Sema::TUK_Friend) { - Diag(Tok, diag::err_friend_decl_defines_type) - << SourceRange(DS.getFriendSpecLoc()); - ConsumeBrace(); - SkipUntil(tok::r_brace); - } else { - ParseEnumBody(StartLoc, TagDecl); - } - } + if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) + ParseEnumBody(StartLoc, TagDecl); if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, NameLoc.isValid() ? NameLoc : StartLoc, @@ -3051,13 +3318,15 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { SourceLocation IdentLoc = ConsumeToken(); // If attributes exist after the enumerator, parse them. - ParsedAttributes attrs(AttrFactory); + ParsedAttributesWithRange attrs(AttrFactory); MaybeParseGNUAttributes(attrs); + MaybeParseCXX0XAttributes(attrs); + ProhibitAttributes(attrs); SourceLocation EqualLoc; ExprResult AssignedVal; - ParsingDeclRAIIObject PD(*this); - + ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent); + if (Tok.is(tok::equal)) { EqualLoc = ConsumeToken(); AssignedVal = ParseConstantExpression(); @@ -3072,26 +3341,27 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { attrs.getList(), EqualLoc, AssignedVal.release()); PD.complete(EnumConstDecl); - + EnumConstantDecls.push_back(EnumConstDecl); LastEnumConstDecl = EnumConstDecl; if (Tok.is(tok::identifier)) { // We're missing a comma between enumerators. SourceLocation Loc = PP.getLocForEndOfToken(PrevTokLocation); - Diag(Loc, diag::err_enumerator_list_missing_comma) + Diag(Loc, diag::err_enumerator_list_missing_comma) << FixItHint::CreateInsertion(Loc, ", "); continue; } - + if (Tok.isNot(tok::comma)) break; SourceLocation CommaLoc = ConsumeToken(); if (Tok.isNot(tok::identifier)) { if (!getLangOpts().C99 && !getLangOpts().CPlusPlus0x) - Diag(CommaLoc, diag::ext_enumerator_list_comma) - << getLangOpts().CPlusPlus + Diag(CommaLoc, getLangOpts().CPlusPlus ? + diag::ext_enumerator_list_comma_cxx : + diag::ext_enumerator_list_comma_c) << FixItHint::CreateRemoval(CommaLoc); else if (getLangOpts().CPlusPlus0x) Diag(CommaLoc, diag::warn_cxx98_compat_enumerator_list_comma) @@ -3114,6 +3384,18 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { EnumScope.Exit(); Actions.ActOnTagFinishDefinition(getCurScope(), EnumDecl, T.getCloseLocation()); + + // The next token must be valid after an enum definition. If not, a ';' + // was probably forgotten. + bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope; + if (!isValidAfterTypeSpecifier(CanBeBitfield)) { + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, "enum"); + // Push this token back into the preprocessor and change our current token + // to ';' so that the rest of the code recovers as though there were an + // ';' after the definition. + PP.EnterToken(Tok); + Tok.setKind(tok::semi); + } } /// isTypeSpecifierQualifier - Return true if the current token could be the @@ -3171,14 +3453,14 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { case tok::kw__Decimal64: case tok::kw__Decimal128: case tok::kw___vector: - + // struct-or-union-specifier (C99) or class-specifier (C++) case tok::kw_class: case tok::kw_struct: case tok::kw_union: // enum-specifier case tok::kw_enum: - + // typedef-name case tok::annot_typename: return true; @@ -3319,16 +3601,16 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { return true; if (Tok.is(tok::identifier)) return false; - + // If we're in Objective-C and we have an Objective-C class type followed - // by an identifier and then either ':' or ']', in a place where an + // by an identifier and then either ':' or ']', in a place where an // expression is permitted, then this is probably a class message send // missing the initial '['. In this case, we won't consider this to be // the start of a declaration. - if (DisambiguatingWithExpression && + if (DisambiguatingWithExpression && isStartOfObjCClassMessageMissingOpenBracket()) return false; - + return isDeclarationSpecifier(); case tok::coloncolon: // ::foo::bar @@ -3353,7 +3635,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // Modules case tok::kw___module_private__: - + // type-specifiers case tok::kw_short: case tok::kw_long: @@ -3423,7 +3705,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::annot_typename: return !DisambiguatingWithExpression || !isStartOfObjCClassMessageMissingOpenBracket(); - + case tok::kw___declspec: case tok::kw___cdecl: case tok::kw___stdcall: @@ -3453,7 +3735,7 @@ bool Parser::isConstructorDeclarator() { // Parse the C++ scope specifier. CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/true)) { TPA.Revert(); return false; @@ -3540,10 +3822,10 @@ bool Parser::isConstructorDeclarator() { /// ParseTypeQualifierListOpt /// type-qualifier-list: [C99 6.7.5] /// type-qualifier -/// [vendor] attributes +/// [vendor] attributes /// [ only if VendorAttributesAllowed=true ] /// type-qualifier-list type-qualifier -/// [vendor] type-qualifier-list attributes +/// [vendor] type-qualifier-list attributes /// [ only if VendorAttributesAllowed=true ] /// [C++0x] attribute-specifier[opt] is allowed before cv-qualifier-seq /// [ only if CXX0XAttributesAllowed=true ] @@ -3571,22 +3853,22 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, case tok::code_completion: Actions.CodeCompleteTypeQualifiers(DS); return cutOffParsing(); - + case tok::kw_const: isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/false); break; case tok::kw_volatile: isInvalid = DS.SetTypeQual(DeclSpec::TQ_volatile, Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/false); break; case tok::kw_restrict: isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID, - getLangOpts()); + getLangOpts(), /*IsTypeSpec*/false); break; // OpenCL qualifiers: - case tok::kw_private: + case tok::kw_private: if (!getLangOpts().OpenCL) goto DoneWithTypeQuals; case tok::kw___private: @@ -3692,7 +3974,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, DirectDeclParseFunction DirectDeclParser) { if (Diags.hasAllExtensionsSilenced()) D.setExtension(); - + // C++ member pointers start with a '::' or a nested-name. // Member pointers get special handling, since there's no place for the // scope spec in the generic path below. @@ -3886,7 +4168,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (D.getCXXScopeSpec().isEmpty()) { bool EnteringContext = D.getContext() == Declarator::FileContext || D.getContext() == Declarator::MemberContext; - ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), ParsedType(), + ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), ParsedType(), EnteringContext); } @@ -3899,9 +4181,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // C++0x [dcl.fct]p14: // There is a syntactic ambiguity when an ellipsis occurs at the end - // of a parameter-declaration-clause without a preceding comma. In - // this case, the ellipsis is parsed as part of the - // abstract-declarator if the type of the parameter names a template + // of a parameter-declaration-clause without a preceding comma. In + // this case, the ellipsis is parsed as part of the + // abstract-declarator if the type of the parameter names a template // parameter pack that has not been expanded; otherwise, it is parsed // as part of the parameter-declaration-clause. if (Tok.is(tok::ellipsis) && D.getCXXScopeSpec().isEmpty() && @@ -3940,9 +4222,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { AllowConstructorName = (D.getContext() == Declarator::MemberContext); SourceLocation TemplateKWLoc; - if (ParseUnqualifiedId(D.getCXXScopeSpec(), - /*EnteringContext=*/true, - /*AllowDestructorName=*/true, + if (ParseUnqualifiedId(D.getCXXScopeSpec(), + /*EnteringContext=*/true, + /*AllowDestructorName=*/true, AllowConstructorName, ParsedType(), TemplateKWLoc, @@ -3992,6 +4274,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // portion is empty), if an abstract-declarator is allowed. D.SetIdentifier(0, Tok.getLocation()); } else { + if (Tok.getKind() == tok::annot_pragma_parser_crash) + *(volatile int*) 0x11 = 0; if (D.getContext() == Declarator::MemberContext) Diag(Tok, diag::err_expected_member_name_or_semi) << D.getDeclSpec().getSourceRange(); @@ -4020,17 +4304,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // The paren may be part of a C++ direct initializer, eg. "int x(1);". // In such a case, check if we actually have a function declarator; if it // is not, the declarator has been fully parsed. - if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) { - // When not in file scope, warn for ambiguous function declarators, just - // in case the author intended it as a variable definition. - bool warnIfAmbiguous = D.getContext() != Declarator::FileContext; - if (!isCXXFunctionDeclarator(warnIfAmbiguous)) - break; - } + bool IsAmbiguous = false; + if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit() && + !isCXXFunctionDeclarator(&IsAmbiguous)) + break; ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); - ParseFunctionDeclarator(D, attrs, T); + ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -4038,7 +4319,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { break; } } -} +} /// ParseParenDeclarator - We parsed the declarator D up to a paren. This is /// only called before the identifier, so these are most likely just grouping @@ -4124,7 +4405,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); // Match the ')'. T.consumeClose(); - D.AddTypeInfo(DeclaratorChunk::getParen(T.getOpenLocation(), + D.AddTypeInfo(DeclaratorChunk::getParen(T.getOpenLocation(), T.getCloseLocation()), attrs, T.getCloseLocation()); @@ -4147,7 +4428,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope); - ParseFunctionDeclarator(D, attrs, T, RequiresArg); + ParseFunctionDeclarator(D, attrs, T, false, RequiresArg); PrototypeScope.Exit(); } @@ -4173,8 +4454,9 @@ void Parser::ParseParenDeclarator(Declarator &D) { void Parser::ParseFunctionDeclarator(Declarator &D, ParsedAttributes &FirstArgAttrs, BalancedDelimiterTracker &Tracker, + bool IsAmbiguous, bool RequiresArg) { - assert(getCurScope()->isFunctionPrototypeScope() && + assert(getCurScope()->isFunctionPrototypeScope() && "Should call from a Function scope"); // lparen is already consumed! assert(D.isPastIdentifier() && "Should not call before identifier!"); @@ -4198,7 +4480,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, SmallVector<SourceRange, 2> DynamicExceptionRanges; ExprResult NoexceptExpr; ParsedAttributes FnAttrs(AttrFactory); - ParsedType TrailingReturnType; + TypeResult TrailingReturnType; Actions.ActOnStartFunctionDeclarator(); @@ -4248,16 +4530,16 @@ void Parser::ParseFunctionDeclarator(Declarator &D, } // C++11 [expr.prim.general]p3: - // If a declaration declares a member function or member function - // template of a class X, the expression this is a prvalue of type + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq - // and the end of the function-definition, member-declarator, or + // and the end of the function-definition, member-declarator, or // declarator. - bool IsCXX11MemberFunction = + bool IsCXX11MemberFunction = getLangOpts().CPlusPlus0x && (D.getContext() == Declarator::MemberContext || (D.getContext() == Declarator::FileContext && - D.getCXXScopeSpec().isValid() && + D.getCXXScopeSpec().isValid() && Actions.CurContext->isRecord())); Sema::CXXThisScopeRAII ThisScope(Actions, dyn_cast<CXXRecordDecl>(Actions.CurContext), @@ -4280,7 +4562,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, if (getLangOpts().CPlusPlus0x && Tok.is(tok::arrow)) { Diag(Tok, diag::warn_cxx98_compat_trailing_return_type); SourceRange Range; - TrailingReturnType = ParseTrailingReturnType(Range).get(); + TrailingReturnType = ParseTrailingReturnType(Range); if (Range.getEnd().isValid()) EndLoc = Range.getEnd(); } @@ -4290,7 +4572,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // Remember that we parsed a function type, and remember the attributes. D.AddTypeInfo(DeclaratorChunk::getFunction(HasProto, /*isVariadic=*/EllipsisLoc.isValid(), - EllipsisLoc, + IsAmbiguous, EllipsisLoc, ParamInfo.data(), ParamInfo.size(), DS.getTypeQualifiers(), RefQualifierIsLValueRef, @@ -4303,7 +4585,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, DynamicExceptions.size(), NoexceptExpr.isUsable() ? NoexceptExpr.get() : 0, - Tracker.getOpenLocation(), + Tracker.getOpenLocation(), EndLoc, D, TrailingReturnType), FnAttrs, EndLoc); @@ -4528,7 +4810,7 @@ void Parser::ParseParameterDeclarationClause( // Consume the '='. ConsumeToken(); - // The argument isn't actually potentially evaluated unless it is + // The argument isn't actually potentially evaluated unless it is // used. EnterExpressionEvaluationContext Eval(Actions, Sema::PotentiallyEvaluatedIfUsed, @@ -4560,7 +4842,7 @@ void Parser::ParseParameterDeclarationClause( if (Tok.isNot(tok::comma)) { if (Tok.is(tok::ellipsis)) { EllipsisLoc = ConsumeToken(); // Consume the ellipsis. - + if (!getLangOpts().CPlusPlus) { // We have ellipsis without a preceding ',', which is ill-formed // in C. Complain and provide the fix. @@ -4568,7 +4850,7 @@ void Parser::ParseParameterDeclarationClause( << FixItHint::CreateInsertion(EllipsisLoc, ", "); } } - + break; } @@ -4598,7 +4880,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) { T.consumeClose(); ParsedAttributes attrs(AttrFactory); MaybeParseCXX0XAttributes(attrs); - + // Remember that we parsed the empty array type. ExprResult NumElements; D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false, 0, @@ -4646,7 +4928,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) { // Handle the case where we have '[*]' as the array size. However, a leading // star could be the start of an expression, for example 'X[*p + 4]'. Verify - // the the token after the star is a ']'. Since stars in arrays are + // the token after the star is a ']'. Since stars in arrays are // infrequent, use of lookahead is not costly here. if (Tok.is(tok::star) && GetLookAheadToken(1).is(tok::r_square)) { ConsumeToken(); // Eat the '*'. diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 5e6c4f50e036..3dc96cf0d294 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -444,6 +444,13 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, CXXScopeSpec SS; SourceLocation TypenameLoc; bool IsTypeName; + ParsedAttributesWithRange attrs(AttrFactory); + + // FIXME: Simply skip the attributes and diagnose, don't bother parsing them. + MaybeParseCXX0XAttributes(attrs); + ProhibitAttributes(attrs); + attrs.clear(); + attrs.Range = SourceRange(); // Ignore optional 'typename'. // FIXME: This is wrong; we should parse this as a typename-specifier. @@ -480,7 +487,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, return 0; } - ParsedAttributes attrs(AttrFactory); + MaybeParseCXX0XAttributes(attrs); // Maybe this is an alias-declaration. bool IsAliasDecl = Tok.is(tok::equal); @@ -533,9 +540,14 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, TypeAlias = ParseTypeName(0, TemplateInfo.Kind ? Declarator::AliasTemplateContext : Declarator::AliasDeclContext, AS, OwnedType); - } else + } else { + // C++11 attributes are not allowed on a using-declaration, but GNU ones + // are. + ProhibitAttributes(attrs); + // Parse (optional) attributes (most likely GNU strong-using extension). MaybeParseGNUAttributes(attrs); + } // Eat ';'. DeclEnd = Tok.getLocation(); @@ -572,6 +584,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, MultiTemplateParamsArg TemplateParamsArg(Actions, TemplateParams ? TemplateParams->data() : 0, TemplateParams ? TemplateParams->size() : 0); + // FIXME: Propagate attributes. return Actions.ActOnAliasDeclaration(getCurScope(), AS, TemplateParamsArg, UsingLoc, Name, TypeAlias); } @@ -874,10 +887,12 @@ Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, } // We have an identifier; check whether it is actually a type. + IdentifierInfo *CorrectedII = 0; ParsedType Type = Actions.getTypeName(*Id, IdLoc, getCurScope(), &SS, true, false, ParsedType(), /*IsCtorOrDtorName=*/false, - /*NonTrivialTypeSourceInfo=*/true); + /*NonTrivialTypeSourceInfo=*/true, + &CorrectedII); if (!Type) { Diag(IdLoc, diag::err_expected_class_name); return true; @@ -900,6 +915,77 @@ Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); } +void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) { + while (Tok.is(tok::kw___single_inheritance) || + Tok.is(tok::kw___multiple_inheritance) || + Tok.is(tok::kw___virtual_inheritance)) { + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = ConsumeToken(); + attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, 0, + SourceLocation(), 0, 0, AttributeList::AS_GNU); + } +} + +/// Determine whether the following tokens are valid after a type-specifier +/// which could be a standalone declaration. This will conservatively return +/// true if there's any doubt, and is appropriate for insert-';' fixits. +bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) { + // This switch enumerates the valid "follow" set for type-specifiers. + switch (Tok.getKind()) { + default: break; + case tok::semi: // struct foo {...} ; + case tok::star: // struct foo {...} * P; + case tok::amp: // struct foo {...} & R = ... + case tok::identifier: // struct foo {...} V ; + case tok::r_paren: //(struct foo {...} ) {4} + case tok::annot_cxxscope: // struct foo {...} a:: b; + case tok::annot_typename: // struct foo {...} a ::b; + case tok::annot_template_id: // struct foo {...} a<int> ::b; + case tok::l_paren: // struct foo {...} ( x); + case tok::comma: // __builtin_offsetof(struct foo{...} , + return true; + case tok::colon: + return CouldBeBitfield; // enum E { ... } : 2; + // Type qualifiers + case tok::kw_const: // struct foo {...} const x; + case tok::kw_volatile: // struct foo {...} volatile x; + case tok::kw_restrict: // struct foo {...} restrict x; + case tok::kw_inline: // struct foo {...} inline foo() {}; + // Storage-class specifiers + case tok::kw_static: // struct foo {...} static x; + case tok::kw_extern: // struct foo {...} extern x; + case tok::kw_typedef: // struct foo {...} typedef x; + case tok::kw_register: // struct foo {...} register x; + case tok::kw_auto: // struct foo {...} auto x; + case tok::kw_mutable: // struct foo {...} mutable x; + case tok::kw_constexpr: // struct foo {...} constexpr x; + // As shown above, type qualifiers and storage class specifiers absolutely + // can occur after class specifiers according to the grammar. However, + // almost no one actually writes code like this. If we see one of these, + // it is much more likely that someone missed a semi colon and the + // type/storage class specifier we're seeing is part of the *next* + // intended declaration, as in: + // + // struct foo { ... } + // typedef int X; + // + // We'd really like to emit a missing semicolon error instead of emitting + // an error on the 'int' saying that you can't have two type specifiers in + // the same declaration of X. Because of this, we look ahead past this + // token to see if it's a type specifier. If so, we know the code is + // otherwise invalid, so we can produce the expected semi error. + if (!isKnownToBeTypeSpecifier(NextToken())) + return true; + break; + case tok::r_brace: // struct bar { struct foo {...} } + // Missing ';' at end of struct is accepted as an extension in C mode. + if (!getLangOpts().CPlusPlus) + return true; + break; + } + return false; +} + /// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or /// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which /// until we reach the start of a definition or see a token that @@ -968,11 +1054,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // As an extension we do not perform access checking on the names used to // specify explicit specializations either. This is important to allow // specializing traits classes for private types. - Sema::SuppressAccessChecksRAII SuppressAccess(Actions, - TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || - TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); + // + // Note that we don't suppress if this turns out to be an elaborated + // type specifier. + bool shouldDelayDiagsInTag = + (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); + SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); - ParsedAttributes attrs(AttrFactory); + ParsedAttributesWithRange attrs(AttrFactory); // If attributes exist after tag, parse them. if (Tok.is(tok::kw___attribute)) ParseGNUAttributes(attrs); @@ -981,6 +1071,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, while (Tok.is(tok::kw___declspec)) ParseMicrosoftDeclSpec(attrs); + // Parse inheritance specifiers. + if (Tok.is(tok::kw___single_inheritance) || + Tok.is(tok::kw___multiple_inheritance) || + Tok.is(tok::kw___virtual_inheritance)) + ParseMicrosoftInheritanceClassAttributes(attrs); + // If C++0x attributes exist here, parse them. // FIXME: Are we consistent with the ordering of parsing of different // styles of attributes? @@ -1103,10 +1199,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } } - // As soon as we're finished parsing the class's template-id, turn access - // checking back on. - SuppressAccess.done(); - // There are four options here. // - If we are in a trailing return type, this is always just a reference, // and we must not try to parse a definition. For instance, @@ -1144,11 +1236,29 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Okay, this is a class definition. TUK = Sema::TUK_Definition; } - } else if (Tok.is(tok::semi) && DSC != DSC_type_specifier) + } else if (DSC != DSC_type_specifier && + (Tok.is(tok::semi) || + (Tok.isAtStartOfLine() && !isValidAfterTypeSpecifier(false)))) { TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration; - else + if (Tok.isNot(tok::semi)) { + // A semicolon was missing after this declaration. Diagnose and recover. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, + TagType == DeclSpec::TST_class ? "class" : + TagType == DeclSpec::TST_struct ? "struct" : "union"); + PP.EnterToken(Tok); + Tok.setKind(tok::semi); + } + } else TUK = Sema::TUK_Reference; + // If this is an elaborated type specifier, and we delayed + // diagnostics before, just merge them into the current pool. + if (shouldDelayDiagsInTag) { + diagsFromTag.done(); + if (TUK == Sema::TUK_Reference) + diagsFromTag.redelay(); + } + if (!Name && !TemplateId && (DS.getTypeSpecType() == DeclSpec::TST_error || TUK != Sema::TUK_Definition)) { if (DS.getTypeSpecType() != DeclSpec::TST_error) { @@ -1175,6 +1285,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && TUK == Sema::TUK_Declaration) { // This is an explicit instantiation of a class template. + ProhibitAttributes(attrs); + TagOrTempResult = Actions.ActOnExplicitInstantiation(getCurScope(), TemplateInfo.ExternLoc, @@ -1196,6 +1308,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } else if (TUK == Sema::TUK_Reference || (TUK == Sema::TUK_Friend && TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) { + ProhibitAttributes(attrs); TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc, TemplateId->SS, TemplateId->TemplateKWLoc, @@ -1260,6 +1373,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // // template struct Outer<int>::Inner; // + ProhibitAttributes(attrs); + TagOrTempResult = Actions.ActOnExplicitInstantiation(getCurScope(), TemplateInfo.ExternLoc, @@ -1268,6 +1383,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, NameLoc, attrs.getList()); } else if (TUK == Sema::TUK_Friend && TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) { + ProhibitAttributes(attrs); + TagOrTempResult = Actions.ActOnTemplatedFriendTag(getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, @@ -1281,6 +1398,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // FIXME: Diagnose this particular error. } + if (TUK != Sema::TUK_Declaration && TUK != Sema::TUK_Definition) + ProhibitAttributes(attrs); + bool IsDependent = false; // Don't pass down template parameter lists if this is just a tag @@ -1344,77 +1464,19 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // impossible token occurs next, we assume that the programmer forgot a ; at // the end of the declaration and recover that way. // - // This switch enumerates the valid "follow" set for definition. - if (TUK == Sema::TUK_Definition) { - bool ExpectedSemi = true; - switch (Tok.getKind()) { - default: break; - case tok::semi: // struct foo {...} ; - case tok::star: // struct foo {...} * P; - case tok::amp: // struct foo {...} & R = ... - case tok::identifier: // struct foo {...} V ; - case tok::r_paren: //(struct foo {...} ) {4} - case tok::annot_cxxscope: // struct foo {...} a:: b; - case tok::annot_typename: // struct foo {...} a ::b; - case tok::annot_template_id: // struct foo {...} a<int> ::b; - case tok::l_paren: // struct foo {...} ( x); - case tok::comma: // __builtin_offsetof(struct foo{...} , - ExpectedSemi = false; - break; - // Type qualifiers - case tok::kw_const: // struct foo {...} const x; - case tok::kw_volatile: // struct foo {...} volatile x; - case tok::kw_restrict: // struct foo {...} restrict x; - case tok::kw_inline: // struct foo {...} inline foo() {}; - // Storage-class specifiers - case tok::kw_static: // struct foo {...} static x; - case tok::kw_extern: // struct foo {...} extern x; - case tok::kw_typedef: // struct foo {...} typedef x; - case tok::kw_register: // struct foo {...} register x; - case tok::kw_auto: // struct foo {...} auto x; - case tok::kw_mutable: // struct foo {...} mutable x; - case tok::kw_constexpr: // struct foo {...} constexpr x; - // As shown above, type qualifiers and storage class specifiers absolutely - // can occur after class specifiers according to the grammar. However, - // almost no one actually writes code like this. If we see one of these, - // it is much more likely that someone missed a semi colon and the - // type/storage class specifier we're seeing is part of the *next* - // intended declaration, as in: - // - // struct foo { ... } - // typedef int X; - // - // We'd really like to emit a missing semicolon error instead of emitting - // an error on the 'int' saying that you can't have two type specifiers in - // the same declaration of X. Because of this, we look ahead past this - // token to see if it's a type specifier. If so, we know the code is - // otherwise invalid, so we can produce the expected semi error. - if (!isKnownToBeTypeSpecifier(NextToken())) - ExpectedSemi = false; - break; - - case tok::r_brace: // struct bar { struct foo {...} } - // Missing ';' at end of struct is accepted as an extension in C mode. - if (!getLangOpts().CPlusPlus) - ExpectedSemi = false; - break; - } - - // C++ [temp]p3 In a template-declaration which defines a class, no - // declarator is permitted. - if (TemplateInfo.Kind) - ExpectedSemi = true; - - if (ExpectedSemi) { - ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, - TagType == DeclSpec::TST_class ? "class" - : TagType == DeclSpec::TST_struct? "struct" : "union"); - // Push this token back into the preprocessor and change our current token - // to ';' so that the rest of the code recovers as though there were an - // ';' after the definition. - PP.EnterToken(Tok); - Tok.setKind(tok::semi); - } + // Also enforce C++ [temp]p3: + // In a template-declaration which defines a class, no declarator + // is permitted. + if (TUK == Sema::TUK_Definition && + (TemplateInfo.Kind || !isValidAfterTypeSpecifier(false))) { + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, + TagType == DeclSpec::TST_class ? "class" : + TagType == DeclSpec::TST_struct ? "struct" : "union"); + // Push this token back into the preprocessor and change our current token + // to ';' so that the rest of the code recovers as though there were an + // ';' after the definition. + PP.EnterToken(Tok); + Tok.setKind(tok::semi); } } @@ -1696,12 +1758,16 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } // Access declarations. + bool MalformedTypeSpec = false; if (!TemplateInfo.Kind && - (Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) && - !TryAnnotateCXXScopeToken() && - Tok.is(tok::annot_cxxscope)) { - bool isAccessDecl = false; - if (NextToken().is(tok::identifier)) + (Tok.is(tok::identifier) || Tok.is(tok::coloncolon))) { + if (TryAnnotateCXXScopeToken()) + MalformedTypeSpec = true; + + bool isAccessDecl; + if (Tok.isNot(tok::annot_cxxscope)) + isAccessDecl = false; + else if (NextToken().is(tok::identifier)) isAccessDecl = GetLookAheadToken(2).is(tok::semi); else isAccessDecl = NextToken().is(tok::kw_operator); @@ -1798,6 +1864,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this, TemplateDiags); DS.takeAttributesFrom(attrs); + if (MalformedTypeSpec) + DS.SetTypeSpecError(); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class, &CommonLateParsedAttrs); @@ -1915,9 +1983,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, LateParsedAttrs.clear(); // Consume the ';' - it's optional unless we have a delete or default - if (Tok.is(tok::semi)) { - ConsumeToken(); - } + if (Tok.is(tok::semi)) + ConsumeExtraSemi(AfterMemberFunctionDefinition); return; } @@ -1961,18 +2028,19 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // goes before or after the GNU attributes and __asm__. ParseOptionalCXX0XVirtSpecifierSeq(VS); - bool HasDeferredInitializer = false; + InClassInitStyle HasInClassInit = ICIS_NoInit; if ((Tok.is(tok::equal) || Tok.is(tok::l_brace)) && !HasInitializer) { if (BitfieldSize.get()) { Diag(Tok, diag::err_bitfield_member_init); SkipUntil(tok::comma, true, true); } else { HasInitializer = true; - HasDeferredInitializer = !DeclaratorInfo.isDeclarationOfFunction() && - DeclaratorInfo.getDeclSpec().getStorageClassSpec() - != DeclSpec::SCS_static && - DeclaratorInfo.getDeclSpec().getStorageClassSpec() - != DeclSpec::SCS_typedef; + if (!DeclaratorInfo.isDeclarationOfFunction() && + DeclaratorInfo.getDeclSpec().getStorageClassSpec() + != DeclSpec::SCS_static && + DeclaratorInfo.getDeclSpec().getStorageClassSpec() + != DeclSpec::SCS_typedef) + HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit; } } @@ -1990,7 +2058,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclaratorInfo, move(TemplateParams), BitfieldSize.release(), - VS, HasDeferredInitializer); + VS, HasInClassInit); if (AccessAttrs) Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs, false, true); @@ -2006,15 +2074,15 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, LateParsedAttrs.clear(); // Handle the initializer. - if (HasDeferredInitializer) { + if (HasInClassInit != ICIS_NoInit) { // The initializer was deferred; parse it and cache the tokens. Diag(Tok, getLangOpts().CPlusPlus0x ? diag::warn_cxx98_compat_nonstatic_member_init : diag::ext_nonstatic_member_init); if (DeclaratorInfo.isArrayOfUnknownBound()) { - // C++0x [dcl.array]p3: An array bound may also be omitted when the - // declarator is followed by an initializer. + // C++11 [dcl.array]p3: An array bound may also be omitted when the + // declarator is followed by an initializer. // // A brace-or-equal-initializer for a member-declarator is not an // initializer in the grammar, so this is ill-formed. @@ -2266,10 +2334,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, // Check for extraneous top-level semicolon. if (Tok.is(tok::semi)) { - Diag(Tok, diag::ext_extra_struct_semi) - << DeclSpec::getSpecifierName((DeclSpec::TST)TagType) - << FixItHint::CreateRemoval(Tok.getLocation()); - ConsumeToken(); + ConsumeExtraSemi(InsideStruct, TagType); continue; } @@ -2779,7 +2844,7 @@ IdentifierInfo *Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc) { StringRef Spelling = PP.getSpelling(Tok.getLocation(), SpellingBuf); if (std::isalpha(Spelling[0])) { Loc = ConsumeToken(); - return &PP.getIdentifierTable().get(Spelling.data()); + return &PP.getIdentifierTable().get(Spelling); } return 0; } @@ -2870,28 +2935,31 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, } bool AttrParsed = false; - // No scoped names are supported; ideally we could put all non-standard - // attributes into namespaces. - if (!ScopeName) { - switch (AttributeList::getKind(AttrName)) { - // No arguments - case AttributeList::AT_carries_dependency: - case AttributeList::AT_noreturn: { - if (Tok.is(tok::l_paren)) { - Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) - << AttrName->getName(); - break; - } - - attrs.addNew(AttrName, AttrLoc, 0, AttrLoc, 0, - SourceLocation(), 0, 0, false, true); - AttrParsed = true; + switch (AttributeList::getKind(AttrName, ScopeName, + AttributeList::AS_CXX11)) { + // No arguments + case AttributeList::AT_CarriesDependency: + // FIXME: implement generic support of attributes with C++11 syntax + // see Parse/ParseDecl.cpp: ParseGNUAttributes + case AttributeList::AT_FallThrough: + case AttributeList::AT_NoReturn: { + if (Tok.is(tok::l_paren)) { + Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) + << AttrName->getName(); break; } - // Silence warnings - default: break; - } + attrs.addNew(AttrName, + SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, + AttrLoc), + ScopeName, ScopeLoc, 0, + SourceLocation(), 0, 0, AttributeList::AS_CXX11); + AttrParsed = true; + break; + } + + // Silence warnings + default: break; } // Skip the entire parameter clause, if any @@ -2917,7 +2985,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, SkipUntil(tok::r_square, false); } -/// ParseCXX11Attributes - Parse a C++0x attribute-specifier-seq. +/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq. /// /// attribute-specifier-seq: /// attribute-specifier-seq[opt] attribute-specifier @@ -2991,10 +3059,7 @@ void Parser::ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType, // Check for extraneous top-level semicolon. if (Tok.is(tok::semi)) { - Diag(Tok, diag::ext_extra_struct_semi) - << DeclSpec::getSpecifierName((DeclSpec::TST)TagType) - << FixItHint::CreateRemoval(Tok.getLocation()); - ConsumeToken(); + ConsumeExtraSemi(InsideStruct, TagType); continue; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 6d31396cc016..8d4668b95481 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -6,17 +6,19 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This file implements the Expression parsing implementation. Expressions in -// C99 basically consist of a bunch of binary operators with unary operators and -// other random stuff at the leaves. -// -// In the C99 grammar, these unary operators bind tightest and are represented -// as the 'cast-expression' production. Everything else is either a binary -// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are -// handled by ParseCastExpression, the higher level pieces are handled by -// ParseBinaryExpression. -// +/// +/// \file +/// \brief Provides the Expression parsing implementation. +/// +/// Expressions in C99 basically consist of a bunch of binary operators with +/// unary operators and other random stuff at the leaves. +/// +/// In the C99 grammar, these unary operators bind tightest and are represented +/// as the 'cast-expression' production. Everything else is either a binary +/// operator (e.g. '/') or a ternary operator ("?:"). The unary leaves are +/// handled by ParseCastExpression, the higher level pieces are handled by +/// ParseBinaryExpression. +/// //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" @@ -30,8 +32,7 @@ #include "llvm/ADT/SmallString.h" using namespace clang; -/// getBinOpPrecedence - Return the precedence of the specified binary operator -/// token. +/// \brief Return the precedence of the specified binary operator token. static prec::Level getBinOpPrecedence(tok::TokenKind Kind, bool GreaterThanIsOperator, bool CPlusPlus0x) { @@ -92,8 +93,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, } -/// ParseExpression - Simple precedence-based parser for binary/ternary -/// operators. +/// \brief Simple precedence-based parser for binary/ternary operators. /// /// Note: we diverge from the C99 grammar when parsing the assignment-expression /// production. C99 specifies that the LHS of an assignment operator should be @@ -104,6 +104,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, /// consistency, we parse the LHS as a conditional-expression, then check for /// l-value-ness in semantic analysis stages. /// +/// \verbatim /// pm-expression: [C++ 5.5] /// cast-expression /// pm-expression '.*' cast-expression @@ -175,6 +176,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, /// expression: [C99 6.5.17] /// assignment-expression ...[opt] /// expression ',' assignment-expression ...[opt] +/// \endverbatim ExprResult Parser::ParseExpression(TypeCastState isTypeCast) { ExprResult LHS(ParseAssignmentExpression(isTypeCast)); return ParseRHSOfBinaryExpression(move(LHS), prec::Comma); @@ -182,8 +184,8 @@ ExprResult Parser::ParseExpression(TypeCastState isTypeCast) { /// This routine is called when the '@' is seen and consumed. /// Current token is an Identifier and is not a 'try'. This -/// routine is necessary to disambiguate @try-statement from, -/// for example, @encode-expression. +/// routine is necessary to disambiguate \@try-statement from, +/// for example, \@encode-expression. /// ExprResult Parser::ParseExpressionWithLeadingAt(SourceLocation AtLoc) { @@ -211,7 +213,7 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) { return ParseRHSOfBinaryExpression(move(LHS), prec::Comma); } -/// ParseAssignmentExpression - Parse an expr that doesn't include commas. +/// \brief Parse an expr that doesn't include (top-level) commas. ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression); @@ -228,11 +230,12 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) { return ParseRHSOfBinaryExpression(move(LHS), prec::Assignment); } -/// ParseAssignmentExprWithObjCMessageExprStart - Parse an assignment expression -/// where part of an objc message send has already been parsed. In this case -/// LBracLoc indicates the location of the '[' of the message send, and either -/// ReceiverName or ReceiverExpr is non-null indicating the receiver of the -/// message. +/// \brief Parse an assignment expression where part of an Objective-C message +/// send has already been parsed. +/// +/// In this case \p LBracLoc indicates the location of the '[' of the message +/// send, and either \p ReceiverName or \p ReceiverExpr is non-null indicating +/// the receiver of the message. /// /// Since this handles full assignment-expression's, it handles postfix /// expressions and other binary operators for these expressions as well. @@ -262,8 +265,8 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) { return Actions.ActOnConstantExpression(Res); } -/// ParseRHSOfBinaryExpression - Parse a binary expression that starts with -/// LHS and has a precedence of at least MinPrec. +/// \brief Parse a binary expression that starts with \p LHS and has a +/// precedence of at least \p MinPrec. ExprResult Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(), @@ -439,10 +442,11 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { } } -/// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is -/// true, parse a unary-expression. isAddressOfOperand exists because an -/// id-expression that is the operand of address-of gets special treatment -/// due to member pointers. +/// \brief Parse a cast-expression, or, if \p isUnaryExpression is true, +/// parse a unary-expression. +/// +/// \p isAddressOfOperand exists because an id-expression that is the +/// operand of address-of gets special treatment due to member pointers. /// ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, @@ -480,12 +484,15 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback { }; } -/// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is -/// true, parse a unary-expression. isAddressOfOperand exists because an -/// id-expression that is the operand of address-of gets special treatment -/// due to member pointers. NotCastExpr is set to true if the token is not the -/// start of a cast-expression, and no diagnostic is emitted in this case. +/// \brief Parse a cast-expression, or, if \pisUnaryExpression is true, parse +/// a unary-expression. /// +/// \p isAddressOfOperand exists because an id-expression that is the operand +/// of address-of gets special treatment due to member pointers. NotCastExpr +/// is set to true if the token is not the start of a cast-expression, and no +/// diagnostic is emitted in this case. +/// +/// \verbatim /// cast-expression: [C99 6.5.4] /// unary-expression /// '(' type-name ')' cast-expression @@ -500,6 +507,7 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback { /// [C++11] 'sizeof' '...' '(' identifier ')' /// [GNU] '__alignof' unary-expression /// [GNU] '__alignof' '(' type-name ')' +/// [C11] '_Alignof' '(' type-name ')' /// [C++11] 'alignof' '(' type-id ')' /// [GNU] '&&' identifier /// [C++11] 'noexcept' '(' expression ')' [C++11 5.3.7] @@ -531,9 +539,9 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback { /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' /// [OBJC] '[' objc-message-expr ']' -/// [OBJC] '@selector' '(' objc-selector-arg ')' -/// [OBJC] '@protocol' '(' identifier ')' -/// [OBJC] '@encode' '(' type-name ')' +/// [OBJC] '\@selector' '(' objc-selector-arg ')' +/// [OBJC] '\@protocol' '(' identifier ')' +/// [OBJC] '\@encode' '(' type-name ')' /// [OBJC] objc-string-literal /// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3] /// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] @@ -641,6 +649,7 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback { /// [Embarcadero] expression-trait: /// '__is_lvalue_expr' /// '__is_rvalue_expr' +/// \endverbatim /// ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, @@ -846,6 +855,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, break; case tok::kw___func__: // primary-expression: __func__ [C99 6.4.2.2] case tok::kw___FUNCTION__: // primary-expression: __FUNCTION__ [GNU] + case tok::kw_L__FUNCTION__: // primary-expression: L__FUNCTION__ [MS] case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU] Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind); ConsumeToken(); @@ -912,12 +922,15 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); return move(Res); } - case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression - // unary-expression: 'sizeof' '(' type-name ')' - case tok::kw_alignof: + case tok::kw__Alignof: // unary-expression: '_Alignof' '(' type-name ')' + if (!getLangOpts().C11) + Diag(Tok, diag::ext_c11_alignment) << Tok.getName(); + // fallthrough + case tok::kw_alignof: // unary-expression: 'alignof' '(' type-id ')' case tok::kw___alignof: // unary-expression: '__alignof' unary-expression // unary-expression: '__alignof' '(' type-name ')' - // unary-expression: 'alignof' '(' type-id ')' + case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression + // unary-expression: 'sizeof' '(' type-name ')' case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression return ParseUnaryExprOrTypeTraitExpression(); case tok::ampamp: { // unary-expression: '&&' identifier @@ -1228,9 +1241,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, return ParsePostfixExpressionSuffix(Res); } -/// ParsePostfixExpressionSuffix - Once the leading part of a postfix-expression -/// is parsed, this method parses any suffixes that apply. +/// \brief Once the leading part of a postfix-expression is parsed, this +/// method parses any suffixes that apply. /// +/// \verbatim /// postfix-expression: [C99 6.5.2] /// primary-expression /// postfix-expression '[' expression ']' @@ -1246,7 +1260,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, /// argument-expression-list: [C99 6.5.2] /// argument-expression ...[opt] /// argument-expression-list ',' assignment-expression ...[opt] -/// +/// \endverbatim ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { // Now that the primary-expression piece of the postfix-expression has been @@ -1498,11 +1512,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { /// type-id. OpTok is the operand token (typeof/sizeof/alignof). Returns the /// expression (isCastExpr == false) or the type (isCastExpr == true). /// +/// \verbatim /// unary-expression: [C99 6.5.3] /// 'sizeof' unary-expression /// 'sizeof' '(' type-name ')' /// [GNU] '__alignof' unary-expression /// [GNU] '__alignof' '(' type-name ')' +/// [C11] '_Alignof' '(' type-name ')' /// [C++0x] 'alignof' '(' type-id ')' /// /// [GNU] typeof-specifier: @@ -1513,7 +1529,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { /// [OpenCL 1.1 6.11.12] vec_step built-in function: /// vec_step ( expressions ) /// vec_step ( type-name ) -/// +/// \endverbatim ExprResult Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, bool &isCastExpr, @@ -1522,7 +1538,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, assert((OpTok.is(tok::kw_typeof) || OpTok.is(tok::kw_sizeof) || OpTok.is(tok::kw___alignof) || OpTok.is(tok::kw_alignof) || - OpTok.is(tok::kw_vec_step)) && + OpTok.is(tok::kw__Alignof) || OpTok.is(tok::kw_vec_step)) && "Not a typeof/sizeof/alignof/vec_step expression!"); ExprResult Operand; @@ -1571,17 +1587,22 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, } -/// ParseUnaryExprOrTypeTraitExpression - Parse a sizeof or alignof expression. +/// \brief Parse a sizeof or alignof expression. +/// +/// \verbatim /// unary-expression: [C99 6.5.3] /// 'sizeof' unary-expression /// 'sizeof' '(' type-name ')' /// [C++0x] 'sizeof' '...' '(' identifier ')' /// [GNU] '__alignof' unary-expression /// [GNU] '__alignof' '(' type-name ')' +/// [C11] '_Alignof' '(' type-name ')' /// [C++0x] 'alignof' '(' type-id ')' +/// \endverbatim ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { - assert((Tok.is(tok::kw_sizeof) || Tok.is(tok::kw___alignof) - || Tok.is(tok::kw_alignof) || Tok.is(tok::kw_vec_step)) && + assert((Tok.is(tok::kw_sizeof) || Tok.is(tok::kw___alignof) || + Tok.is(tok::kw_alignof) || Tok.is(tok::kw__Alignof) || + Tok.is(tok::kw_vec_step)) && "Not a sizeof/alignof/vec_step expression!"); Token OpTok = Tok; ConsumeToken(); @@ -1629,7 +1650,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { RParenLoc); } - if (OpTok.is(tok::kw_alignof)) + if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw__Alignof)) Diag(OpTok, diag::warn_cxx98_compat_alignof); EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); @@ -1643,7 +1664,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { CastRange); UnaryExprOrTypeTrait ExprKind = UETT_SizeOf; - if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw___alignof)) + if (OpTok.is(tok::kw_alignof) || OpTok.is(tok::kw___alignof) || + OpTok.is(tok::kw__Alignof)) ExprKind = UETT_AlignOf; else if (OpTok.is(tok::kw_vec_step)) ExprKind = UETT_VecStep; @@ -1667,6 +1689,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { /// ParseBuiltinPrimaryExpression /// +/// \verbatim /// primary-expression: [C99 6.5.1] /// [GNU] '__builtin_va_arg' '(' assignment-expression ',' type-name ')' /// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' @@ -1679,7 +1702,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { /// [GNU] identifier /// [GNU] offsetof-member-designator '.' identifier /// [GNU] offsetof-member-designator '[' expression ']' -/// +/// \endverbatim ExprResult Parser::ParseBuiltinPrimaryExpression() { ExprResult Res; const IdentifierInfo *BuiltinII = Tok.getIdentifierInfo(); @@ -1869,6 +1892,7 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// in ExprType. If stopIfCastExpr is true, it will only return the parsed type, /// not the parsed cast-expression. /// +/// \verbatim /// primary-expression: [C99 6.5.1] /// '(' expression ')' /// [GNU] '(' compound-statement ')' (if !ParenExprOnly) @@ -1883,12 +1907,12 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// (__bridge type-name) cast-expression /// (__bridge_transfer type-name) cast-expression /// (__bridge_retained type-name) cast-expression +/// \endverbatim ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, bool isTypeCast, ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); - GreaterThanIsOperatorScope G(GreaterThanIsOperator, true); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.consumeOpen()) return ExprError(); @@ -2102,10 +2126,11 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, /// ParseCompoundLiteralExpression - We have parsed the parenthesized type-name /// and we are at the left brace. /// +/// \verbatim /// postfix-expression: [C99 6.5.2] /// '(' type-name ')' '{' initializer-list '}' /// '(' type-name ')' '{' initializer-list ',' '}' -/// +/// \endverbatim ExprResult Parser::ParseCompoundLiteralExpression(ParsedType Ty, SourceLocation LParenLoc, @@ -2123,8 +2148,10 @@ Parser::ParseCompoundLiteralExpression(ParsedType Ty, /// form string literals, and also handles string concatenation [C99 5.1.1.2, /// translation phase #6]. /// +/// \verbatim /// primary-expression: [C99 6.5.1] /// string-literal +/// \verbatim ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) { assert(isTokenStringLiteral() && "Not a string literal!"); @@ -2145,6 +2172,7 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) { /// ParseGenericSelectionExpression - Parse a C11 generic-selection /// [C11 6.5.1.1]. /// +/// \verbatim /// generic-selection: /// _Generic ( assignment-expression , generic-assoc-list ) /// generic-assoc-list: @@ -2153,6 +2181,7 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) { /// generic-association: /// type-name : assignment-expression /// default : assignment-expression +/// \endverbatim ExprResult Parser::ParseGenericSelectionExpression() { assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected"); SourceLocation KeyLoc = ConsumeToken(); @@ -2239,6 +2268,7 @@ ExprResult Parser::ParseGenericSelectionExpression() { /// ParseExpressionList - Used for C/C++ (argument-)expression-list. /// +/// \verbatim /// argument-expression-list: /// assignment-expression /// argument-expression-list , assignment-expression @@ -2257,7 +2287,7 @@ ExprResult Parser::ParseGenericSelectionExpression() { /// [C++0x] initializer-clause: /// [C++0x] assignment-expression /// [C++0x] braced-init-list -/// +/// \endverbatim bool Parser::ParseExpressionList(SmallVectorImpl<Expr*> &Exprs, SmallVectorImpl<SourceLocation> &CommaLocs, void (Sema::*Completer)(Scope *S, @@ -2297,10 +2327,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr*> &Exprs, /// ParseBlockId - Parse a block-id, which roughly looks like int (int x). /// +/// \verbatim /// [clang] block-id: /// [clang] specifier-qualifier-list block-declarator -/// -void Parser::ParseBlockId() { +/// \endverbatim +void Parser::ParseBlockId(SourceLocation CaretLoc) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Type); return cutOffParsing(); @@ -2320,18 +2351,19 @@ void Parser::ParseBlockId() { MaybeParseGNUAttributes(DeclaratorInfo); // Inform sema that we are starting a block. - Actions.ActOnBlockArguments(DeclaratorInfo, getCurScope()); + Actions.ActOnBlockArguments(CaretLoc, DeclaratorInfo, getCurScope()); } /// ParseBlockLiteralExpression - Parse a block literal, which roughly looks /// like ^(int x){ return x+1; } /// +/// \verbatim /// block-literal: /// [clang] '^' block-args[opt] compound-statement /// [clang] '^' block-id compound-statement /// [clang] block-args: /// [clang] '(' parameter-list ')' -/// +/// \endverbatim ExprResult Parser::ParseBlockLiteralExpression() { assert(Tok.is(tok::caret) && "block literal starts with ^"); SourceLocation CaretLoc = ConsumeToken(); @@ -2377,13 +2409,13 @@ ExprResult Parser::ParseBlockLiteralExpression() { MaybeParseGNUAttributes(ParamInfo); // Inform sema that we are starting a block. - Actions.ActOnBlockArguments(ParamInfo, getCurScope()); + Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope()); } else if (!Tok.is(tok::l_brace)) { - ParseBlockId(); + ParseBlockId(CaretLoc); } else { // Otherwise, pretend we saw (void). ParsedAttributes attrs(AttrFactory); - ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, + ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, false, SourceLocation(), 0, 0, 0, true, SourceLocation(), @@ -2400,7 +2432,7 @@ ExprResult Parser::ParseBlockLiteralExpression() { MaybeParseGNUAttributes(ParamInfo); // Inform sema that we are starting a block. - Actions.ActOnBlockArguments(ParamInfo, getCurScope()); + Actions.ActOnBlockArguments(CaretLoc, ParamInfo, getCurScope()); } diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 715218448a3e..afac25793a73 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -36,7 +36,7 @@ static int SelectDigraphErrorMessage(tok::TokenKind Kind) { } // Are the two tokens adjacent in the same source file? -static bool AreTokensAdjacent(Preprocessor &PP, Token &First, Token &Second) { +bool Parser::areTokensAdjacent(const Token &First, const Token &Second) { SourceManager &SM = PP.getSourceManager(); SourceLocation FirstLoc = SM.getSpellingLoc(First.getLocation()); SourceLocation FirstEnd = FirstLoc.getLocWithOffset(First.getLength()); @@ -80,7 +80,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, return; Token SecondToken = GetLookAheadToken(2); - if (!SecondToken.is(tok::colon) || !AreTokensAdjacent(PP, Next, SecondToken)) + if (!SecondToken.is(tok::colon) || !areTokensAdjacent(Next, SecondToken)) return; TemplateTy Template; @@ -642,7 +642,13 @@ llvm::Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro){ while (Tok.isNot(tok::r_square)) { if (!first) { if (Tok.isNot(tok::comma)) { - if (Tok.is(tok::code_completion)) { + // Provide a completion for a lambda introducer here. Except + // in Objective-C, where this is Almost Surely meant to be a message + // send. In that case, fail here and let the ObjC message + // expression parser perform the completion. + if (Tok.is(tok::code_completion) && + !(getLangOpts().ObjC1 && Intro.Default == LCD_None && + !Intro.Captures.empty())) { Actions.CodeCompleteLambdaIntroducer(getCurScope(), Intro, /*AfterAmpersand=*/false); ConsumeCodeCompletionToken(); @@ -792,10 +798,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( MaybeParseCXX0XAttributes(Attr, &DeclEndLoc); // Parse trailing-return-type[opt]. - ParsedType TrailingReturnType; + TypeResult TrailingReturnType; if (Tok.is(tok::arrow)) { SourceRange Range; - TrailingReturnType = ParseTrailingReturnType(Range).get(); + TrailingReturnType = ParseTrailingReturnType(Range); if (Range.getEnd().isValid()) DeclEndLoc = Range.getEnd(); } @@ -804,7 +810,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true, /*isVariadic=*/EllipsisLoc.isValid(), - EllipsisLoc, + /*isAmbiguous=*/false, EllipsisLoc, ParamInfo.data(), ParamInfo.size(), DS.getTypeQualifiers(), /*RefQualifierIsLValueRef=*/true, @@ -838,10 +844,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( } // Parse the return type, if there is one. - ParsedType TrailingReturnType; + TypeResult TrailingReturnType; if (Tok.is(tok::arrow)) { SourceRange Range; - TrailingReturnType = ParseTrailingReturnType(Range).get(); + TrailingReturnType = ParseTrailingReturnType(Range); if (Range.getEnd().isValid()) DeclEndLoc = Range.getEnd(); } @@ -849,6 +855,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( ParsedAttributes Attr(AttrFactory); D.AddTypeInfo(DeclaratorChunk::getFunction(/*hasProto=*/true, /*isVariadic=*/false, + /*isAmbiguous=*/false, /*EllipsisLoc=*/SourceLocation(), /*Params=*/0, /*NumParams=*/0, /*TypeQuals=*/0, @@ -921,7 +928,7 @@ ExprResult Parser::ParseCXXCasts() { // diagnose error, suggest fix, and recover parsing. Token Next = NextToken(); if (Tok.is(tok::l_square) && Tok.getLength() == 2 && Next.is(tok::colon) && - AreTokensAdjacent(PP, Tok, Next)) + areTokensAdjacent(Tok, Next)) FixDigraph(*this, PP, Tok, Next, Kind, /*AtDigraph*/true); if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName)) @@ -1235,8 +1242,6 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { MultiExprArg(&InitList, 1), SourceLocation()); } else { - GreaterThanIsOperatorScope G(GreaterThanIsOperator, true); - BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -1298,7 +1303,12 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut, return true; } + ParsedAttributesWithRange attrs(AttrFactory); + MaybeParseCXX0XAttributes(attrs); + if (!isCXXConditionDeclaration()) { + ProhibitAttributes(attrs); + // Parse the expression. ExprOut = ParseExpression(); // expression DeclOut = 0; @@ -1379,39 +1389,6 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut, return false; } -/// \brief Determine whether the current token starts a C++ -/// simple-type-specifier. -bool Parser::isCXXSimpleTypeSpecifier() const { - switch (Tok.getKind()) { - case tok::annot_typename: - case tok::kw_short: - case tok::kw_long: - case tok::kw___int64: - case tok::kw___int128: - case tok::kw_signed: - case tok::kw_unsigned: - case tok::kw_void: - case tok::kw_char: - case tok::kw_int: - case tok::kw_half: - case tok::kw_float: - case tok::kw_double: - case tok::kw_wchar_t: - case tok::kw_char16_t: - case tok::kw_char32_t: - case tok::kw_bool: - case tok::kw_decltype: - case tok::kw_typeof: - case tok::kw___underlying_type: - return true; - - default: - break; - } - - return false; -} - /// ParseCXXSimpleTypeSpecifier - [C++ 7.1.5.2] Simple type specifiers. /// This should only be called when the current token is known to be part of /// simple-type-specifier. @@ -2426,10 +2403,14 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { // Array delete? bool ArrayDelete = false; if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) { - // FIXME: This could be the start of a lambda-expression. We should - // disambiguate this, but that will require arbitrary lookahead if - // the next token is '(': - // delete [](int*){ /* ... */ + // C++11 [expr.delete]p1: + // Whenever the delete keyword is followed by empty square brackets, it + // shall be interpreted as [array delete]. + // [Footnote: A lambda expression with a lambda-introducer that consists + // of empty square brackets can follow the delete keyword if + // the lambda expression is enclosed in parentheses.] + // FIXME: Produce a better diagnostic if the '[]' is unambiguously a + // lambda-introducer. ArrayDelete = true; BalancedDelimiterTracker T(*this, tok::l_square); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 789a8ae7a93a..db35a386c350 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -308,16 +308,16 @@ public: MethodImplKind(MethodImplKind) { } - Decl *invoke(FieldDeclarator &FD) { + void invoke(ParsingFieldDeclarator &FD) { if (FD.D.getIdentifier() == 0) { P.Diag(AtLoc, diag::err_objc_property_requires_field_name) << FD.D.getSourceRange(); - return 0; + return; } if (FD.BitfieldSize) { P.Diag(AtLoc, diag::err_objc_property_bitfield) << FD.D.getSourceRange(); - return 0; + return; } // Install the property declarator into interfaceDecl. @@ -344,7 +344,7 @@ public: if (!isOverridingProperty) Props.push_back(Property); - return Property; + FD.complete(Property); } }; @@ -375,9 +375,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, while (1) { // If this is a method prototype, parse it. if (Tok.is(tok::minus) || Tok.is(tok::plus)) { - Decl *methodPrototype = - ParseObjCMethodPrototype(MethodImplKind, false); - allMethods.push_back(methodPrototype); + if (Decl *methodPrototype = + ParseObjCMethodPrototype(MethodImplKind, false)) + allMethods.push_back(methodPrototype); // Consume the ';' here, since ParseObjCMethodPrototype() is re-used for // method definitions. if (ExpectAndConsumeSemi(diag::err_expected_semi_after_method_proto)) { @@ -420,7 +420,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, // erroneous r_brace would cause an infinite loop if not handled here. if (Tok.is(tok::r_brace)) break; - ParsedAttributes attrs(AttrFactory); + ParsedAttributesWithRange attrs(AttrFactory); allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs)); continue; } @@ -493,7 +493,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, OCDS, AtLoc, LParenLoc, MethodImplKind); // Parse all the comma separated declarators. - DeclSpec DS(AttrFactory); + ParsingDeclSpec DS(*this); ParseStructDeclaration(DS, Callback); ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list); @@ -894,6 +894,7 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, DeclSpec declSpec(AttrFactory); declSpec.setObjCQualifiers(&DS); ParseSpecifierQualifierList(declSpec); + declSpec.SetRangeEnd(Tok.getLocation()); Declarator declarator(declSpec, context); ParseDeclarator(declarator); @@ -965,7 +966,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, tok::TokenKind mType, tok::ObjCKeywordKind MethodImplKind, bool MethodDefinition) { - ParsingDeclRAIIObject PD(*this); + ParsingDeclRAIIObject PD(*this, ParsingDeclRAIIObject::NoParent); if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCMethodDecl(getCurScope(), mType == tok::minus, @@ -1000,8 +1001,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, if (!SelIdent && Tok.isNot(tok::colon)) { // missing selector name. Diag(Tok, diag::err_expected_selector_for_method) << SourceRange(mLoc, Tok.getLocation()); - // Skip until we get a ; or {}. - SkipUntil(tok::r_brace); + // Skip until we get a ; or @. + SkipUntil(tok::at, true /*StopAtSemi*/, true /*don't consume*/); return 0; } @@ -1105,7 +1106,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, } bool isVariadic = false; - + bool cStyleParamWarned = false; // Parse the (optional) parameter list. while (Tok.is(tok::comma)) { ConsumeToken(); @@ -1114,6 +1115,10 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ConsumeToken(); break; } + if (!cStyleParamWarned) { + Diag(Tok, diag::warn_cstyle_param); + cStyleParamWarned = true; + } DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); // Parse the declarator. @@ -1125,7 +1130,6 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ParmDecl.getIdentifierLoc(), Param, 0)); - } // FIXME: Add support for optional parameter list... @@ -1258,9 +1262,7 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl, // Check for extraneous top-level semicolon. if (Tok.is(tok::semi)) { - Diag(Tok, diag::ext_extra_ivar_semi) - << FixItHint::CreateRemoval(Tok.getLocation()); - ConsumeToken(); + ConsumeExtraSemi(InstanceVariableList); continue; } @@ -1304,7 +1306,7 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl, P(P), IDecl(IDecl), visibility(V), AllIvarDecls(AllIvarDecls) { } - Decl *invoke(FieldDeclarator &FD) { + void invoke(ParsingFieldDeclarator &FD) { P.Actions.ActOnObjCContainerStartDefinition(IDecl); // Install the declarator into the interface decl. Decl *Field @@ -1314,12 +1316,12 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl, P.Actions.ActOnObjCContainerFinishDefinition(); if (Field) AllIvarDecls.push_back(Field); - return Field; + FD.complete(Field); } } Callback(*this, interfaceDecl, visibility, AllIvarDecls); // Parse all the comma separated declarators. - DeclSpec DS(AttrFactory); + ParsingDeclSpec DS(*this); ParseStructDeclaration(DS, Callback); if (Tok.is(tok::semi)) { @@ -1348,15 +1350,15 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl, /// objc-protocol-forward-reference /// /// objc-protocol-definition: -/// @protocol identifier +/// \@protocol identifier /// objc-protocol-refs[opt] /// objc-interface-decl-list -/// @end +/// \@end /// /// objc-protocol-forward-reference: -/// @protocol identifier-list ';' +/// \@protocol identifier-list ';' /// -/// "@protocol identifier ;" should be resolved as "@protocol +/// "\@protocol identifier ;" should be resolved as "\@protocol /// identifier-list ;": objc-interface-decl-list may not start with a /// semicolon in the first alternative if objc-protocol-refs are omitted. Parser::DeclGroupPtrTy @@ -1573,10 +1575,16 @@ void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) { assert(!Finished); P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl); for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) - P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i]); + P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], + true/*Methods*/); P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd); + if (HasCFunction) + for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) + P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], + false/*c-functions*/); + /// \brief Clear and free the cached objc methods. for (LateParsedObjCMethodContainer::iterator I = LateParsedObjCMethods.begin(), @@ -1608,8 +1616,8 @@ Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) { SourceLocation classLoc = ConsumeToken(); // consume class-name; ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@compatibility_alias"); - return Actions.ActOnCompatiblityAlias(atLoc, aliasId, aliasLoc, - classId, classLoc); + return Actions.ActOnCompatibilityAlias(atLoc, aliasId, aliasLoc, + classId, classLoc); } /// property-synthesis: @@ -1913,6 +1921,43 @@ Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { AutoreleasePoolBody.take()); } +/// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them +/// for later parsing. +void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) { + LexedMethod* LM = new LexedMethod(this, MDecl); + CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM); + CachedTokens &Toks = LM->Toks; + // Begin by storing the '{' or 'try' or ':' token. + Toks.push_back(Tok); + if (Tok.is(tok::kw_try)) { + ConsumeToken(); + if (Tok.is(tok::colon)) { + Toks.push_back(Tok); + ConsumeToken(); + while (Tok.isNot(tok::l_brace)) { + ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false); + ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false); + } + } + Toks.push_back(Tok); // also store '{' + } + else if (Tok.is(tok::colon)) { + ConsumeToken(); + while (Tok.isNot(tok::l_brace)) { + ConsumeAndStoreUntil(tok::l_paren, Toks, /*StopAtSemi=*/false); + ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false); + } + Toks.push_back(Tok); // also store '{' + } + ConsumeBrace(); + // Consume everything up to (and including) the matching right brace. + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); + while (Tok.is(tok::kw_catch)) { + ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false); + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); + } +} + /// objc-method-def: objc-method-proto ';'[opt] '{' body '}' /// Decl *Parser::ParseObjCMethodDefinition() { @@ -1950,23 +1995,10 @@ Decl *Parser::ParseObjCMethodDefinition() { // Allow the rest of sema to find private method decl implementations. Actions.AddAnyMethodToGlobalPool(MDecl); - - if (CurParsedObjCImpl) { - // Consume the tokens and store them for later parsing. - LexedMethod* LM = new LexedMethod(this, MDecl); - CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM); - CachedTokens &Toks = LM->Toks; - // Begin by storing the '{' token. - Toks.push_back(Tok); - ConsumeBrace(); - // Consume everything up to (and including) the matching right brace. - ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); - - } else { - ConsumeBrace(); - SkipUntil(tok::r_brace, /*StopAtSemi=*/false); - } - + assert (CurParsedObjCImpl + && "ParseObjCMethodDefinition - Method out of @implementation"); + // Consume the tokens and store them for later parsing. + StashAwayMethodOrFunctionBodyTokens(MDecl); return MDecl; } @@ -2066,6 +2098,10 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { // Objective-C dictionary literal return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc)); + case tok::l_paren: + // Objective-C boxed expression + return ParsePostfixExpressionSuffix(ParseObjCBoxedExpr(AtLoc)); + default: if (Tok.getIdentifierInfo() == 0) return ExprError(Diag(AtLoc, diag::err_unexpected_at)); @@ -2077,8 +2113,23 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc)); case tok::objc_selector: return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); - default: - return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + default: { + const char *str = 0; + if (GetLookAheadToken(1).is(tok::l_brace)) { + char ch = Tok.getIdentifierInfo()->getNameStart()[0]; + str = + ch == 't' ? "try" + : (ch == 'f' ? "finally" + : (ch == 'a' ? "autoreleasepool" : 0)); + } + if (str) { + SourceLocation kwLoc = Tok.getLocation(); + return ExprError(Diag(AtLoc, diag::err_unexpected_at) << + FixItHint::CreateReplacement(kwLoc, str)); + } + else + return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + } } } } @@ -2112,7 +2163,7 @@ bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) { Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) TryAnnotateTypeOrScopeToken(); - if (!isCXXSimpleTypeSpecifier()) { + if (!Actions.isSimpleTypeSpecifier(Tok.getKind())) { // objc-receiver: // expression ExprResult Receiver = ParseExpression(); @@ -2449,10 +2500,14 @@ Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc, } // Parse the, optional, argument list, comma separated. while (Tok.is(tok::comma)) { - ConsumeToken(); // Eat the ','. + SourceLocation commaLoc = ConsumeToken(); // Eat the ','. /// Parse the expression after ',' ExprResult Res(ParseAssignmentExpression()); if (Res.isInvalid()) { + if (Tok.is(tok::colon)) { + Diag(commaLoc, diag::note_extra_comma_message_arg) << + FixItHint::CreateRemoval(commaLoc); + } // We must manually skip to a ']', otherwise the expression skipper will // stop at the ']' when it skips to the ';'. We want it to skip beyond // the enclosing expression. @@ -2580,6 +2635,31 @@ ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) { return Owned(Actions.BuildObjCNumericLiteral(AtLoc, Lit.take())); } +/// ParseObjCBoxedExpr - +/// objc-box-expression: +/// @( assignment-expression ) +ExprResult +Parser::ParseObjCBoxedExpr(SourceLocation AtLoc) { + if (Tok.isNot(tok::l_paren)) + return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@"); + + BalancedDelimiterTracker T(*this, tok::l_paren); + T.consumeOpen(); + ExprResult ValueExpr(ParseAssignmentExpression()); + if (T.consumeClose()) + return ExprError(); + + if (ValueExpr.isInvalid()) + return ExprError(); + + // Wrap the sub-expression in a parenthesized expression, to distinguish + // a boxed expression from a literal. + SourceLocation LPLoc = T.getOpenLocation(), RPLoc = T.getCloseLocation(); + ValueExpr = Actions.ActOnParenExpr(LPLoc, RPLoc, ValueExpr.take()); + return Owned(Actions.BuildObjCBoxedExpr(SourceRange(AtLoc, RPLoc), + ValueExpr.take())); +} + ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) { ExprVector ElementExprs(Actions); // array elements. ConsumeBracket(); // consume the l_square. @@ -2698,7 +2778,7 @@ Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) { } /// objc-protocol-expression -/// @protocol ( protocol-name ) +/// \@protocol ( protocol-name ) ExprResult Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) { SourceLocation ProtoLoc = ConsumeToken(); @@ -2713,12 +2793,13 @@ Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) { return ExprError(Diag(Tok, diag::err_expected_ident)); IdentifierInfo *protocolId = Tok.getIdentifierInfo(); - ConsumeToken(); + SourceLocation ProtoIdLoc = ConsumeToken(); T.consumeClose(); return Owned(Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc, T.getOpenLocation(), + ProtoIdLoc, T.getCloseLocation())); } @@ -2785,8 +2866,15 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) { T.getCloseLocation())); } -Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { - +void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) { + // MCDecl might be null due to error in method or c-function prototype, etc. + Decl *MCDecl = LM.D; + bool skip = MCDecl && + ((parseMethod && !Actions.isObjCMethodDecl(MCDecl)) || + (!parseMethod && Actions.isObjCMethodDecl(MCDecl))); + if (skip) + return; + // Save the current token position. SourceLocation OrigLoc = Tok.getLocation(); @@ -2796,40 +2884,32 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { LM.Toks.push_back(Tok); PP.EnterTokenStream(LM.Toks.data(), LM.Toks.size(), true, false); - // MDecl might be null due to error in method prototype, etc. - Decl *MDecl = LM.D; // Consume the previously pushed token. ConsumeAnyToken(); - assert(Tok.is(tok::l_brace) && "Inline objective-c method not starting with '{'"); - SourceLocation BraceLoc = Tok.getLocation(); - // Enter a scope for the method body. + assert((Tok.is(tok::l_brace) || Tok.is(tok::kw_try) || + Tok.is(tok::colon)) && + "Inline objective-c method not starting with '{' or 'try' or ':'"); + // Enter a scope for the method or c-fucntion body. ParseScope BodyScope(this, - Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope); - - // Tell the actions module that we have entered a method definition with the - // specified Declarator for the method. - Actions.ActOnStartOfObjCMethodDef(getCurScope(), MDecl); - - if (SkipFunctionBodies && trySkippingFunctionBody()) { - BodyScope.Exit(); - return Actions.ActOnFinishFunctionBody(MDecl, 0); - } - - StmtResult FnBody(ParseCompoundStatementBody()); + parseMethod + ? Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope + : Scope::FnScope|Scope::DeclScope); - // If the function body could not be parsed, make a bogus compoundstmt. - if (FnBody.isInvalid()) { - Sema::CompoundScopeRAII CompoundScope(Actions); - FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc, - MultiStmtArg(Actions), false); + // Tell the actions module that we have entered a method or c-function definition + // with the specified Declarator for the method/function. + if (parseMethod) + Actions.ActOnStartOfObjCMethodDef(getCurScope(), MCDecl); + else + Actions.ActOnStartOfFunctionDef(getCurScope(), MCDecl); + if (Tok.is(tok::kw_try)) + MCDecl = ParseFunctionTryBlock(MCDecl, BodyScope); + else { + if (Tok.is(tok::colon)) + ParseConstructorInitializer(MCDecl); + MCDecl = ParseFunctionStatementBody(MCDecl, BodyScope); } - - // Leave the function body scope. - BodyScope.Exit(); - - MDecl = Actions.ActOnFinishFunctionBody(MDecl, FnBody.take()); - + if (Tok.getLocation() != OrigLoc) { // Due to parsing error, we either went over the cached tokens or // there are still cached tokens left. If it's the latter case skip the @@ -2842,5 +2922,5 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { ConsumeAnyToken(); } - return MDecl; + return; } diff --git a/lib/Parse/ParsePragma.h b/lib/Parse/ParsePragma.h index ebb185ad1a98..fef6960813d8 100644 --- a/lib/Parse/ParsePragma.h +++ b/lib/Parse/ParsePragma.h @@ -30,10 +30,9 @@ public: }; class PragmaGCCVisibilityHandler : public PragmaHandler { - Sema &Actions; public: - explicit PragmaGCCVisibilityHandler(Sema &A) : PragmaHandler("visibility"), - Actions(A) {} + explicit PragmaGCCVisibilityHandler(Sema &/*A*/) + : PragmaHandler("visibility") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &FirstToken); @@ -70,11 +69,9 @@ public: }; class PragmaUnusedHandler : public PragmaHandler { - Sema &Actions; - Parser &parser; public: - PragmaUnusedHandler(Sema &A, Parser& p) - : PragmaHandler("unused"), Actions(A), parser(p) {} + PragmaUnusedHandler(Sema &/*A*/) + : PragmaHandler("unused") {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &FirstToken); @@ -102,10 +99,9 @@ public: class PragmaOpenCLExtensionHandler : public PragmaHandler { Sema &Actions; - Parser &parser; public: - PragmaOpenCLExtensionHandler(Sema &S, Parser& p) : - PragmaHandler("EXTENSION"), Actions(S), parser(p) {} + PragmaOpenCLExtensionHandler(Sema &A) : + PragmaHandler("EXTENSION"), Actions(A) {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &FirstToken); }; @@ -113,10 +109,9 @@ public: class PragmaFPContractHandler : public PragmaHandler { Sema &Actions; - Parser &parser; public: - PragmaFPContractHandler(Sema &S, Parser& p) : - PragmaHandler("FP_CONTRACT"), Actions(S), parser(p) {} + PragmaFPContractHandler(Sema &A) : + PragmaHandler("FP_CONTRACT"), Actions(A) {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &FirstToken); }; diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 44320dfcb3bf..d2e4309c3f4e 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -20,6 +20,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -771,7 +772,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { DeclsInGroup.data(), DeclsInGroup.size()); StmtResult R = Actions.ActOnDeclStmt(Res, LabelLoc, Tok.getLocation()); - ExpectAndConsume(tok::semi, diag::err_expected_semi_declaration); + ExpectAndConsumeSemi(diag::err_expected_semi_declaration); if (R.isUsable()) Stmts.push_back(R.release()); } @@ -895,6 +896,16 @@ bool Parser::ParseParenExprOrCondition(ExprResult &ExprResult, // Otherwise the condition is valid or the rparen is present. T.consumeClose(); + + // Check for extraneous ')'s to catch things like "if (foo())) {". We know + // that all callers are looking for a statement after the condition, so ")" + // isn't valid. + while (Tok.is(tok::r_paren)) { + Diag(Tok, diag::err_extraneous_rparen_in_condition) + << FixItHint::CreateRemoval(Tok.getLocation()); + ConsumeParen(); + } + return false; } @@ -938,7 +949,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (ParseParenExprOrCondition(CondExp, CondVar, IfLoc, true)) return StmtError(); - FullExprArg FullCondExp(Actions.MakeFullExpr(CondExp.get())); + FullExprArg FullCondExp(Actions.MakeFullExpr(CondExp.get(), IfLoc)); // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this @@ -1164,7 +1175,7 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { if (ParseParenExprOrCondition(Cond, CondVar, WhileLoc, true)) return StmtError(); - FullExprArg FullCond(Actions.MakeFullExpr(Cond.get())); + FullExprArg FullCond(Actions.MakeFullExpr(Cond.get(), WhileLoc)); // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this @@ -1248,6 +1259,12 @@ StmtResult Parser::ParseDoStatement() { // Parse the parenthesized condition. BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); + + // FIXME: Do not just parse the attribute contents and throw them away + ParsedAttributesWithRange attrs(AttrFactory); + MaybeParseCXX0XAttributes(attrs); + ProhibitAttributes(attrs); + ExprResult Cond = ParseExpression(); T.consumeClose(); DoScope.Exit(); @@ -1288,7 +1305,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { return StmtError(); } - bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus || getLangOpts().ObjC1; + bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus || + getLangOpts().ObjC1; // C99 6.8.5p5 - In C99, the for statement is a block. This is not // the case for C90. Start the loop scope. @@ -1336,8 +1354,12 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { return StmtError(); } + ParsedAttributesWithRange attrs(AttrFactory); + MaybeParseCXX0XAttributes(attrs); + // Parse the first part of the for specifier. if (Tok.is(tok::semi)) { // for (; + ProhibitAttributes(attrs); // no first part, eat the ';'. ConsumeToken(); } else if (isForInitDeclaration()) { // for (int X = 4; @@ -1382,6 +1404,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { Diag(Tok, diag::err_expected_semi_for); } } else { + ProhibitAttributes(attrs); Value = ParseExpression(); ForEach = isTokIdentifier_in(); @@ -1441,7 +1464,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { Second.get()); } SecondPartIsInvalid = Second.isInvalid(); - SecondPart = Actions.MakeFullExpr(Second.get()); + SecondPart = Actions.MakeFullExpr(Second.get(), ForLoc); } if (Tok.isNot(tok::semi)) { @@ -1469,6 +1492,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // statememt before parsing the body, in order to be able to deduce the type // of an auto-typed loop variable. StmtResult ForRangeStmt; + StmtResult ForEachStmt; + if (ForRange) { ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, T.getOpenLocation(), FirstPart.take(), @@ -1480,9 +1505,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // Similarly, we need to do the semantic analysis for a for-range // statement immediately in order to close over temporaries correctly. } else if (ForEach) { - if (!Collection.isInvalid()) - Collection = - Actions.ActOnObjCForCollectionOperand(ForLoc, Collection.take()); + ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc, T.getOpenLocation(), + FirstPart.take(), + Collection.take(), + T.getCloseLocation()); } // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if @@ -1512,11 +1538,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { return StmtError(); if (ForEach) - return Actions.ActOnObjCForCollectionStmt(ForLoc, T.getOpenLocation(), - FirstPart.take(), - Collection.take(), - T.getCloseLocation(), - Body.take()); + return Actions.FinishObjCForCollectionStmt(ForEachStmt.take(), + Body.take()); if (ForRange) return Actions.FinishCXXForRangeStmt(ForRangeStmt.take(), Body.take()); @@ -1617,9 +1640,24 @@ StmtResult Parser::ParseReturnStatement() { /// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled, /// this routine is called to collect the tokens for an MS asm statement. +/// +/// [MS] ms-asm-statement: +/// ms-asm-block +/// ms-asm-block ms-asm-statement +/// +/// [MS] ms-asm-block: +/// '__asm' ms-asm-line '\n' +/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt] +/// +/// [MS] ms-asm-instruction-block +/// ms-asm-line +/// ms-asm-line '\n' ms-asm-instruction-block +/// StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { SourceManager &SrcMgr = PP.getSourceManager(); SourceLocation EndLoc = AsmLoc; + SmallVector<Token, 4> AsmToks; + SmallVector<unsigned, 4> LineEnds; do { bool InBraces = false; unsigned short savedBraceCount = 0; @@ -1648,8 +1686,10 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { // If we hit EOF, we're done, period. if (Tok.is(tok::eof)) break; - // When we consume the closing brace, we're done. - if (InBraces && BraceCount == savedBraceCount) + + // The asm keyword is a statement separator, so multiple asm statements + // are allowed. + if (!InAsmComment && Tok.is(tok::kw_asm)) break; if (!InAsmComment && Tok.is(tok::semi)) { @@ -1681,18 +1721,28 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { break; } } + if (!InAsmComment && InBraces && Tok.is(tok::r_brace) && + BraceCount == (savedBraceCount + 1)) { + // Consume the closing brace, and finish + EndLoc = ConsumeBrace(); + break; + } // Consume the next token; make sure we don't modify the brace count etc. // if we are in a comment. EndLoc = TokLoc; if (InAsmComment) PP.Lex(Tok); - else + else { + AsmToks.push_back(Tok); ConsumeAnyToken(); + } TokLoc = Tok.getLocation(); ++NumTokensRead; } while (1); + LineEnds.push_back(AsmToks.size()); + if (InBraces && BraceCount != savedBraceCount) { // __asm without closing brace (this can happen at EOF). Diag(Tok, diag::err_expected_rbrace); @@ -1709,24 +1759,10 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { break; EndLoc = ConsumeToken(); } while (1); - // FIXME: Need to actually grab the data and pass it on to Sema. Ideally, - // what Sema wants is a string of the entire inline asm, with one instruction - // per line and all the __asm keywords stripped out, and a way of mapping - // from any character of that string to its location in the original source - // code. I'm not entirely sure how to go about that, though. - Token t; - t.setKind(tok::string_literal); - t.setLiteralData("\"/*FIXME: not done*/\""); - t.clearFlag(Token::NeedsCleaning); - t.setLength(21); - ExprResult AsmString(Actions.ActOnStringLiteral(&t, 1)); - ExprVector Constraints(Actions); - ExprVector Exprs(Actions); - ExprVector Clobbers(Actions); - return Actions.ActOnAsmStmt(AsmLoc, true, true, 0, 0, 0, - move_arg(Constraints), move_arg(Exprs), - AsmString.take(), move_arg(Clobbers), - EndLoc, true); + + // FIXME: We should be passing source locations for better diagnostics. + return Actions.ActOnMSAsmStmt(AsmLoc, llvm::makeArrayRef(AsmToks), + llvm::makeArrayRef(LineEnds), EndLoc); } /// ParseAsmStatement - Parse a GNU extended asm statement. @@ -1748,23 +1784,12 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { /// asm-string-literal /// asm-clobbers ',' asm-string-literal /// -/// [MS] ms-asm-statement: -/// ms-asm-block -/// ms-asm-block ms-asm-statement -/// -/// [MS] ms-asm-block: -/// '__asm' ms-asm-line '\n' -/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt] -/// -/// [MS] ms-asm-instruction-block -/// ms-asm-line -/// ms-asm-line '\n' ms-asm-instruction-block -/// StmtResult Parser::ParseAsmStatement(bool &msAsm) { assert(Tok.is(tok::kw_asm) && "Not an asm stmt"); SourceLocation AsmLoc = ConsumeToken(); - if (getLangOpts().MicrosoftExt && Tok.isNot(tok::l_paren) && !isTypeQualifier()) { + if (getLangOpts().MicrosoftExt && Tok.isNot(tok::l_paren) && + !isTypeQualifier()) { msAsm = true; return ParseMicrosoftAsmStatement(AsmLoc); } @@ -2067,7 +2092,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) { return move(TryBlock); // Borland allows SEH-handlers with 'try' - + if ((Tok.is(tok::identifier) && Tok.getIdentifierInfo() == getSEHExceptKeyword()) || Tok.is(tok::kw___finally)) { @@ -2107,7 +2132,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) { if (Handlers.empty()) return StmtError(); - return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.take(), move_arg(Handlers)); + return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.take(),move_arg(Handlers)); } } @@ -2203,10 +2228,10 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) { case IEB_Parse: // Parse the statements below. break; - + case IEB_Dependent: llvm_unreachable("Dependent case handled above"); - + case IEB_Skip: Braces.skipToEnd(); return; diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 5c3e2ba589ff..ade918fb221a 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -90,7 +90,8 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context, // Tell the action that names should be checked in the context of // the declaration to come. - ParsingDeclRAIIObject ParsingTemplateParams(*this); + ParsingDeclRAIIObject + ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent); // Parse multiple levels of template headers within this template // parameter scope, e.g., @@ -213,11 +214,15 @@ Parser::ParseSingleDeclarationAfterTemplate( return ParseUsingDirectiveOrDeclaration(Context, TemplateInfo, DeclEnd, prefixAttrs); - // Parse the declaration specifiers, stealing the accumulated - // diagnostics from the template parameters. + // Parse the declaration specifiers, stealing any diagnostics from + // the template parameters. ParsingDeclSpec DS(*this, &DiagsFromTParams); - DS.takeAttributesFrom(prefixAttrs); + // Move the attributes from the prefix into the DS. + if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) + ProhibitAttributes(prefixAttrs); + else + DS.takeAttributesFrom(prefixAttrs); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, getDeclSpecContextFromDeclaratorContext(Context)); @@ -259,7 +264,7 @@ Parser::ParseSingleDeclarationAfterTemplate( } // Eat the semi colon after the declaration. - ExpectAndConsume(tok::semi, diag::err_expected_semi_declaration); + ExpectAndConsumeSemi(diag::err_expected_semi_declaration); if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, ThisDecl, true, false); DeclaratorInfo.complete(ThisDecl); @@ -314,6 +319,11 @@ bool Parser::ParseTemplateParameters(unsigned Depth, Failed = ParseTemplateParameterList(Depth, TemplateParams); if (Tok.is(tok::greatergreater)) { + // No diagnostic required here: a template-parameter-list can only be + // followed by a declaration or, for a template template parameter, the + // 'class' keyword. Therefore, the second '>' will be diagnosed later. + // This matters for elegant diagnosis of: + // template<template<typename>> struct S; Tok.setKind(tok::greater); RAngleLoc = Tok.getLocation(); Tok.setLocation(Tok.getLocation().getLocWithOffset(1)); @@ -711,34 +721,104 @@ Parser::ParseTemplateIdAfterTemplateName(TemplateTy Template, } } - if (Tok.isNot(tok::greater) && Tok.isNot(tok::greatergreater)) { + // What will be left once we've consumed the '>'. + tok::TokenKind RemainingToken; + const char *ReplacementStr = "> >"; + + switch (Tok.getKind()) { + default: Diag(Tok.getLocation(), diag::err_expected_greater); return true; - } - // Determine the location of the '>' or '>>'. Only consume this - // token if the caller asked us to. - RAngleLoc = Tok.getLocation(); + case tok::greater: + // Determine the location of the '>' token. Only consume this token + // if the caller asked us to. + RAngleLoc = Tok.getLocation(); + if (ConsumeLastToken) + ConsumeToken(); + return false; - if (Tok.is(tok::greatergreater)) { - const char *ReplaceStr = "> >"; - if (NextToken().is(tok::greater) || NextToken().is(tok::greatergreater)) - ReplaceStr = "> > "; + case tok::greatergreater: + RemainingToken = tok::greater; + break; - Diag(Tok.getLocation(), getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_two_right_angle_brackets : - diag::err_two_right_angle_brackets_need_space) - << FixItHint::CreateReplacement(SourceRange(Tok.getLocation()), - ReplaceStr); + case tok::greatergreatergreater: + RemainingToken = tok::greatergreater; + break; - Tok.setKind(tok::greater); - if (!ConsumeLastToken) { - // Since we're not supposed to consume the '>>' token, we need - // to insert a second '>' token after the first. - PP.EnterToken(Tok); - } - } else if (ConsumeLastToken) + case tok::greaterequal: + RemainingToken = tok::equal; + ReplacementStr = "> ="; + break; + + case tok::greatergreaterequal: + RemainingToken = tok::greaterequal; + break; + } + + // This template-id is terminated by a token which starts with a '>'. Outside + // C++11, this is now error recovery, and in C++11, this is error recovery if + // the token isn't '>>'. + + RAngleLoc = Tok.getLocation(); + + // The source range of the '>>' or '>=' at the start of the token. + CharSourceRange ReplacementRange = + CharSourceRange::getCharRange(RAngleLoc, + Lexer::AdvanceToTokenCharacter(RAngleLoc, 2, PP.getSourceManager(), + getLangOpts())); + + // A hint to put a space between the '>>'s. In order to make the hint as + // clear as possible, we include the characters either side of the space in + // the replacement, rather than just inserting a space at SecondCharLoc. + FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange, + ReplacementStr); + + // A hint to put another space after the token, if it would otherwise be + // lexed differently. + FixItHint Hint2; + Token Next = NextToken(); + if ((RemainingToken == tok::greater || + RemainingToken == tok::greatergreater) && + (Next.is(tok::greater) || Next.is(tok::greatergreater) || + Next.is(tok::greatergreatergreater) || Next.is(tok::equal) || + Next.is(tok::greaterequal) || Next.is(tok::greatergreaterequal) || + Next.is(tok::equalequal)) && + areTokensAdjacent(Tok, Next)) + Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " "); + + unsigned DiagId = diag::err_two_right_angle_brackets_need_space; + if (getLangOpts().CPlusPlus0x && Tok.is(tok::greatergreater)) + DiagId = diag::warn_cxx98_compat_two_right_angle_brackets; + else if (Tok.is(tok::greaterequal)) + DiagId = diag::err_right_angle_bracket_equal_needs_space; + Diag(Tok.getLocation(), DiagId) << Hint1 << Hint2; + + // Strip the initial '>' from the token. + if (RemainingToken == tok::equal && Next.is(tok::equal) && + areTokensAdjacent(Tok, Next)) { + // Join two adjacent '=' tokens into one, for cases like: + // void (*p)() = f<int>; + // return f<int>==p; ConsumeToken(); + Tok.setKind(tok::equalequal); + Tok.setLength(Tok.getLength() + 1); + } else { + Tok.setKind(RemainingToken); + Tok.setLength(Tok.getLength() - 1); + } + Tok.setLocation(Lexer::AdvanceToTokenCharacter(RAngleLoc, 1, + PP.getSourceManager(), + getLangOpts())); + + if (!ConsumeLastToken) { + // Since we're not supposed to consume the '>' token, we need to push + // this token and revert the current token back to the '>'. + PP.EnterToken(Tok); + Tok.setKind(tok::greater); + Tok.setLength(1); + Tok.setLocation(RAngleLoc); + } return false; } @@ -1132,7 +1212,8 @@ Decl *Parser::ParseExplicitInstantiation(unsigned Context, SourceLocation &DeclEnd, AccessSpecifier AS) { // This isn't really required here. - ParsingDeclRAIIObject ParsingTemplateParams(*this); + ParsingDeclRAIIObject + ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent); return ParseSingleDeclarationAfterTemplate(Context, ParsedTemplateInfo(ExternLoc, diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 28c5e8b67370..1a4df4791b5f 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -671,7 +671,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, // initializer that follows the declarator. Note that ctor-style // initializers are not possible in contexts where abstract declarators // are allowed. - if (!mayBeAbstract && !isCXXFunctionDeclarator(false/*warnIfAmbiguous*/)) + if (!mayBeAbstract && !isCXXFunctionDeclarator()) break; // direct-declarator '(' parameter-declaration-clause ')' @@ -735,6 +735,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw_alignof: case tok::kw_noexcept: case tok::kw_nullptr: + case tok::kw__Alignof: case tok::kw___null: case tok::kw___alignof: case tok::kw___builtin_choose_expr: @@ -744,6 +745,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw___imag: case tok::kw___real: case tok::kw___FUNCTION__: + case tok::kw_L__FUNCTION__: case tok::kw___PRETTY_FUNCTION__: case tok::kw___has_nothrow_assign: case tok::kw___has_nothrow_copy: @@ -827,6 +829,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// be either a decl-specifier or a function-style cast, and TPResult::Error() /// if a parsing error was found and reported. /// +/// If HasMissingTypename is provided, a name with a dependent scope specifier +/// will be treated as ambiguous if the 'typename' keyword is missing. If this +/// happens, *HasMissingTypename will be set to 'true'. +/// /// decl-specifier: /// storage-class-specifier /// type-specifier @@ -918,7 +924,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// [GNU] restrict /// Parser::TPResult -Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { +Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, + bool *HasMissingTypename) { switch (Tok.getKind()) { case tok::identifier: // foo::bar // Check for need to substitute AltiVec __vector keyword @@ -931,9 +938,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error(); - if (Tok.is(tok::identifier)) - return TPResult::False(); - return isCXXDeclarationSpecifier(BracedCastResult); + if (Tok.is(tok::identifier)) { + const Token &Next = NextToken(); + return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ? + TPResult::True() : TPResult::False(); + } + return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); @@ -947,7 +957,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error(); - return isCXXDeclarationSpecifier(BracedCastResult); + return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); // decl-specifier: // storage-class-specifier @@ -1049,12 +1059,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { bool isIdentifier = Tok.is(tok::identifier); TPResult TPR = TPResult::False(); if (!isIdentifier) - TPR = isCXXDeclarationSpecifier(BracedCastResult); + TPR = isCXXDeclarationSpecifier(BracedCastResult, + HasMissingTypename); PA.Revert(); if (isIdentifier || TPR == TPResult::True() || TPR == TPResult::Error()) return TPResult::Error(); + + if (HasMissingTypename) { + // We can't tell whether this is a missing 'typename' or a valid + // expression. + *HasMissingTypename = true; + return TPResult::Ambiguous(); + } } } return TPResult::False(); @@ -1218,21 +1236,24 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() { return TPResult::Error(); } -Parser::TPResult Parser::TryParseDeclarationSpecifier() { - TPResult TPR = isCXXDeclarationSpecifier(); +Parser::TPResult +Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) { + TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(), + HasMissingTypename); if (TPR != TPResult::Ambiguous()) return TPR; if (Tok.is(tok::kw_typeof)) TryParseTypeofSpecifier(); else { + if (Tok.is(tok::annot_cxxscope)) + ConsumeToken(); ConsumeToken(); if (getLangOpts().ObjC1 && Tok.is(tok::less)) TryParseProtocolQualifiers(); } - assert(Tok.is(tok::l_paren) && "Expected '('!"); return TPResult::Ambiguous(); } @@ -1246,7 +1267,7 @@ Parser::TPResult Parser::TryParseDeclarationSpecifier() { /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// -bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { +bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { // C++ 8.2p1: // The ambiguity arising from the similarity between a function-style cast and @@ -1260,27 +1281,36 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { TentativeParsingAction PA(*this); ConsumeParen(); - TPResult TPR = TryParseParameterDeclarationClause(); - if (TPR == TPResult::Ambiguous() && Tok.isNot(tok::r_paren)) - TPR = TPResult::False(); + bool InvalidAsDeclaration = false; + TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration); + if (TPR == TPResult::Ambiguous()) { + if (Tok.isNot(tok::r_paren)) + TPR = TPResult::False(); + else { + const Token &Next = NextToken(); + if (Next.is(tok::amp) || Next.is(tok::ampamp) || + Next.is(tok::kw_const) || Next.is(tok::kw_volatile) || + Next.is(tok::kw_throw) || Next.is(tok::kw_noexcept) || + Next.is(tok::l_square) || isCXX0XVirtSpecifier(Next) || + Next.is(tok::l_brace) || Next.is(tok::kw_try) || + Next.is(tok::equal) || Next.is(tok::arrow)) + // The next token cannot appear after a constructor-style initializer, + // and can appear next in a function definition. This must be a function + // declarator. + TPR = TPResult::True(); + else if (InvalidAsDeclaration) + // Use the absence of 'typename' as a tie-breaker. + TPR = TPResult::False(); + } + } - SourceLocation TPLoc = Tok.getLocation(); PA.Revert(); - // In case of an error, let the declaration parsing code handle it. - if (TPR == TPResult::Error()) - return true; + if (IsAmbiguous && TPR == TPResult::Ambiguous()) + *IsAmbiguous = true; - if (TPR == TPResult::Ambiguous()) { - // Function declarator has precedence over constructor-style initializer. - // Emit a warning just in case the author intended a variable definition. - if (warnIfAmbiguous) - Diag(Tok, diag::warn_parens_disambiguated_as_function_decl) - << SourceRange(Tok.getLocation(), TPLoc); - return true; - } - - return TPR == TPResult::True(); + // In case of an error, let the declaration parsing code handle it. + return TPR != TPResult::False(); } /// parameter-declaration-clause: @@ -1300,10 +1330,11 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] '=' assignment-expression /// -Parser::TPResult Parser::TryParseParameterDeclarationClause() { +Parser::TPResult +Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { if (Tok.is(tok::r_paren)) - return TPResult::True(); + return TPResult::Ambiguous(); // parameter-declaration-list[opt] '...'[opt] // parameter-declaration-list ',' '...' @@ -1333,7 +1364,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() { // decl-specifier-seq // A parameter-declaration's initializer must be preceded by an '=', so // decl-specifier-seq '{' is not a parameter in C++11. - TPResult TPR = TryParseDeclarationSpecifier(); + TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration); if (TPR != TPResult::Ambiguous()) return TPR; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index f1b99fb0a129..3725e2b84550 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -23,6 +23,22 @@ #include "clang/AST/ASTConsumer.h" using namespace clang; +namespace { +/// \brief A comment handler that passes comments found by the preprocessor +/// to the parser action. +class ActionCommentHandler : public CommentHandler { + Sema &S; + +public: + explicit ActionCommentHandler(Sema &S) : S(S) { } + + virtual bool HandleComment(Preprocessor &PP, SourceRange Comment) { + S.ActOnComment(Comment); + return false; + } +}; +} // end anonymous namespace + IdentifierInfo *Parser::getSEHExceptKeyword() { // __except is accepted as a (contextual) keyword if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland)) @@ -35,7 +51,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies) : PP(pp), Actions(actions), Diags(PP.getDiagnostics()), GreaterThanIsOperator(true), ColonIsSacred(false), InMessageExpression(false), TemplateParameterDepth(0), - SkipFunctionBodies(SkipFunctionBodies) { + ParsingInObjCContainer(false), SkipFunctionBodies(SkipFunctionBodies) { Tok.setKind(tok::eof); Actions.CurScope = 0; NumCachedScopes = 0; @@ -59,7 +75,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies) MSStructHandler.reset(new PragmaMSStructHandler(actions)); PP.AddPragmaHandler(MSStructHandler.get()); - UnusedHandler.reset(new PragmaUnusedHandler(actions, *this)); + UnusedHandler.reset(new PragmaUnusedHandler(actions)); PP.AddPragmaHandler(UnusedHandler.get()); WeakHandler.reset(new PragmaWeakHandler(actions)); @@ -68,17 +84,19 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies) RedefineExtnameHandler.reset(new PragmaRedefineExtnameHandler(actions)); PP.AddPragmaHandler(RedefineExtnameHandler.get()); - FPContractHandler.reset(new PragmaFPContractHandler(actions, *this)); + FPContractHandler.reset(new PragmaFPContractHandler(actions)); PP.AddPragmaHandler("STDC", FPContractHandler.get()); if (getLangOpts().OpenCL) { - OpenCLExtensionHandler.reset( - new PragmaOpenCLExtensionHandler(actions, *this)); + OpenCLExtensionHandler.reset(new PragmaOpenCLExtensionHandler(actions)); PP.AddPragmaHandler("OPENCL", OpenCLExtensionHandler.get()); PP.AddPragmaHandler("OPENCL", FPContractHandler.get()); } - + + CommentSemaHandler.reset(new ActionCommentHandler(actions)); + PP.addCommentHandler(CommentSemaHandler.get()); + PP.setCodeCompletionHandler(*this); } @@ -185,7 +203,7 @@ bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID, bool Parser::ExpectAndConsumeSemi(unsigned DiagID) { if (Tok.is(tok::semi) || Tok.is(tok::code_completion)) { - ConsumeAnyToken(); + ConsumeToken(); return false; } @@ -202,6 +220,42 @@ bool Parser::ExpectAndConsumeSemi(unsigned DiagID) { return ExpectAndConsume(tok::semi, DiagID); } +void Parser::ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST) { + if (!Tok.is(tok::semi)) return; + + bool HadMultipleSemis = false; + SourceLocation StartLoc = Tok.getLocation(); + SourceLocation EndLoc = Tok.getLocation(); + ConsumeToken(); + + while ((Tok.is(tok::semi) && !Tok.isAtStartOfLine())) { + HadMultipleSemis = true; + EndLoc = Tok.getLocation(); + ConsumeToken(); + } + + // C++11 allows extra semicolons at namespace scope, but not in any of the + // other contexts. + if (Kind == OutsideFunction && getLangOpts().CPlusPlus) { + if (getLangOpts().CPlusPlus0x) + Diag(StartLoc, diag::warn_cxx98_compat_top_level_semi) + << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc)); + else + Diag(StartLoc, diag::ext_extra_semi_cxx11) + << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc)); + return; + } + + if (Kind != AfterMemberFunctionDefinition || HadMultipleSemis) + Diag(StartLoc, diag::ext_extra_semi) + << Kind << DeclSpec::getSpecifierName((DeclSpec::TST)TST) + << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc)); + else + // A single semicolon is valid after a member function definition. + Diag(StartLoc, diag::warn_extra_semi_after_mem_fn_def) + << FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc)); +} + //===----------------------------------------------------------------------===// // Error recovery. //===----------------------------------------------------------------------===// @@ -396,6 +450,9 @@ Parser::~Parser() { PP.RemovePragmaHandler("STDC", FPContractHandler.get()); FPContractHandler.reset(); + + PP.removeCommentHandler(CommentSemaHandler.get()); + PP.clearCodeCompletionHandler(); assert(TemplateIds.empty() && "Still alive TemplateIdAnnotations around?"); @@ -412,10 +469,6 @@ void Parser::Initialize() { // Prime the lexer look-ahead. ConsumeToken(); - if (Tok.is(tok::eof) && - !getLangOpts().CPlusPlus) // Empty source file is an extension in C - Diag(Tok, diag::ext_empty_source_file); - // Initialization for Objective-C context sensitive keywords recognition. // Referenced in Parser::ParseObjCTypeQualifierList. if (getLangOpts().ObjC1) { @@ -582,11 +635,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, HandlePragmaPack(); return DeclGroupPtrTy(); case tok::semi: - Diag(Tok, getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_top_level_semi : diag::ext_top_level_semi) - << FixItHint::CreateRemoval(Tok.getLocation()); - - ConsumeToken(); + ConsumeExtraSemi(OutsideFunction); // TODO: Invoke action for top-level semicolon. return DeclGroupPtrTy(); case tok::r_brace: @@ -641,7 +690,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, case tok::kw_export: // As in 'export template' case tok::kw_static_assert: case tok::kw__Static_assert: - // A function definition cannot start with a these keywords. + // A function definition cannot start with any of these keywords. { SourceLocation DeclEnd; StmtVector Stmts(Actions); @@ -708,8 +757,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, dont_know: // We can't tell whether this is a function-definition or declaration yet. if (DS) { - DS->takeAttributesFrom(attrs); - return ParseDeclarationOrFunctionDefinition(*DS); + return ParseDeclarationOrFunctionDefinition(attrs, DS); } else { return ParseDeclarationOrFunctionDefinition(attrs); } @@ -729,7 +777,7 @@ bool Parser::isDeclarationAfterDeclarator() { if (KW.is(tok::kw_default) || KW.is(tok::kw_delete)) return false; } - + return Tok.is(tok::equal) || // int X()= -> not a function def Tok.is(tok::comma) || // int X(), -> not a function def Tok.is(tok::semi) || // int X(); -> not a function def @@ -777,20 +825,24 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) { /// [OMP] threadprivate-directive [TODO] /// Parser::DeclGroupPtrTy -Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS, - AccessSpecifier AS) { +Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs, + ParsingDeclSpec &DS, + AccessSpecifier AS) { // Parse the common declaration-specifiers piece. ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level); // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.is(tok::semi)) { + ProhibitAttributes(attrs); ConsumeToken(); Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS, DS); DS.complete(TheDecl); return Actions.ConvertDeclToDeclGroup(TheDecl); } + DS.takeAttributesFrom(attrs); + // ObjC2 allows prefix attributes on class interfaces and protocols. // FIXME: This still needs better diagnostics. We should only accept // attributes here, no types, etc. @@ -831,16 +883,20 @@ Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS, } Parser::DeclGroupPtrTy -Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs, +Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributesWithRange &attrs, + ParsingDeclSpec *DS, AccessSpecifier AS) { - ParsingDeclSpec DS(*this); - DS.takeAttributesFrom(attrs); - // Must temporarily exit the objective-c container scope for - // parsing c constructs and re-enter objc container scope - // afterwards. - ObjCDeclContextSwitch ObjCDC(*this); - - return ParseDeclarationOrFunctionDefinition(DS, AS); + if (DS) { + return ParseDeclOrFunctionDefInternal(attrs, *DS, AS); + } else { + ParsingDeclSpec PDS(*this); + // Must temporarily exit the objective-c container scope for + // parsing c constructs and re-enter objc container scope + // afterwards. + ObjCDeclContextSwitch ObjCDC(*this); + + return ParseDeclOrFunctionDefInternal(attrs, PDS, AS); + } } /// ParseFunctionDefinition - We parsed and verified that the specified @@ -914,6 +970,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // In delayed template parsing mode, for function template we consume the // tokens and store them for late parsing at the end of the translation unit. if (getLangOpts().DelayedTemplateParsing && + Tok.isNot(tok::equal) && TemplateInfo.Kind == ParsedTemplateInfo::Template) { MultiTemplateParamsArg TemplateParameterLists(Actions, TemplateInfo.TemplateParams->data(), @@ -947,7 +1004,28 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, } return DP; } - + else if (CurParsedObjCImpl && + !TemplateInfo.TemplateParams && + (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) || + Tok.is(tok::colon)) && + Actions.CurContext->isTranslationUnit()) { + MultiTemplateParamsArg TemplateParameterLists(Actions, 0, 0); + ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope); + Scope *ParentScope = getCurScope()->getParent(); + + D.setFunctionDefinitionKind(FDK_Definition); + Decl *FuncDecl = Actions.HandleDeclarator(ParentScope, D, + move(TemplateParameterLists)); + D.complete(FuncDecl); + D.getMutableDeclSpec().abort(); + if (FuncDecl) { + // Consume the tokens and store them for later parsing. + StashAwayMethodOrFunctionBodyTokens(FuncDecl); + CurParsedObjCImpl->HasCFunction = true; + return FuncDecl; + } + } + // Enter a scope for the function body. ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope); @@ -1130,10 +1208,7 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { ParseDeclarator(ParmDeclarator); } - if (Tok.is(tok::semi)) { - ConsumeToken(); - } else { - Diag(Tok, diag::err_expected_semi_declaration); + if (ExpectAndConsumeSemi(diag::err_expected_semi_declaration)) { // Skip to end of block or statement SkipUntil(tok::semi, true); if (Tok.is(tok::semi)) @@ -1251,7 +1326,8 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) { bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) { assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) - || Tok.is(tok::kw_decltype)) && "Cannot be a type or scope token!"); + || Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id)) + && "Cannot be a type or scope token!"); if (Tok.is(tok::kw_typename)) { // Parse a C++ typename-specifier, e.g., "typename T::type". @@ -1267,10 +1343,23 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) { 0, /*IsTypename*/true)) return true; if (!SS.isSet()) { - if (getLangOpts().MicrosoftExt) - Diag(Tok.getLocation(), diag::warn_expected_qualified_after_typename); - else - Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); + if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) || + Tok.is(tok::annot_decltype)) { + // Attempt to recover by skipping the invalid 'typename' + if (Tok.is(tok::annot_decltype) || + (!TryAnnotateTypeOrScopeToken(EnteringContext, NeedType) && + Tok.isAnnotation())) { + unsigned DiagID = diag::err_expected_qualified_after_typename; + // MS compatibility: MSVC permits using known types with typename. + // e.g. "typedef typename T* pointer_type" + if (getLangOpts().MicrosoftExt) + DiagID = diag::warn_expected_qualified_after_typename; + Diag(Tok.getLocation(), DiagID); + return false; + } + } + + Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); return true; } @@ -1423,8 +1512,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) { /// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only /// annotates C++ scope specifiers and template-ids. This returns -/// true if the token was annotated or there was an error that could not be -/// recovered from. +/// true if there was an error that could not be recovered from. /// /// Note that this routine emits an error if you call it with ::new or ::delete /// as the current tokens, so only call it in contexts where these are invalid. @@ -1678,13 +1766,13 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { return Actions.ConvertDeclToDeclGroup(Import.get()); } -bool Parser::BalancedDelimiterTracker::diagnoseOverflow() { +bool BalancedDelimiterTracker::diagnoseOverflow() { P.Diag(P.Tok, diag::err_parser_impl_limit_overflow); P.SkipUntil(tok::eof); return true; } -bool Parser::BalancedDelimiterTracker::expectAndConsume(unsigned DiagID, +bool BalancedDelimiterTracker::expectAndConsume(unsigned DiagID, const char *Msg, tok::TokenKind SkipToToc ) { LOpen = P.Tok.getLocation(); @@ -1697,7 +1785,7 @@ bool Parser::BalancedDelimiterTracker::expectAndConsume(unsigned DiagID, return diagnoseOverflow(); } -bool Parser::BalancedDelimiterTracker::diagnoseMissingClose() { +bool BalancedDelimiterTracker::diagnoseMissingClose() { assert(!P.Tok.is(Close) && "Should have consumed closing delimiter"); const char *LHSName = "unknown"; @@ -1715,6 +1803,6 @@ bool Parser::BalancedDelimiterTracker::diagnoseMissingClose() { return true; } -void Parser::BalancedDelimiterTracker::skipToEnd() { +void BalancedDelimiterTracker::skipToEnd() { P.SkipUntil(Close, false); } diff --git a/lib/Parse/RAIIObjectsForParser.h b/lib/Parse/RAIIObjectsForParser.h index ef17aee3f512..455c4af2ffae 100644 --- a/lib/Parse/RAIIObjectsForParser.h +++ b/lib/Parse/RAIIObjectsForParser.h @@ -16,13 +16,230 @@ #define LLVM_CLANG_PARSE_RAII_OBJECTS_FOR_PARSER_H #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/Sema.h" namespace clang { - // TODO: move ParsingDeclRAIIObject here. // TODO: move ParsingClassDefinition here. // TODO: move TentativeParsingAction here. - - + + /// \brief A RAII object used to temporarily suppress access-like + /// checking. Access-like checks are those associated with + /// controlling the use of a declaration, like C++ access control + /// errors and deprecation warnings. They are contextually + /// dependent, in that they can only be resolved with full + /// information about what's being declared. They are also + /// suppressed in certain contexts, like the template arguments of + /// an explicit instantiation. However, those suppression contexts + /// cannot necessarily be fully determined in advance; for + /// example, something starting like this: + /// template <> class std::vector<A::PrivateType> + /// might be the entirety of an explicit instantiation: + /// template <> class std::vector<A::PrivateType>; + /// or just an elaborated type specifier: + /// template <> class std::vector<A::PrivateType> make_vector<>(); + /// Therefore this class collects all the diagnostics and permits + /// them to be re-delayed in a new context. + class SuppressAccessChecks { + Sema &S; + sema::DelayedDiagnosticPool DiagnosticPool; + Sema::ParsingDeclState State; + bool Active; + + public: + /// Begin suppressing access-like checks + SuppressAccessChecks(Parser &P, bool activate = true) + : S(P.getActions()), DiagnosticPool(NULL) { + if (activate) { + State = S.PushParsingDeclaration(DiagnosticPool); + Active = true; + } else { + Active = false; + } + } + + void done() { + assert(Active && "trying to end an inactive suppression"); + S.PopParsingDeclaration(State, NULL); + Active = false; + } + + void redelay() { + assert(!Active && "redelaying without having ended first"); + if (!DiagnosticPool.pool_empty()) + S.redelayDiagnostics(DiagnosticPool); + assert(DiagnosticPool.pool_empty()); + } + + ~SuppressAccessChecks() { + if (Active) done(); + } + }; + + /// \brief RAII object used to inform the actions that we're + /// currently parsing a declaration. This is active when parsing a + /// variable's initializer, but not when parsing the body of a + /// class or function definition. + class ParsingDeclRAIIObject { + Sema &Actions; + sema::DelayedDiagnosticPool DiagnosticPool; + Sema::ParsingDeclState State; + bool Popped; + + // Do not implement. + ParsingDeclRAIIObject(const ParsingDeclRAIIObject &other); + ParsingDeclRAIIObject &operator=(const ParsingDeclRAIIObject &other); + + public: + enum NoParent_t { NoParent }; + ParsingDeclRAIIObject(Parser &P, NoParent_t _) + : Actions(P.getActions()), DiagnosticPool(NULL) { + push(); + } + + /// Creates a RAII object whose pool is optionally parented by another. + ParsingDeclRAIIObject(Parser &P, + const sema::DelayedDiagnosticPool *parentPool) + : Actions(P.getActions()), DiagnosticPool(parentPool) { + push(); + } + + /// Creates a RAII object and, optionally, initialize its + /// diagnostics pool by stealing the diagnostics from another + /// RAII object (which is assumed to be the current top pool). + ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other) + : Actions(P.getActions()), + DiagnosticPool(other ? other->DiagnosticPool.getParent() : NULL) { + if (other) { + DiagnosticPool.steal(other->DiagnosticPool); + other->abort(); + } + push(); + } + + ~ParsingDeclRAIIObject() { + abort(); + } + + sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() { + return DiagnosticPool; + } + const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const { + return DiagnosticPool; + } + + /// Resets the RAII object for a new declaration. + void reset() { + abort(); + push(); + } + + /// Signals that the context was completed without an appropriate + /// declaration being parsed. + void abort() { + pop(0); + } + + void complete(Decl *D) { + assert(!Popped && "ParsingDeclaration has already been popped!"); + pop(D); + } + + /// Unregister this object from Sema, but remember all the + /// diagnostics that were emitted into it. + void abortAndRemember() { + pop(0); + } + + private: + void push() { + State = Actions.PushParsingDeclaration(DiagnosticPool); + Popped = false; + } + + void pop(Decl *D) { + if (!Popped) { + Actions.PopParsingDeclaration(State, D); + Popped = true; + } + } + }; + + /// A class for parsing a DeclSpec. + class ParsingDeclSpec : public DeclSpec { + ParsingDeclRAIIObject ParsingRAII; + + public: + ParsingDeclSpec(Parser &P) + : DeclSpec(P.getAttrFactory()), + ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {} + ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII) + : DeclSpec(P.getAttrFactory()), + ParsingRAII(P, RAII) {} + + const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const { + return ParsingRAII.getDelayedDiagnosticPool(); + } + + void complete(Decl *D) { + ParsingRAII.complete(D); + } + + void abort() { + ParsingRAII.abort(); + } + }; + + /// A class for parsing a declarator. + class ParsingDeclarator : public Declarator { + ParsingDeclRAIIObject ParsingRAII; + + public: + ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, TheContext C) + : Declarator(DS, C), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { + } + + const ParsingDeclSpec &getDeclSpec() const { + return static_cast<const ParsingDeclSpec&>(Declarator::getDeclSpec()); + } + + ParsingDeclSpec &getMutableDeclSpec() const { + return const_cast<ParsingDeclSpec&>(getDeclSpec()); + } + + void clear() { + Declarator::clear(); + ParsingRAII.reset(); + } + + void complete(Decl *D) { + ParsingRAII.complete(D); + } + }; + + /// A class for parsing a field declarator. + class ParsingFieldDeclarator : public FieldDeclarator { + ParsingDeclRAIIObject ParsingRAII; + + public: + ParsingFieldDeclarator(Parser &P, const ParsingDeclSpec &DS) + : FieldDeclarator(DS), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { + } + + const ParsingDeclSpec &getDeclSpec() const { + return static_cast<const ParsingDeclSpec&>(D.getDeclSpec()); + } + + ParsingDeclSpec &getMutableDeclSpec() const { + return const_cast<ParsingDeclSpec&>(getDeclSpec()); + } + + void complete(Decl *D) { + ParsingRAII.complete(D); + } + }; + /// ExtensionRAIIObject - This saves the state of extension warnings when /// constructed and disables them. When destructed, it restores them back to /// the way they used to be. This is used to handle __extension__ in the @@ -137,6 +354,81 @@ namespace clang { } }; + /// \brief RAII class that helps handle the parsing of an open/close delimiter + /// pair, such as braces { ... } or parentheses ( ... ). + class BalancedDelimiterTracker : public GreaterThanIsOperatorScope { + Parser& P; + tok::TokenKind Kind, Close; + SourceLocation (Parser::*Consumer)(); + SourceLocation LOpen, LClose; + + unsigned short &getDepth() { + switch (Kind) { + case tok::l_brace: return P.BraceCount; + case tok::l_square: return P.BracketCount; + case tok::l_paren: return P.ParenCount; + default: llvm_unreachable("Wrong token kind"); + } + } + + enum { MaxDepth = 256 }; + + bool diagnoseOverflow(); + bool diagnoseMissingClose(); + + public: + BalancedDelimiterTracker(Parser& p, tok::TokenKind k) + : GreaterThanIsOperatorScope(p.GreaterThanIsOperator, true), + P(p), Kind(k) + { + switch (Kind) { + default: llvm_unreachable("Unexpected balanced token"); + case tok::l_brace: + Close = tok::r_brace; + Consumer = &Parser::ConsumeBrace; + break; + case tok::l_paren: + Close = tok::r_paren; + Consumer = &Parser::ConsumeParen; + break; + + case tok::l_square: + Close = tok::r_square; + Consumer = &Parser::ConsumeBracket; + break; + } + } + + SourceLocation getOpenLocation() const { return LOpen; } + SourceLocation getCloseLocation() const { return LClose; } + SourceRange getRange() const { return SourceRange(LOpen, LClose); } + + bool consumeOpen() { + if (!P.Tok.is(Kind)) + return true; + + if (getDepth() < MaxDepth) { + LOpen = (P.*Consumer)(); + return false; + } + + return diagnoseOverflow(); + } + + bool expectAndConsume(unsigned DiagID, + const char *Msg = "", + tok::TokenKind SkipToTok = tok::unknown); + bool consumeClose() { + if (P.Tok.is(Close)) { + LClose = (P.*Consumer)(); + return false; + } + + return diagnoseMissingClose(); + } + void skipToEnd(); + }; + } // end namespace clang #endif diff --git a/lib/Rewrite/CMakeLists.txt b/lib/Rewrite/CMakeLists.txt index 2a0504019381..af8f6d4f90dd 100644 --- a/lib/Rewrite/CMakeLists.txt +++ b/lib/Rewrite/CMakeLists.txt @@ -1,11 +1,10 @@ -set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend) - add_clang_library(clangRewrite DeltaTree.cpp FixItRewriter.cpp FrontendActions.cpp HTMLPrint.cpp HTMLRewrite.cpp + InclusionRewriter.cpp RewriteMacros.cpp RewriteModernObjC.cpp RewriteObjC.cpp @@ -18,5 +17,17 @@ add_clang_library(clangRewrite add_dependencies(clangRewrite ClangAttrClasses ClangAttrList + ClangAttrParsedAttrList + ClangCommentNodes ClangDeclNodes - ClangStmtNodes) + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangStmtNodes + ) + +target_link_libraries(clangRewrite + clangBasic + clangAST + clangParse + clangFrontend + ) diff --git a/lib/Rewrite/FrontendActions.cpp b/lib/Rewrite/FrontendActions.cpp index 1753325a26c7..9bc218e994fe 100644 --- a/lib/Rewrite/FrontendActions.cpp +++ b/lib/Rewrite/FrontendActions.cpp @@ -155,7 +155,7 @@ bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { ASTConsumer *RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { - if (CI.getLangOpts().ObjCNonFragileABI) + if (CI.getLangOpts().ObjCRuntime.isNonFragile()) return CreateModernObjCRewriter(InFile, OS, CI.getDiagnostics(), CI.getLangOpts(), CI.getDiagnosticOpts().NoRewriteMacros); @@ -181,3 +181,12 @@ void RewriteTestAction::ExecuteAction() { DoRewriteTest(CI.getPreprocessor(), OS); } + +void RewriteIncludesAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + RewriteIncludesInInput(CI.getPreprocessor(), OS, + CI.getPreprocessorOutputOpts()); +} diff --git a/lib/Rewrite/HTMLRewrite.cpp b/lib/Rewrite/HTMLRewrite.cpp index dc39dde7ff1f..236b98fc2828 100644 --- a/lib/Rewrite/HTMLRewrite.cpp +++ b/lib/Rewrite/HTMLRewrite.cpp @@ -325,11 +325,12 @@ void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID, " .msgControl { background-color:#bbbbbb; color:#000000 }\n" " .mrange { background-color:#dfddf3 }\n" " .mrange { border-bottom:1px solid #6F9DBE }\n" - " .PathIndex { font-weight: bold; padding:0px 5px 0px 5px; " + " .PathIndex { font-weight: bold; padding:0px 5px; " "margin-right:5px; }\n" " .PathIndex { -webkit-border-radius:8px }\n" " .PathIndexEvent { background-color:#bfba87 }\n" " .PathIndexControl { background-color:#8c8c8c }\n" + " .PathNav a { text-decoration:none; font-size: larger }\n" " .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n" " .CodeRemovalHint { background-color:#de1010 }\n" " .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n" @@ -495,6 +496,11 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) { // Inform the preprocessor that we don't want comments. TmpPP.SetCommentRetentionState(false, false); + // We don't want pragmas either. Although we filtered out #pragma, removing + // _Pragma and __pragma is much harder. + bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled(); + TmpPP.setPragmasEnabled(false); + // Enter the tokens we just lexed. This will cause them to be macro expanded // but won't enter sub-files (because we removed #'s). TmpPP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false); @@ -571,6 +577,7 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) { "<span class='macro'>", Expansion.c_str()); } - // Restore diagnostics object back to its own thing. + // Restore the preprocessor's old state. TmpPP.setDiagnostics(*OldDiags); + TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled); } diff --git a/lib/Rewrite/InclusionRewriter.cpp b/lib/Rewrite/InclusionRewriter.cpp new file mode 100644 index 000000000000..3dfc3b008987 --- /dev/null +++ b/lib/Rewrite/InclusionRewriter.cpp @@ -0,0 +1,361 @@ +//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites include invocations into their expansions. This gives you +// a file with all included files merged into it. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Rewriters.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace llvm; + +namespace { + +class InclusionRewriter : public PPCallbacks { + /// Information about which #includes were actually performed, + /// created by preprocessor callbacks. + struct FileChange { + SourceLocation From; + FileID Id; + SrcMgr::CharacteristicKind FileType; + FileChange(SourceLocation From) : From(From) { + } + }; + Preprocessor &PP; ///< Used to find inclusion directives. + SourceManager &SM; ///< Used to read and manage source files. + raw_ostream &OS; ///< The destination stream for rewritten contents. + bool ShowLineMarkers; ///< Show #line markers. + bool UseLineDirective; ///< Use of line directives or line markers. + typedef std::map<unsigned, FileChange> FileChangeMap; + FileChangeMap FileChanges; /// Tracks which files were included where. + /// Used transitively for building up the FileChanges mapping over the + /// various \c PPCallbacks callbacks. + FileChangeMap::iterator LastInsertedFileChange; +public: + InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers); + bool Process(FileID FileId, SrcMgr::CharacteristicKind FileType); +private: + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID); + virtual void FileSkipped(const FileEntry &ParentFile, + const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath); + void WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef EOL, StringRef Extra = StringRef()); + void OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &lines, + bool EnsureNewline = false); + void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, + const MemoryBuffer &FromFile, StringRef EOL, + unsigned &NextToWrite, int &Lines); + const FileChange *FindFileChangeLocation(SourceLocation Loc) const; + StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); +}; + +} // end anonymous namespace + +/// Initializes an InclusionRewriter with a \p PP source and \p OS destination. +InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS, + bool ShowLineMarkers) + : PP(PP), SM(PP.getSourceManager()), OS(OS), + ShowLineMarkers(ShowLineMarkers), + LastInsertedFileChange(FileChanges.end()) { + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; +} + +/// Write appropriate line information as either #line directives or GNU line +/// markers depending on what mode we're in, including the \p Filename and +/// \p Line we are located at, using the specified \p EOL line separator, and +/// any \p Extra context specifiers in GNU line directives. +void InclusionRewriter::WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef EOL, StringRef Extra) { + if (!ShowLineMarkers) + return; + if (UseLineDirective) { + OS << "#line" << ' ' << Line << ' ' << '"' << Filename << '"'; + } else { + // Use GNU linemarkers as described here: + // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html + OS << '#' << ' ' << Line << ' ' << '"' << Filename << '"'; + if (!Extra.empty()) + OS << Extra; + if (FileType == SrcMgr::C_System) + // "`3' This indicates that the following text comes from a system header + // file, so certain warnings should be suppressed." + OS << " 3"; + else if (FileType == SrcMgr::C_ExternCSystem) + // as above for `3', plus "`4' This indicates that the following text + // should be treated as being wrapped in an implicit extern "C" block." + OS << " 3 4"; + } + OS << EOL; +} + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. +void InclusionRewriter::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID) { + if (Reason != EnterFile) + return; + if (LastInsertedFileChange == FileChanges.end()) + // we didn't reach this file (eg: the main file) via an inclusion directive + return; + LastInsertedFileChange->second.Id = FullSourceLoc(Loc, SM).getFileID(); + LastInsertedFileChange->second.FileType = NewFileType; + LastInsertedFileChange = FileChanges.end(); +} + +/// Called whenever an inclusion is skipped due to canonical header protection +/// macros. +void InclusionRewriter::FileSkipped(const FileEntry &/*ParentFile*/, + const Token &/*FilenameTok*/, + SrcMgr::CharacteristicKind /*FileType*/) { + assert(LastInsertedFileChange != FileChanges.end() && "A file, that wasn't " + "found via an inclusion directive, was skipped"); + FileChanges.erase(LastInsertedFileChange); + LastInsertedFileChange = FileChanges.end(); +} + +/// This should be called whenever the preprocessor encounters include +/// directives. It does not say whether the file has been included, but it +/// provides more information about the directive (hash location instead +/// of location inside the included file). It is assumed that the matching +/// FileChanged() or FileSkipped() is called after this. +void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, + const Token &/*IncludeTok*/, + StringRef /*FileName*/, + bool /*IsAngled*/, + const FileEntry * /*File*/, + SourceLocation /*EndLoc*/, + StringRef /*SearchPath*/, + StringRef /*RelativePath*/) { + assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion " + "directive was found before the previous one was processed"); + std::pair<FileChangeMap::iterator, bool> p = FileChanges.insert( + std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc))); + assert(p.second && "Unexpected revisitation of the same include directive"); + LastInsertedFileChange = p.first; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of inclusion information, FileChanges. +const InclusionRewriter::FileChange * +InclusionRewriter::FindFileChangeLocation(SourceLocation Loc) const { + FileChangeMap::const_iterator I = FileChanges.find(Loc.getRawEncoding()); + if (I != FileChanges.end()) + return &I->second; + return NULL; +} + +/// Detect the likely line ending style of \p FromFile by examining the first +/// newline found within it. +static StringRef DetectEOL(const MemoryBuffer &FromFile) { + // detect what line endings the file uses, so that added content does not mix + // the style + const char *Pos = strchr(FromFile.getBufferStart(), '\n'); + if (Pos == NULL) + return "\n"; + if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r') + return "\n\r"; + if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r') + return "\r\n"; + return "\n"; +} + +/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at +/// \p WriteTo - 1. +void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &Line, + bool EnsureNewline) { + if (WriteTo <= WriteFrom) + return; + OS.write(FromFile.getBufferStart() + WriteFrom, WriteTo - WriteFrom); + // count lines manually, it's faster than getPresumedLoc() + Line += std::count(FromFile.getBufferStart() + WriteFrom, + FromFile.getBufferStart() + WriteTo, '\n'); + if (EnsureNewline) { + char LastChar = FromFile.getBufferStart()[WriteTo - 1]; + if (LastChar != '\n' && LastChar != '\r') + OS << EOL; + } + WriteFrom = WriteTo; +} + +/// Print characters from \p FromFile starting at \p NextToWrite up until the +/// inclusion directive at \p StartToken, then print out the inclusion +/// inclusion directive disabled by a #if directive, updating \p NextToWrite +/// and \p Line to track the number of source lines visited and the progress +/// through the \p FromFile buffer. +void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex, + const Token &StartToken, + const MemoryBuffer &FromFile, + StringRef EOL, + unsigned &NextToWrite, int &Line) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(StartToken.getLocation()), EOL, Line); + Token DirectiveToken; + do { + DirectiveLex.LexFromRawLexer(DirectiveToken); + } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof)); + OS << "#if 0 /* expanded by -frewrite-includes */" << EOL; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(DirectiveToken.getLocation()) + DirectiveToken.getLength(), + EOL, Line); + OS << "#endif /* expanded by -frewrite-includes */" << EOL; +} + +/// Find the next identifier in the pragma directive specified by \p RawToken. +StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, + Token &RawToken) { + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) + return RawToken.getIdentifierInfo()->getName(); + return StringRef(); +} + +/// Use a raw lexer to analyze \p FileId, inccrementally copying parts of it +/// and including content of included files recursively. +bool InclusionRewriter::Process(FileID FileId, + SrcMgr::CharacteristicKind FileType) +{ + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); + if (Invalid) // invalid inclusion + return true; + const char *FileName = FromFile.getBufferIdentifier(); + Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); + RawLex.SetCommentRetentionState(false); + + StringRef EOL = DetectEOL(FromFile); + + // Per the GNU docs: "1" indicates the start of a new file. + WriteLineInfo(FileName, 1, FileType, EOL, " 1"); + + if (SM.getFileIDSize(FileId) == 0) + return true; + + // The next byte to be copied from the source file + unsigned NextToWrite = 0; + int Line = 1; // The current input file line number. + + Token RawToken; + RawLex.LexFromRawLexer(RawToken); + + // TODO: Consider adding a switch that strips possibly unimportant content, + // such as comments, to reduce the size of repro files. + while (RawToken.isNot(tok::eof)) { + if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) { + RawLex.setParsingPreprocessorDirective(true); + Token HashToken = RawToken; + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) { + switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_include_next: + case tok::pp_import: { + CommentOutDirective(RawLex, HashToken, FromFile, EOL, NextToWrite, + Line); + if (const FileChange *Change = FindFileChangeLocation( + HashToken.getLocation())) { + // now include and recursively process the file + if (Process(Change->Id, Change->FileType)) + // and set lineinfo back to this file, if the nested one was + // actually included + // `2' indicates returning to a file (after having included + // another file. + WriteLineInfo(FileName, Line, FileType, EOL, " 2"); + } else + // fix up lineinfo (since commented out directive changed line + // numbers) for inclusions that were skipped due to header guards + WriteLineInfo(FileName, Line, FileType, EOL); + break; + } + case tok::pp_pragma: { + StringRef Identifier = NextIdentifierName(RawLex, RawToken); + if (Identifier == "clang" || Identifier == "GCC") { + if (NextIdentifierName(RawLex, RawToken) == "system_header") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, EOL, + NextToWrite, Line); + // update our own type + FileType = SM.getFileCharacteristic(RawToken.getLocation()); + WriteLineInfo(FileName, Line, FileType, EOL); + } + } else if (Identifier == "once") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, EOL, + NextToWrite, Line); + WriteLineInfo(FileName, Line, FileType, EOL); + } + break; + } + default: + break; + } + } + RawLex.setParsingPreprocessorDirective(false); + } + RawLex.LexFromRawLexer(RawToken); + } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(SM.getLocForEndOfFile(FileId)) + 1, EOL, Line, + /*EnsureNewline*/true); + return true; +} + +/// InclusionRewriterInInput - Implement -frewrite-includes mode. +void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + SourceManager &SM = PP.getSourceManager(); + InclusionRewriter *Rewrite = new InclusionRewriter(PP, *OS, + Opts.ShowLineMarkers); + PP.addPPCallbacks(Rewrite); + + // First let the preprocessor process the entire file and call callbacks. + // Callbacks will record which #include's were actually performed. + PP.EnterMainSourceFile(); + Token Tok; + // Only preprocessor directives matter here, so disable macro expansion + // everywhere else as an optimization. + // TODO: It would be even faster if the preprocessor could be switched + // to a mode where it would parse only preprocessor directives and comments, + // nothing else matters for parsing or processing. + PP.SetMacroExpansionOnlyInDirectives(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User); + OS->flush(); +} diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp index 94fba64e17a7..9f42fcacfab2 100644 --- a/lib/Rewrite/RewriteModernObjC.cpp +++ b/lib/Rewrite/RewriteModernObjC.cpp @@ -102,7 +102,6 @@ namespace { FunctionDecl *CFStringFunctionDecl; FunctionDecl *SuperContructorFunctionDecl; FunctionDecl *CurFunctionDef; - FunctionDecl *CurFunctionDeclToDeclareForBlock; /* Misc. containers needed for meta-data rewrite. */ SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; @@ -110,7 +109,7 @@ namespace { llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs; llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols; llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCWrittenInterfaces; - llvm::SmallPtrSet<TagDecl*, 8> TagsDefinedInIvarDecls; + llvm::SmallPtrSet<TagDecl*, 32> GlobalDefinedTags; SmallVector<ObjCInterfaceDecl*, 32> ObjCInterfacesSeen; /// DefinedNonLazyClasses - List of defined "non-lazy" classes. SmallVector<ObjCInterfaceDecl*, 8> DefinedNonLazyClasses; @@ -304,9 +303,12 @@ namespace { void RewriteFunctionDecl(FunctionDecl *FD); void RewriteBlockPointerType(std::string& Str, QualType Type); void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); void RewriteTypeOfDecl(VarDecl *VD); void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + std::string getIvarAccessString(ObjCIvarDecl *D); // Expression Rewriting. Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); @@ -317,11 +319,12 @@ namespace { Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); - Stmt *RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp); + Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, @@ -337,7 +340,7 @@ namespace { // Block specific rewrite rules. void RewriteBlockPointerDecl(NamedDecl *VD); - void RewriteByRefVar(VarDecl *VD); + void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl); Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); @@ -346,6 +349,10 @@ namespace { std::string &Result); void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result); + bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag, + bool &IsNamedDefinition); + void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result); bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result); @@ -354,12 +361,19 @@ namespace { virtual void Initialize(ASTContext &context); - // Misc. AST transformation routines. Somtimes they end up calling + // Misc. AST transformation routines. Sometimes they end up calling // rewriting routines on the new ASTs. CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc=SourceLocation(), SourceLocation EndLoc=SourceLocation()); + + Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation StartLoc=SourceLocation(), @@ -387,23 +401,23 @@ namespace { std::string &Result); void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, std::string &Result); - virtual void RewriteObjCProtocolListMetaData( + void RewriteObjCProtocolListMetaData( const ObjCList<ObjCProtocolDecl> &Prots, StringRef prefix, StringRef ClassName, std::string &Result); - virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, std::string &Result); - virtual void RewriteClassSetupInitHook(std::string &Result); + void RewriteClassSetupInitHook(std::string &Result); - virtual void RewriteMetaDataIntoBuffer(std::string &Result); - virtual void WriteImageInfo(std::string &Result); - virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + void RewriteMetaDataIntoBuffer(std::string &Result); + void WriteImageInfo(std::string &Result); + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, std::string &Result); - virtual void RewriteCategorySetupInitHook(std::string &Result); + void RewriteCategorySetupInitHook(std::string &Result); // Rewriting ivar - virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, std::string &Result); - virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); @@ -622,7 +636,6 @@ void RewriteModernObjC::InitializeCommon(ASTContext &context) { NSStringRecord = 0; CurMethodDef = 0; CurFunctionDef = 0; - CurFunctionDeclToDeclareForBlock = 0; GlobalVarDecl = 0; GlobalConstructionExp = 0; SuperStructDecl = 0; @@ -768,29 +781,104 @@ void RewriteModernObjC::RewriteInclude() { } } -static std::string getIvarAccessString(ObjCIvarDecl *OID) { - const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface(); - std::string S; - S = "((struct "; - S += ClassDecl->getIdentifier()->getName(); - S += "_IMPL *)self)->"; - S += OID->getName(); +static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl, + ObjCIvarDecl *IvarDecl, std::string &Result) { + Result += "OBJC_IVAR_$_"; + Result += IDecl->getName(); + Result += "$"; + Result += IvarDecl->getName(); +} + +std::string +RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { + const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface(); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + WriteInternalIvarName(ClassDecl, D, IvarOffsetName); + + + std::string S = "(*("; + QualType IvarT = D->getType(); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + std::string TypeString(castT.getAsString(Context->getPrintingPolicy())); + S += TypeString; + S += ")"; + + // ((char *)self + IVAR_OFFSET_SYMBOL_NAME) + S += "((char *)self + "; + S += IvarOffsetName; + S += "))"; + ReferencedIvars[const_cast<ObjCInterfaceDecl *>(ClassDecl)].insert(D); return S; } +/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not +/// been found in the class implementation. In this case, it must be synthesized. +static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, + ObjCPropertyDecl *PD, + bool getter) { + return getter ? !IMP->getInstanceMethod(PD->getGetterName()) + : !IMP->getInstanceMethod(PD->getSetterName()); + +} + void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, ObjCImplementationDecl *IMD, ObjCCategoryImplDecl *CID) { static bool objcGetPropertyDefined = false; static bool objcSetPropertyDefined = false; - SourceLocation startLoc = PID->getLocStart(); - InsertText(startLoc, "// "); - const char *startBuf = SM->getCharacterData(startLoc); - assert((*startBuf == '@') && "bogus @synthesize location"); - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "@synthesize: can't find ';'"); - SourceLocation onePastSemiLoc = - startLoc.getLocWithOffset(semiBuf-startBuf+1); + SourceLocation startGetterSetterLoc; + + if (PID->getLocStart().isValid()) { + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + } + else + startGetterSetterLoc = IMD ? IMD->getLocEnd() : CID->getLocEnd(); if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) return; // FIXME: is this correct? @@ -802,7 +890,7 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, if (!OID) return; unsigned Attributes = PD->getPropertyAttributes(); - if (!PD->getGetterMethodDecl()->isDefined()) { + if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) { bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_copy)); @@ -854,10 +942,11 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, else Getr += "return " + getIvarAccessString(OID); Getr += "; }"; - InsertText(onePastSemiLoc, Getr); + InsertText(startGetterSetterLoc, Getr); } - if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) + if (PD->isReadOnly() || + !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) return; // Generate the 'setter' function. @@ -895,8 +984,8 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, Setr += getIvarAccessString(OID) + " = "; Setr += PD->getName(); } - Setr += "; }"; - InsertText(onePastSemiLoc, Setr); + Setr += "; }\n"; + InsertText(startGetterSetterLoc, Setr); } static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, @@ -985,17 +1074,13 @@ void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { SourceLocation LocStart = CatDecl->getLocStart(); // FIXME: handle category headers that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); - if (CatDecl->getIvarLBraceLoc().isValid()) - InsertText(CatDecl->getIvarLBraceLoc(), "// "); - for (ObjCCategoryDecl::ivar_iterator - I = CatDecl->ivar_begin(), E = CatDecl->ivar_end(); I != E; ++I) { - ObjCIvarDecl *Ivar = (*I); - SourceLocation LocStart = Ivar->getLocStart(); + if (CatDecl->getIvarRBraceLoc().isValid()) { + ReplaceText(LocStart, 1, "/** "); + ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ "); + } + else { ReplaceText(LocStart, 0, "// "); - } - if (CatDecl->getIvarRBraceLoc().isValid()) - InsertText(CatDecl->getIvarRBraceLoc(), "// "); + } for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(), E = CatDecl->prop_end(); I != E; ++I) @@ -1221,17 +1306,13 @@ void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); if (IMD) { - InsertText(IMD->getLocStart(), "// "); - if (IMD->getIvarLBraceLoc().isValid()) - InsertText(IMD->getIvarLBraceLoc(), "// "); - for (ObjCImplementationDecl::ivar_iterator - I = IMD->ivar_begin(), E = IMD->ivar_end(); I != E; ++I) { - ObjCIvarDecl *Ivar = (*I); - SourceLocation LocStart = Ivar->getLocStart(); - ReplaceText(LocStart, 0, "// "); + if (IMD->getIvarRBraceLoc().isValid()) { + ReplaceText(IMD->getLocStart(), 1, "/** "); + ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ "); + } + else { + InsertText(IMD->getLocStart(), "// "); } - if (IMD->getIvarRBraceLoc().isValid()) - InsertText(IMD->getIvarRBraceLoc(), "// "); } else InsertText(CID->getLocStart(), "// "); @@ -1808,6 +1889,15 @@ void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S) return; } +Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + SourceLocation startLoc = S->getAtLoc(); + ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */"); + ReplaceText(S->getSubStmt()->getLocStart(), 1, + "{ __AtAutoreleasePool __autoreleasepool; "); + + return 0; +} + Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt(); bool noCatch = S->getNumCatchStmts() == 0; @@ -2245,6 +2335,32 @@ void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str, } } +void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + QualType Type = proto->getResultType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getArgType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + if (FD->isVariadic()) { + FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n"; + } + else + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); +} + // SynthSuperContructorFunctionDecl - id __rw_objc_super(id obj, id super); void RewriteModernObjC::SynthSuperContructorFunctionDecl() { if (SuperContructorFunctionDecl) @@ -2362,12 +2478,12 @@ void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() { SC_None, false); } -// SynthGetClassFunctionDecl - id objc_getClass(const char *name); +// SynthGetClassFunctionDecl - Class objc_getClass(const char *name); void RewriteModernObjC::SynthGetClassFunctionDecl() { IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); SmallVector<QualType, 16> ArgTys; ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), &ArgTys[0], ArgTys.size()); GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), @@ -2395,12 +2511,12 @@ void RewriteModernObjC::SynthGetSuperClassFunctionDecl() { false); } -// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name); +// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name); void RewriteModernObjC::SynthGetMetaClassFunctionDecl() { IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); SmallVector<QualType, 16> ArgTys; ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), &ArgTys[0], ArgTys.size()); GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), @@ -2471,7 +2587,7 @@ Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { return PE; } -Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) { +Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { // synthesize declaration of helper functions needed in this routine. if (!SelGetUidFunctionDecl) SynthSelGetUidFunctionDecl(); @@ -2489,13 +2605,12 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) SmallVector<Expr*, 4> MsgExprs; SmallVector<Expr*, 4> ClsExprs; QualType argType = Context->getPointerType(Context->CharTy); - QualType expType = Exp->getType(); - // Create a call to objc_getClass("NSNumber"). It will be th 1st argument. - ObjCInterfaceDecl *Class = - expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + // Create a call to objc_getClass("<BoxingClass>"). It will be the 1st argument. + ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); + ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); - IdentifierInfo *clsName = Class->getIdentifier(); + IdentifierInfo *clsName = BoxingClass->getIdentifier(); ClsExprs.push_back(StringLiteral::Create(*Context, clsName->getName(), StringLiteral::Ascii, false, @@ -2506,12 +2621,11 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) StartLoc, EndLoc); MsgExprs.push_back(Cls); - // Create a call to sel_registerName("numberWithBool:"), etc. + // Create a call to sel_registerName("<BoxingMethod>:"), etc. // it will be the 2nd argument. SmallVector<Expr*, 4> SelExprs; - ObjCMethodDecl *NumericMethod = Exp->getObjCNumericLiteralMethod(); SelExprs.push_back(StringLiteral::Create(*Context, - NumericMethod->getSelector().getAsString(), + BoxingMethod->getSelector().getAsString(), StringLiteral::Ascii, false, argType, SourceLocation())); CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, @@ -2519,25 +2633,25 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) StartLoc, EndLoc); MsgExprs.push_back(SelExp); - // User provided numeric literal is the 3rd, and last, argument. - Expr *userExpr = Exp->getNumber(); - if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) { + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(subExpr)) { QualType type = ICE->getType(); const Expr *SubExpr = ICE->IgnoreParenImpCasts(); CastKind CK = CK_BitCast; if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) CK = CK_IntegralToBoolean; - userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); } - MsgExprs.push_back(userExpr); + MsgExprs.push_back(subExpr); SmallVector<QualType, 4> ArgTypes; ArgTypes.push_back(Context->getObjCIdType()); ArgTypes.push_back(Context->getObjCSelType()); - for (ObjCMethodDecl::param_iterator PI = NumericMethod->param_begin(), - E = NumericMethod->param_end(); PI != E; ++PI) + for (ObjCMethodDecl::param_iterator PI = BoxingMethod->param_begin(), + E = BoxingMethod->param_end(); PI != E; ++PI) ArgTypes.push_back((*PI)->getType()); - + QualType returnType = Exp->getType(); // Get the type, we will need to reference it in a couple spots. QualType msgSendType = MsgSendFlavor->getType(); @@ -2547,13 +2661,13 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) VK_LValue, SourceLocation()); CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); // Now do the "normal" pointer to function cast. QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - NumericMethod->isVariadic()); + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + BoxingMethod->isVariadic()); castType = Context->getPointerType(castType); cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, cast); @@ -2613,7 +2727,7 @@ Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) { &Context->Idents.get("arr"), Context->getPointerType(Context->VoidPtrTy), 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *ArrayLiteralME = new (Context) MemberExpr(NSArrayCallExpr, false, ARRFD, SourceLocation(), @@ -2760,7 +2874,7 @@ Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral &Context->Idents.get("arr"), Context->getPointerType(Context->VoidPtrTy), 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *DictLiteralValueME = new (Context) MemberExpr(NSValueCallExpr, false, ARRFD, SourceLocation(), @@ -2907,7 +3021,7 @@ QualType RewriteModernObjC::getSuperStructType() { FieldTypes[i], 0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false)); + ICIS_NoInit)); } SuperStructDecl->completeDefinition(); @@ -2940,7 +3054,7 @@ QualType RewriteModernObjC::getConstantStringStructType() { FieldTypes[i], 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false)); + ICIS_NoInit)); } ConstantStringDecl->completeDefinition(); @@ -2948,6 +3062,112 @@ QualType RewriteModernObjC::getConstantStringStructType() { return Context->getTagDeclType(ConstantStringDecl); } +/// getFunctionSourceLocation - returns start location of a function +/// definition. Complication arises when function has declared as +/// extern "C" or extern "C" {...} +static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R, + FunctionDecl *FD) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + return LSD->getExternLoc(); + } + if (FD->getStorageClassAsWritten() != SC_None) + R.RewriteBlockLiteralFunctionDecl(FD); + return FD->getTypeSpecStartLoc(); +} + +/// SynthMsgSendStretCallExpr - This routine translates message expression +/// into a call to objc_msgSend_stret() entry point. Tricky part is that +/// nil check on receiver must be performed before calling objc_msgSend_stret. +/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...) +/// msgSendType - function type of objc_msgSend_stret(...) +/// returnType - Result type of the method being synthesized. +/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type. +/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret, +/// starting with receiver. +/// Method - Method being rewritten. +Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + Method ? Method->isVariadic() : false); + castType = Context->getPointerType(castType); + + // build type for containing the objc_msgSend_stret object. + static unsigned stretCount=0; + std::string name = "__Stret"; name += utostr(stretCount); + std::string str = + "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n"; + str += "struct "; str += name; + str += " {\n\t"; + str += name; + str += "(id receiver, SEL sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + MsgExprs[i]->getType().getAsStringInternal(ArgName, + Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + + str += ") {\n"; + str += "\t if (receiver == 0)\n"; + str += "\t memset((void*)&s, 0, sizeof(s));\n"; + str += "\t else\n"; + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend_stret)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + + str += ");\n"; + str += "\t}\n"; + str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy()); + str += " s;\n"; + str += "};\n\n"; + SourceLocation FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + InsertText(FunLocStart, str); + ++stretCount; + + // AST for __Stretn(receiver, args).s; + IdentifierInfo *ID = &Context->Idents.get(name); + FunctionDecl *FD = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, castType, 0, SC_Extern, + SC_None, false, false); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue, + SourceLocation()); + CallExpr *STCE = new (Context) CallExpr(*Context, DRE, &MsgExprs[0], MsgExprs.size(), + castType, VK_LValue, SourceLocation()); + + FieldDecl *FieldD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("s"), + returnType, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(STCE, false, FieldD, SourceLocation(), + FieldD->getType(), VK_LValue, + OK_Ordinary); + + return ME; +} + Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -3013,17 +3233,14 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, ClassDecl->getIdentifier()->getName(), StringLiteral::Ascii, false, argType, SourceLocation())); + // (Class)objc_getClass("CurrentClass") CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, &ClsExprs[0], ClsExprs.size(), StartLoc, EndLoc); - // (Class)objc_getClass("CurrentClass") - CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getObjCClassType(), - CK_BitCast, Cls); ClsExprs.clear(); - ClsExprs.push_back(ArgExpr); + ClsExprs.push_back(Cls); Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, &ClsExprs[0], ClsExprs.size(), StartLoc, EndLoc); @@ -3096,7 +3313,10 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, &ClsExprs[0], ClsExprs.size(), StartLoc, EndLoc); - MsgExprs.push_back(Cls); + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls); + MsgExprs.push_back(ArgExpr); break; } @@ -3124,16 +3344,13 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, ClassDecl->getIdentifier()->getName(), StringLiteral::Ascii, false, argType, SourceLocation())); + // (Class)objc_getClass("CurrentClass") CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, &ClsExprs[0], ClsExprs.size(), StartLoc, EndLoc); - // (Class)objc_getClass("CurrentClass") - CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getObjCClassType(), - CK_BitCast, Cls); ClsExprs.clear(); - ClsExprs.push_back(ArgExpr); + ClsExprs.push_back(Cls); Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, &ClsExprs[0], ClsExprs.size(), StartLoc, EndLoc); @@ -3339,29 +3556,10 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, // expression which dictate which one to envoke depending on size of // method's return type. - // Create a reference to the objc_msgSend_stret() declaration. - DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, - false, msgSendType, - VK_LValue, SourceLocation()); - // Need to cast objc_msgSend_stret to "void *" (see above comment). - cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, STDRE); - // Now do the "normal" pointer to function cast. - castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : false); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); - - FT = msgSendType->getAs<FunctionType>(); - CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - SourceLocation()); + Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); // Build sizeof(returnType) UnaryExprOrTypeTraitExpr *sizeofExpr = @@ -3471,10 +3669,44 @@ bool RewriteModernObjC::BufferContainsPPDirectives(const char *startBuf, return false; } +/// IsTagDefinedInsideClass - This routine checks that a named tagged type +/// is defined inside an objective-c class. If so, it returns true. +bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, + TagDecl *Tag, + bool &IsNamedDefinition) { + if (!IDecl) + return false; + SourceLocation TagLocation; + if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) { + RD = RD->getDefinition(); + if (!RD || !RD->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = RD->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + } + if (EnumDecl *ED = dyn_cast<EnumDecl>(Tag)) { + if (!ED || !ED->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = ED->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + + } + return false; +} + /// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer. /// It handles elaborated types, as well as enum types in the process. bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, std::string &Result) { + if (isa<TypedefType>(Type)) { + Result += "\t"; + return false; + } + if (Type->isArrayType()) { QualType ElemTy = Context->getBaseElementType(Type); return RewriteObjCFieldDeclType(ElemTy, Result); @@ -3490,12 +3722,11 @@ bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, assert(false && "class not allowed as an ivar type"); Result += RD->getName(); - if (TagsDefinedInIvarDecls.count(RD)) { - // This struct is already defined. Do not write its definition again. + if (GlobalDefinedTags.count(RD)) { + // struct/union is defined globally, use it. Result += " "; return true; } - TagsDefinedInIvarDecls.insert(RD); Result += " {\n"; for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i) { @@ -3511,12 +3742,11 @@ bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, if (ED->isCompleteDefinition()) { Result += "\n\tenum "; Result += ED->getName(); - if (TagsDefinedInIvarDecls.count(ED)) { - // This enum is already defined. Do not write its definition again. + if (GlobalDefinedTags.count(ED)) { + // Enum is globall defined, use it. Result += " "; return true; } - TagsDefinedInIvarDecls.insert(ED); Result += " {\n"; for (EnumDecl::enumerator_iterator EC = ED->enumerator_begin(), @@ -3567,6 +3797,41 @@ void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl, Result += ";\n"; } +/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined +/// named aggregate types into the input buffer. +void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + if (isa<TypedefType>(Type)) + return; + if (Type->isArrayType()) + Type = Context->getBaseElementType(Type); + ObjCContainerDecl *IDecl = + dyn_cast<ObjCContainerDecl>(fieldDecl->getDeclContext()); + + TagDecl *TD = 0; + if (Type->isRecordType()) { + TD = Type->getAs<RecordType>()->getDecl(); + } + else if (Type->isEnumeralType()) { + TD = Type->getAs<EnumType>()->getDecl(); + } + + if (TD) { + if (GlobalDefinedTags.count(TD)) + return; + + bool IsNamedDefinition = false; + if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) { + RewriteObjCFieldDeclType(Type, Result); + Result += ";"; + } + if (IsNamedDefinition) + GlobalDefinedTags.insert(TD); + } + +} + /// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to /// an objective-c class with ivars. void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, @@ -3595,6 +3860,12 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, return; } + // Insert named struct/union definitions inside class to + // outer scope. This follows semantics of locally defined + // struct/unions in objective-c classes. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + RewriteLocallyDefinedNamedAggregates(IVars[i], Result); + Result += "\nstruct "; Result += CDecl->getNameAsString(); Result += "_IMPL {\n"; @@ -3604,7 +3875,7 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, Result += "_IMPL "; Result += RCDecl->getNameAsString(); Result += "_IVARS;\n"; } - TagsDefinedInIvarDecls.clear(); + for (unsigned i = 0, e = IVars.size(); i < e; i++) RewriteObjCFieldDecl(IVars[i], Result); @@ -3616,14 +3887,6 @@ void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct"); } -static void WriteInternalIvarName(ObjCInterfaceDecl *IDecl, - ObjCIvarDecl *IvarDecl, std::string &Result) { - Result += "OBJC_IVAR_$_"; - Result += IDecl->getName(); - Result += "$"; - Result += IvarDecl->getName(); -} - /// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which /// have been referenced in an ivar access expression. void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, @@ -3961,8 +4224,8 @@ std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, unsigned hasCopy) { std::string S = "\nstatic struct " + DescTag; - S += " {\n unsigned long reserved;\n"; - S += " unsigned long Block_size;\n"; + S += " {\n size_t reserved;\n"; + S += " size_t Block_size;\n"; if (hasCopy) { S += " void (*copy)(struct "; S += ImplTag; S += "*, struct "; @@ -3983,23 +4246,6 @@ std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, return S; } -/// getFunctionSourceLocation - returns start location of a function -/// definition. Complication arises when function has declared as -/// extern "C" or extern "C" {...} -static SourceLocation getFunctionSourceLocation (FunctionDecl *FD) { - if (!FD->isExternC() || FD->isMain()) - return FD->getTypeSpecStartLoc(); - const DeclContext *DC = FD->getDeclContext(); - if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) { - SourceLocation BodyRBrace = LSD->getRBraceLoc(); - // if it is extern "C" {...}, return function decl's own location. - if (BodyRBrace.isValid()) - return FD->getTypeSpecStartLoc(); - return LSD->getExternLoc(); - } - return FD->getTypeSpecStartLoc(); -} - void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, StringRef FunName) { bool RewriteSC = (GlobalVarDecl && @@ -4110,7 +4356,9 @@ void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, } void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { - SourceLocation FunLocStart = getFunctionSourceLocation(FD); + SourceLocation FunLocStart = + (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD) + : FD->getTypeSpecStartLoc(); StringRef FuncName = FD->getName(); SynthesizeBlockLiterals(FunLocStart, FuncName); @@ -4320,7 +4568,7 @@ Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp &Context->Idents.get("FuncPtr"), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), FD->getType(), VK_LValue, OK_Ordinary); @@ -4369,7 +4617,7 @@ Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { &Context->Idents.get("__forwarding"), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, FD, SourceLocation(), FD->getType(), VK_LValue, @@ -4380,7 +4628,7 @@ Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { &Context->Idents.get(Name), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), DeclRefExp->getType(), VK_LValue, OK_Ordinary); @@ -4719,7 +4967,8 @@ std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, /// ND=initializer-if-any}; /// /// -void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { +void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl, + bool lastDecl) { int flag = 0; int isa = 0; SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); @@ -4758,17 +5007,17 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { // Insert this type in global scope. It is needed by helper function. SourceLocation FunLocStart; if (CurFunctionDef) - FunLocStart = getFunctionSourceLocation(CurFunctionDef); + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); else { assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); FunLocStart = CurMethodDef->getLocStart(); } InsertText(FunLocStart, ByrefType); + if (Ty.isObjCGCWeak()) { flag |= BLOCK_FIELD_IS_WEAK; isa = 1; } - if (HasCopyAndDispose) { flag = BLOCK_BYREF_CALLER; QualType Ty = ND->getType(); @@ -4788,8 +5037,13 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { bool hasInit = (ND->getInit() != 0); // FIXME. rewriter does not support __block c++ objects which // require construction. - if (hasInit && dyn_cast<CXXConstructExpr>(ND->getInit())) - hasInit = false; + if (hasInit) + if (CXXConstructExpr *CExp = dyn_cast<CXXConstructExpr>(ND->getInit())) { + CXXConstructorDecl *CXXDecl = CExp->getConstructor(); + if (CXXDecl && CXXDecl->isDefaultConstructor()) + hasInit = false; + } + unsigned flags = 0; if (HasCopyAndDispose) flags |= BLOCK_HAS_COPY_DISPOSE; @@ -4798,21 +5052,36 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { RewriteByRefString(ByrefType, Name, ND); std::string ForwardingCastType("("); ForwardingCastType += ByrefType + " *)"; + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + + if (!firstDecl) { + // In multiple __block declarations, and for all but 1st declaration, + // find location of the separating comma. This would be start location + // where new text is to be inserted. + DeclLoc = ND->getLocation(); + const char *startDeclBuf = SM->getCharacterData(DeclLoc); + const char *commaBuf = startDeclBuf; + while (*commaBuf != ',') + commaBuf--; + assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','"); + DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf); + startBuf = commaBuf; + } + if (!hasInit) { - ByrefType += " " + Name + " = {(void*)"; - ByrefType += utostr(isa); - ByrefType += "," + ForwardingCastType + "&" + Name + ", "; - ByrefType += utostr(flags); - ByrefType += ", "; - ByrefType += "sizeof("; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += ")"; - if (HasCopyAndDispose) { - ByrefType += ", __Block_byref_id_object_copy_"; - ByrefType += utostr(flag); - ByrefType += ", __Block_byref_id_object_dispose_"; - ByrefType += utostr(flag); - } ByrefType += "};\n"; unsigned nameSize = Name.size(); // for block or function pointer declaration. Name is aleady @@ -4822,6 +5091,7 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); } else { + ByrefType += ", "; SourceLocation startLoc; Expr *E = ND->getInit(); if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) @@ -4830,39 +5100,17 @@ void RewriteModernObjC::RewriteByRefVar(VarDecl *ND) { startLoc = E->getLocStart(); startLoc = SM->getExpansionLoc(startLoc); endBuf = SM->getCharacterData(startLoc); - ByrefType += " " + Name; - ByrefType += " = {(void*)"; - ByrefType += utostr(isa); - ByrefType += "," + ForwardingCastType + "&" + Name + ", "; - ByrefType += utostr(flags); - ByrefType += ", "; - ByrefType += "sizeof("; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += "), "; - if (HasCopyAndDispose) { - ByrefType += "__Block_byref_id_object_copy_"; - ByrefType += utostr(flag); - ByrefType += ", __Block_byref_id_object_dispose_"; - ByrefType += utostr(flag); - ByrefType += ", "; - } ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); - - // Complete the newly synthesized compound expression by inserting a right - // curly brace before the end of the declaration. - // FIXME: This approach avoids rewriting the initializer expression. It - // also assumes there is only one declarator. For example, the following - // isn't currently supported by this routine (in general): - // - // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37; - // - const char *startInitializerBuf = SM->getCharacterData(startLoc); - const char *semiBuf = strchr(startInitializerBuf, ';'); - assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'"); - SourceLocation semiLoc = - startLoc.getLocWithOffset(semiBuf-startInitializerBuf); - InsertText(semiLoc, "}"); + const char separator = lastDecl ? ';' : ','; + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *separatorBuf = strchr(startInitializerBuf, separator); + assert((*separatorBuf == separator) && + "RewriteByRefVar: can't find ';' or ','"); + SourceLocation separatorLoc = + startLoc.getLocWithOffset(separatorBuf-startInitializerBuf); + + InsertText(separatorLoc, lastDecl ? "}" : "};\n"); } return; } @@ -5214,8 +5462,8 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast<ObjCBoolLiteralExpr>(S)) return RewriteObjCBoolLiteralExpr(BoolLitExpr); - if (ObjCNumericLiteral *NumericLitExpr = dyn_cast<ObjCNumericLiteral>(S)) - return RewriteObjCNumericLiteralExpr(NumericLitExpr); + if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(S)) + return RewriteObjCBoxedExpr(BoxedExpr); if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast<ObjCArrayLiteral>(S)) return RewriteObjCArrayLiteralExpr(ArrayLitExpr); @@ -5247,6 +5495,11 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { return RewriteMessageExpr(MessExpr); } + if (ObjCAutoreleasePoolStmt *StmtAutoRelease = + dyn_cast<ObjCAutoreleasePoolStmt>(S)) { + return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease); + } + if (ObjCAtTryStmt *StmtTry = dyn_cast<ObjCAtTryStmt>(S)) return RewriteObjCTryStmt(StmtTry); @@ -5300,7 +5553,7 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { assert(!BlockByRefDeclNo.count(ND) && "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; - RewriteByRefVar(VD); + RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE)); } else RewriteTypeOfDecl(VD); @@ -5402,7 +5655,6 @@ void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { // FIXME: If this should support Obj-C++, support CXXTryStmt if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { CurFunctionDef = FD; - CurFunctionDeclToDeclareForBlock = FD; CurrentBody = Body; Body = cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); @@ -5416,7 +5668,6 @@ void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { // and any copy/dispose helper functions. InsertBlockLiteralsWithinFunction(FD); CurFunctionDef = 0; - CurFunctionDeclToDeclareForBlock = 0; } break; } @@ -5515,7 +5766,7 @@ static void Write_ProtocolExprReferencedMetadata(ASTContext *Context, std::string &Result) { // Also output .objc_protorefs$B section and its meta-data. if (Context->getLangOpts().MicrosoftExt) - Result += "__declspec(allocate(\".objc_protorefs$B\")) "; + Result += "static "; Result += "struct _protocol_t *"; Result += "_OBJC_PROTOCOL_REFERENCE_$_"; Result += PDecl->getNameAsString(); @@ -5539,6 +5790,10 @@ void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { } InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) { ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i]; // Write struct declaration for the class matching its ivar declarations. @@ -5547,9 +5802,6 @@ void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { // private ivars. RewriteInterfaceDecl(CDecl); } - - if (ClassImplementation.size() || CategoryImplementation.size()) - RewriteImplementations(); // Get the buffer corresponding to MainFileID. If we haven't changed it, then // we are done. @@ -5605,7 +5857,6 @@ void RewriteModernObjC::Initialize(ASTContext &context) { Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n"; Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n"; Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_protorefs$B\", long, read, write)\n"; // These are generated but not necessary for functionality. Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n"; Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n"; @@ -5636,11 +5887,11 @@ void RewriteModernObjC::Initialize(ASTContext &context) { Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n"; Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass"; Preamble += "(const char *);\n"; Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; Preamble += "(struct objc_class *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass"; Preamble += "(const char *);\n"; Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n"; // @synchronized hooks. @@ -5723,11 +5974,20 @@ void RewriteModernObjC::Initialize(ASTContext &context) { Preamble += "\t arr[i] = va_arg(marker, void *);\n"; Preamble += "\tva_end( marker );\n"; Preamble += " };\n"; - Preamble += " __NSContainer_literal() {\n"; + Preamble += " ~__NSContainer_literal() {\n"; Preamble += "\tdelete[] arr;\n"; Preamble += " }\n"; Preamble += "};\n"; + // Declaration required for implementation of @autoreleasepool statement. + Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n"; + Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n"; + Preamble += "struct __AtAutoreleasePool {\n"; + Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n"; + Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n"; + Preamble += " void * atautoreleasepoolobj;\n"; + Preamble += "};\n"; + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long // as this avoids warning in any 64bit/32bit compilation model. Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; @@ -6738,20 +6998,20 @@ void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), PropEnd = IDecl->propimpl_end(); Prop != PropEnd; ++Prop) { - if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; - if (!(*Prop)->getPropertyIvarDecl()) + if (!Prop->getPropertyIvarDecl()) continue; - ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl(); + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - if (!Getter->isDefined()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) InstanceMethods.push_back(Getter); if (PD->isReadOnly()) continue; if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) - if (!Setter->isDefined()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) InstanceMethods.push_back(Setter); } @@ -7002,11 +7262,11 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), PropEnd = IDecl->propimpl_end(); Prop != PropEnd; ++Prop) { - if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; - if (!(*Prop)->getPropertyIvarDecl()) + if (!Prop->getPropertyIvarDecl()) continue; - ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl(); + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) @@ -7053,7 +7313,7 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, ClassProperties.push_back(*I); Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, - /* Container */0, + /* Container */IDecl, "_OBJC_$_PROP_LIST_", FullCategoryName); @@ -7189,7 +7449,7 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { if (BaseExpr->getType()->isObjCObjectPointerType()) { const ObjCInterfaceType *iFaceDecl = - dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); // lookup which class implements the instance variable. ObjCInterfaceDecl *clsDeclared = 0; @@ -7223,13 +7483,52 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { SourceLocation(), addExpr); QualType IvarT = D->getType(); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } convertObjCTypeToCStyleType(IvarT); QualType castT = Context->getPointerType(IvarT); - + castExpr = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, PE); + + Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, VK_LValue, OK_Ordinary, SourceLocation()); diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp index 9c0737f659a6..425cd77751ca 100644 --- a/lib/Rewrite/RewriteObjC.cpp +++ b/lib/Rewrite/RewriteObjC.cpp @@ -349,13 +349,18 @@ namespace { virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, std::string &Result) = 0; - // Misc. AST transformation routines. Somtimes they end up calling + // Misc. AST transformation routines. Sometimes they end up calling // rewriting routines on the new ASTs. CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc=SourceLocation(), SourceLocation EndLoc=SourceLocation()); - + CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation StartLoc=SourceLocation(), SourceLocation EndLoc=SourceLocation()); @@ -2592,7 +2597,7 @@ QualType RewriteObjC::getSuperStructType() { FieldTypes[i], 0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false)); + ICIS_NoInit)); } SuperStructDecl->completeDefinition(); @@ -2625,7 +2630,7 @@ QualType RewriteObjC::getConstantStringStructType() { FieldTypes[i], 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false)); + ICIS_NoInit)); } ConstantStringDecl->completeDefinition(); @@ -2633,6 +2638,40 @@ QualType RewriteObjC::getConstantStringStructType() { return Context->getTagDeclType(ConstantStringDecl); } +CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Create a reference to the objc_msgSend_stret() declaration. + DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, + false, msgSendType, + VK_LValue, SourceLocation()); + // Need to cast objc_msgSend_stret to "void *" (see above comment). + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, STDRE); + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + Method ? Method->isVariadic() : false); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], + MsgExprs.size(), + FT->getResultType(), VK_RValue, + SourceLocation()); + return STCE; + +} + + Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -3023,30 +3062,11 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, // call to objc_msgSend_stret and hang both varieties on a conditional // expression which dictate which one to envoke depending on size of // method's return type. - - // Create a reference to the objc_msgSend_stret() declaration. - DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, - false, msgSendType, - VK_LValue, SourceLocation()); - // Need to cast objc_msgSend_stret to "void *" (see above comment). - cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, STDRE); - // Now do the "normal" pointer to function cast. - castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : false); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); - - FT = msgSendType->getAs<FunctionType>(); - CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - SourceLocation()); + + CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); // Build sizeof(returnType) UnaryExprOrTypeTraitExpr *sizeofExpr = @@ -3887,7 +3907,7 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { &Context->Idents.get("FuncPtr"), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), FD->getType(), VK_LValue, OK_Ordinary); @@ -3936,7 +3956,7 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { &Context->Idents.get("__forwarding"), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, FD, SourceLocation(), FD->getType(), VK_LValue, @@ -3947,7 +3967,7 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { &Context->Idents.get(Name), Context->VoidPtrTy, 0, /*BitWidth=*/0, /*Mutable=*/true, - /*HasInit=*/false); + ICIS_NoInit); ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), DeclRefExp->getType(), VK_LValue, OK_Ordinary); @@ -5442,10 +5462,10 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe IVE = CDecl->ivar_end(); } Result += "\t,{{\""; - Result += (*IVI)->getNameAsString(); + Result += IVI->getNameAsString(); Result += "\", \""; std::string TmpString, StrEncoding; - Context->getObjCEncodingForType((*IVI)->getType(), TmpString, *IVI); + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); QuoteDoublequotes(TmpString, StrEncoding); Result += StrEncoding; Result += "\", "; @@ -5453,14 +5473,14 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe Result += "}\n"; for (++IVI; IVI != IVE; ++IVI) { Result += "\t ,{\""; - Result += (*IVI)->getNameAsString(); + Result += IVI->getNameAsString(); Result += "\", \""; std::string TmpString, StrEncoding; - Context->getObjCEncodingForType((*IVI)->getType(), TmpString, *IVI); + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); QuoteDoublequotes(TmpString, StrEncoding); Result += StrEncoding; Result += "\", "; - RewriteIvarOffsetComputation((*IVI), Result); + RewriteIvarOffsetComputation(*IVI, Result); Result += "}\n"; } @@ -5476,11 +5496,11 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), PropEnd = IDecl->propimpl_end(); Prop != PropEnd; ++Prop) { - if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; - if (!(*Prop)->getPropertyIvarDecl()) + if (!Prop->getPropertyIvarDecl()) continue; - ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl(); + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) @@ -5761,11 +5781,11 @@ void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *ID for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), PropEnd = IDecl->propimpl_end(); Prop != PropEnd; ++Prop) { - if ((*Prop)->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; - if (!(*Prop)->getPropertyIvarDecl()) + if (!Prop->getPropertyIvarDecl()) continue; - ObjCPropertyDecl *PD = (*Prop)->getPropertyDecl(); + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) @@ -6015,4 +6035,3 @@ Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { ReplaceStmtWithRange(IV, Replacement, OldRange); return Replacement; } - diff --git a/lib/Rewrite/Rewriter.cpp b/lib/Rewrite/Rewriter.cpp index 43fb01bb1c34..7c27114f1cfd 100644 --- a/lib/Rewrite/Rewriter.cpp +++ b/lib/Rewrite/Rewriter.cpp @@ -15,9 +15,12 @@ #include "clang/Rewrite/Rewriter.h" #include "clang/AST/Stmt.h" #include "clang/AST/Decl.h" -#include "clang/Lex/Lexer.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" using namespace clang; raw_ostream &RewriteBuffer::write(raw_ostream &os) const { @@ -27,7 +30,7 @@ raw_ostream &RewriteBuffer::write(raw_ostream &os) const { } /// \brief Return true if this character is non-new-line whitespace: -/// ' ', '\t', '\f', '\v', '\r'. +/// ' ', '\\t', '\\f', '\\v', '\\r'. static inline bool isWhitespace(unsigned char c) { switch (c) { case ' ': @@ -412,3 +415,72 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, return false; } + +// A wrapper for a file stream that atomically overwrites the target. +// +// Creates a file output stream for a temporary file in the constructor, +// which is later accessible via getStream() if ok() return true. +// Flushes the stream and moves the temporary file to the target location +// in the destructor. +class AtomicallyMovedFile { +public: + AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename, + bool &AllWritten) + : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) { + TempFilename = Filename; + TempFilename += "-%%%%%%%%"; + int FD; + if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename, + /*makeAbsolute=*/true, 0664)) { + AllWritten = false; + Diagnostics.Report(clang::diag::err_unable_to_make_temp) + << TempFilename; + } else { + FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true)); + } + } + + ~AtomicallyMovedFile() { + if (!ok()) return; + + FileStream->flush(); +#ifdef _WIN32 + // Win32 does not allow rename/removing opened files. + FileStream.reset(); +#endif + if (llvm::error_code ec = + llvm::sys::fs::rename(TempFilename.str(), Filename)) { + AllWritten = false; + Diagnostics.Report(clang::diag::err_unable_to_rename_temp) + << TempFilename << Filename << ec.message(); + bool existed; + // If the remove fails, there's not a lot we can do - this is already an + // error. + llvm::sys::fs::remove(TempFilename.str(), existed); + } + } + + bool ok() { return FileStream; } + llvm::raw_ostream &getStream() { return *FileStream; } + +private: + DiagnosticsEngine &Diagnostics; + StringRef Filename; + SmallString<128> TempFilename; + OwningPtr<llvm::raw_fd_ostream> FileStream; + bool &AllWritten; +}; + +bool Rewriter::overwriteChangedFiles() { + bool AllWritten = true; + for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { + const FileEntry *Entry = + getSourceMgr().getFileEntryForID(I->first); + AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(), + AllWritten); + if (File.ok()) { + I->second.write(File.getStream()); + } + } + return !AllWritten; +} diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index a8e679132c74..19a7d6f35c8f 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceLocation.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Lexer.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprObjC.h" @@ -27,6 +28,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/Analyses/ReachableCode.h" @@ -42,7 +44,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include <algorithm> +#include <iterator> #include <vector> +#include <deque> using namespace clang; @@ -185,6 +189,12 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { continue; } } + if (isa<MSAsmStmt>(S)) { + // TODO: Verify this is correct. + HasFakeEdge = true; + HasLiveReturn = true; + continue; + } if (isa<CXXTryStmt>(S)) { HasAbnormalEdge = true; continue; @@ -438,9 +448,14 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { return false; // Suggest possible initialization (if any). - const char *Init = S.getFixItZeroInitializerForType(VariableTy); - if (!Init) + std::string Init = S.getFixItZeroInitializerForType(VariableTy); + if (Init.empty()) return false; + + // Don't suggest a fixit inside macros. + if (VD->getLocEnd().isMacroID()) + return false; + SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd()); S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() @@ -448,82 +463,428 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { return true; } +/// Create a fixit to remove an if-like statement, on the assumption that its +/// condition is CondVal. +static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then, + const Stmt *Else, bool CondVal, + FixItHint &Fixit1, FixItHint &Fixit2) { + if (CondVal) { + // If condition is always true, remove all but the 'then'. + Fixit1 = FixItHint::CreateRemoval( + CharSourceRange::getCharRange(If->getLocStart(), + Then->getLocStart())); + if (Else) { + SourceLocation ElseKwLoc = Lexer::getLocForEndOfToken( + Then->getLocEnd(), 0, S.getSourceManager(), S.getLangOpts()); + Fixit2 = FixItHint::CreateRemoval( + SourceRange(ElseKwLoc, Else->getLocEnd())); + } + } else { + // If condition is always false, remove all but the 'else'. + if (Else) + Fixit1 = FixItHint::CreateRemoval( + CharSourceRange::getCharRange(If->getLocStart(), + Else->getLocStart())); + else + Fixit1 = FixItHint::CreateRemoval(If->getSourceRange()); + } +} + +/// DiagUninitUse -- Helper function to produce a diagnostic for an +/// uninitialized use of a variable. +static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, + bool IsCapturedByBlock) { + bool Diagnosed = false; + + // Diagnose each branch which leads to a sometimes-uninitialized use. + for (UninitUse::branch_iterator I = Use.branch_begin(), E = Use.branch_end(); + I != E; ++I) { + assert(Use.getKind() == UninitUse::Sometimes); + + const Expr *User = Use.getUser(); + const Stmt *Term = I->Terminator; + + // Information used when building the diagnostic. + unsigned DiagKind; + const char *Str; + SourceRange Range; + + // FixIts to suppress the diagnosic by removing the dead condition. + // For all binary terminators, branch 0 is taken if the condition is true, + // and branch 1 is taken if the condition is false. + int RemoveDiagKind = -1; + const char *FixitStr = + S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false") + : (I->Output ? "1" : "0"); + FixItHint Fixit1, Fixit2; + + switch (Term->getStmtClass()) { + default: + // Don't know how to report this. Just fall back to 'may be used + // uninitialized'. This happens for range-based for, which the user + // can't explicitly fix. + // FIXME: This also happens if the first use of a variable is always + // uninitialized, eg "for (int n; n < 10; ++n)". We should report that + // with the 'is uninitialized' diagnostic. + continue; + + // "condition is true / condition is false". + case Stmt::IfStmtClass: { + const IfStmt *IS = cast<IfStmt>(Term); + DiagKind = 0; + Str = "if"; + Range = IS->getCond()->getSourceRange(); + RemoveDiagKind = 0; + CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), + I->Output, Fixit1, Fixit2); + break; + } + case Stmt::ConditionalOperatorClass: { + const ConditionalOperator *CO = cast<ConditionalOperator>(Term); + DiagKind = 0; + Str = "?:"; + Range = CO->getCond()->getSourceRange(); + RemoveDiagKind = 0; + CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), + I->Output, Fixit1, Fixit2); + break; + } + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BO = cast<BinaryOperator>(Term); + if (!BO->isLogicalOp()) + continue; + DiagKind = 0; + Str = BO->getOpcodeStr(); + Range = BO->getLHS()->getSourceRange(); + RemoveDiagKind = 0; + if ((BO->getOpcode() == BO_LAnd && I->Output) || + (BO->getOpcode() == BO_LOr && !I->Output)) + // true && y -> y, false || y -> y. + Fixit1 = FixItHint::CreateRemoval(SourceRange(BO->getLocStart(), + BO->getOperatorLoc())); + else + // false && y -> false, true || y -> true. + Fixit1 = FixItHint::CreateReplacement(BO->getSourceRange(), FixitStr); + break; + } + + // "loop is entered / loop is exited". + case Stmt::WhileStmtClass: + DiagKind = 1; + Str = "while"; + Range = cast<WhileStmt>(Term)->getCond()->getSourceRange(); + RemoveDiagKind = 1; + Fixit1 = FixItHint::CreateReplacement(Range, FixitStr); + break; + case Stmt::ForStmtClass: + DiagKind = 1; + Str = "for"; + Range = cast<ForStmt>(Term)->getCond()->getSourceRange(); + RemoveDiagKind = 1; + if (I->Output) + Fixit1 = FixItHint::CreateRemoval(Range); + else + Fixit1 = FixItHint::CreateReplacement(Range, FixitStr); + break; + + // "condition is true / loop is exited". + case Stmt::DoStmtClass: + DiagKind = 2; + Str = "do"; + Range = cast<DoStmt>(Term)->getCond()->getSourceRange(); + RemoveDiagKind = 1; + Fixit1 = FixItHint::CreateReplacement(Range, FixitStr); + break; + + // "switch case is taken". + case Stmt::CaseStmtClass: + DiagKind = 3; + Str = "case"; + Range = cast<CaseStmt>(Term)->getLHS()->getSourceRange(); + break; + case Stmt::DefaultStmtClass: + DiagKind = 3; + Str = "default"; + Range = cast<DefaultStmt>(Term)->getDefaultLoc(); + break; + } + + S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var) + << VD->getDeclName() << IsCapturedByBlock << DiagKind + << Str << I->Output << Range; + S.Diag(User->getLocStart(), diag::note_uninit_var_use) + << IsCapturedByBlock << User->getSourceRange(); + if (RemoveDiagKind != -1) + S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond) + << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; + + Diagnosed = true; + } + + if (!Diagnosed) + S.Diag(Use.getUser()->getLocStart(), + Use.getKind() == UninitUse::Always ? diag::warn_uninit_var + : diag::warn_maybe_uninit_var) + << VD->getDeclName() << IsCapturedByBlock + << Use.getUser()->getSourceRange(); +} + /// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an /// uninitialized variable. This manages the different forms of diagnostic /// emitted for particular types of uses. Returns true if the use was diagnosed -/// as a warning. If a pariticular use is one we omit warnings for, returns +/// as a warning. If a particular use is one we omit warnings for, returns /// false. static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD, - const Expr *E, bool isAlwaysUninit, + const UninitUse &Use, bool alwaysReportSelfInit = false) { - bool isSelfInit = false; - - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { - if (isAlwaysUninit) { - // Inspect the initializer of the variable declaration which is - // being referenced prior to its initialization. We emit - // specialized diagnostics for self-initialization, and we - // specifically avoid warning about self references which take the - // form of: - // - // int x = x; - // - // This is used to indicate to GCC that 'x' is intentionally left - // uninitialized. Proven code paths which access 'x' in - // an uninitialized state after this will still warn. - // - // TODO: Should we suppress maybe-uninitialized warnings for - // variables initialized in this way? - if (const Expr *Initializer = VD->getInit()) { - if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts()) - return false; - - ContainsReference CR(S.Context, DRE); - CR.Visit(const_cast<Expr*>(Initializer)); - isSelfInit = CR.doesContainReference(); - } - if (isSelfInit) { + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Use.getUser())) { + // Inspect the initializer of the variable declaration which is + // being referenced prior to its initialization. We emit + // specialized diagnostics for self-initialization, and we + // specifically avoid warning about self references which take the + // form of: + // + // int x = x; + // + // This is used to indicate to GCC that 'x' is intentionally left + // uninitialized. Proven code paths which access 'x' in + // an uninitialized state after this will still warn. + if (const Expr *Initializer = VD->getInit()) { + if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts()) + return false; + + ContainsReference CR(S.Context, DRE); + CR.Visit(const_cast<Expr*>(Initializer)); + if (CR.doesContainReference()) { S.Diag(DRE->getLocStart(), diag::warn_uninit_self_reference_in_init) - << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange(); - } else { - S.Diag(DRE->getLocStart(), diag::warn_uninit_var) - << VD->getDeclName() << DRE->getSourceRange(); + << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange(); + return true; } - } else { - S.Diag(DRE->getLocStart(), diag::warn_maybe_uninit_var) - << VD->getDeclName() << DRE->getSourceRange(); } + + DiagUninitUse(S, VD, Use, false); } else { - const BlockExpr *BE = cast<BlockExpr>(E); - if (VD->getType()->isBlockPointerType() && - !VD->hasAttr<BlocksAttr>()) - S.Diag(BE->getLocStart(), diag::warn_uninit_byref_blockvar_captured_by_block) - << VD->getDeclName(); - else + const BlockExpr *BE = cast<BlockExpr>(Use.getUser()); + if (VD->getType()->isBlockPointerType() && !VD->hasAttr<BlocksAttr>()) S.Diag(BE->getLocStart(), - isAlwaysUninit ? diag::warn_uninit_var_captured_by_block - : diag::warn_maybe_uninit_var_captured_by_block) + diag::warn_uninit_byref_blockvar_captured_by_block) << VD->getDeclName(); + else + DiagUninitUse(S, VD, Use, true); } // Report where the variable was declared when the use wasn't within // the initializer of that declaration & we didn't already suggest // an initialization fixit. - if (!isSelfInit && !SuggestInitializationFixit(S, VD)) + if (!SuggestInitializationFixit(S, VD)) S.Diag(VD->getLocStart(), diag::note_uninit_var_def) << VD->getDeclName(); return true; } -typedef std::pair<const Expr*, bool> UninitUse; +namespace { + class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> { + public: + FallthroughMapper(Sema &S) + : FoundSwitchStatements(false), + S(S) { + } + + bool foundSwitchStatements() const { return FoundSwitchStatements; } + + void markFallthroughVisited(const AttributedStmt *Stmt) { + bool Found = FallthroughStmts.erase(Stmt); + assert(Found); + (void)Found; + } + + typedef llvm::SmallPtrSet<const AttributedStmt*, 8> AttrStmts; + + const AttrStmts &getFallthroughStmts() const { + return FallthroughStmts; + } + + bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt) { + int UnannotatedCnt = 0; + AnnotatedCnt = 0; + + std::deque<const CFGBlock*> BlockQueue; + + std::copy(B.pred_begin(), B.pred_end(), std::back_inserter(BlockQueue)); + + while (!BlockQueue.empty()) { + const CFGBlock *P = BlockQueue.front(); + BlockQueue.pop_front(); + + const Stmt *Term = P->getTerminator(); + if (Term && isa<SwitchStmt>(Term)) + continue; // Switch statement, good. + + const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel()); + if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end()) + continue; // Previous case label has no statements, good. + + if (P->pred_begin() == P->pred_end()) { // The block is unreachable. + // This only catches trivially unreachable blocks. + for (CFGBlock::const_iterator ElIt = P->begin(), ElEnd = P->end(); + ElIt != ElEnd; ++ElIt) { + if (const CFGStmt *CS = ElIt->getAs<CFGStmt>()){ + if (const AttributedStmt *AS = asFallThroughAttr(CS->getStmt())) { + S.Diag(AS->getLocStart(), + diag::warn_fallthrough_attr_unreachable); + markFallthroughVisited(AS); + ++AnnotatedCnt; + } + // Don't care about other unreachable statements. + } + } + // If there are no unreachable statements, this may be a special + // case in CFG: + // case X: { + // A a; // A has a destructor. + // break; + // } + // // <<<< This place is represented by a 'hanging' CFG block. + // case Y: + continue; + } + + const Stmt *LastStmt = getLastStmt(*P); + if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { + markFallthroughVisited(AS); + ++AnnotatedCnt; + continue; // Fallthrough annotation, good. + } + + if (!LastStmt) { // This block contains no executable statements. + // Traverse its predecessors. + std::copy(P->pred_begin(), P->pred_end(), + std::back_inserter(BlockQueue)); + continue; + } + + ++UnannotatedCnt; + } + return !!UnannotatedCnt; + } + + // RecursiveASTVisitor setup. + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitAttributedStmt(AttributedStmt *S) { + if (asFallThroughAttr(S)) + FallthroughStmts.insert(S); + return true; + } + + bool VisitSwitchStmt(SwitchStmt *S) { + FoundSwitchStatements = true; + return true; + } + + private: + + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { + if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) + return AS; + } + return 0; + } + + static const Stmt *getLastStmt(const CFGBlock &B) { + if (const Stmt *Term = B.getTerminator()) + return Term; + for (CFGBlock::const_reverse_iterator ElemIt = B.rbegin(), + ElemEnd = B.rend(); + ElemIt != ElemEnd; ++ElemIt) { + if (const CFGStmt *CS = ElemIt->getAs<CFGStmt>()) + return CS->getStmt(); + } + // Workaround to detect a statement thrown out by CFGBuilder: + // case X: {} case Y: + // case X: ; case Y: + if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) + if (!isa<SwitchCase>(SW->getSubStmt())) + return SW->getSubStmt(); + + return 0; + } + + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + }; +} + +static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC, + bool PerFunction) { + FallthroughMapper FM(S); + FM.TraverseStmt(AC.getBody()); + + if (!FM.foundSwitchStatements()) + return; + + if (PerFunction && FM.getFallthroughStmts().empty()) + return; + + CFG *Cfg = AC.getCFG(); + + if (!Cfg) + return; + + int AnnotatedCnt; + + for (CFG::reverse_iterator I = Cfg->rbegin(), E = Cfg->rend(); I != E; ++I) { + const CFGBlock &B = **I; + const Stmt *Label = B.getLabel(); + + if (!Label || !isa<SwitchCase>(Label)) + continue; + + if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt)) + continue; + + S.Diag(Label->getLocStart(), + PerFunction ? diag::warn_unannotated_fallthrough_per_function + : diag::warn_unannotated_fallthrough); + + if (!AnnotatedCnt) { + SourceLocation L = Label->getLocStart(); + if (L.isMacroID()) + continue; + if (S.getLangOpts().CPlusPlus0x) { + const Stmt *Term = B.getTerminator(); + if (!(B.empty() && Term && isa<BreakStmt>(Term))) { + S.Diag(L, diag::note_insert_fallthrough_fixit) << + FixItHint::CreateInsertion(L, "[[clang::fallthrough]]; "); + } + } + S.Diag(L, diag::note_insert_break_fixit) << + FixItHint::CreateInsertion(L, "break; "); + } + } + + const FallthroughMapper::AttrStmts &Fallthroughs = FM.getFallthroughStmts(); + for (FallthroughMapper::AttrStmts::const_iterator I = Fallthroughs.begin(), + E = Fallthroughs.end(); + I != E; ++I) { + S.Diag((*I)->getLocStart(), diag::warn_fallthrough_attr_invalid_placement); + } + +} namespace { struct SLocSort { bool operator()(const UninitUse &a, const UninitUse &b) { - SourceLocation aLoc = a.first->getLocStart(); - SourceLocation bLoc = b.first->getLocStart(); + // Prefer a more confident report over a less confident one. + if (a.getKind() != b.getKind()) + return a.getKind() > b.getKind(); + SourceLocation aLoc = a.getUser()->getLocStart(); + SourceLocation bLoc = b.getUser()->getLocStart(); return aLoc.getRawEncoding() < bLoc.getRawEncoding(); } }; @@ -552,9 +913,8 @@ public: return V; } - void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd, - bool isAlwaysUninit) { - getUses(vd).first->push_back(std::make_pair(ex, isAlwaysUninit)); + void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) { + getUses(vd).first->push_back(use); } void handleSelfInit(const VarDecl *vd) { @@ -565,6 +925,8 @@ public: if (!uses) return; + // FIXME: This iteration order, and thus the resulting diagnostic order, + // is nondeterministic. for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) { const VarDecl *vd = i->first; const UsesMap::mapped_type &V = i->second; @@ -576,8 +938,9 @@ public: // variable, but the root cause is an idiomatic self-init. We want // to report the diagnostic at the self-init since that is the root cause. if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec)) - DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(), - /* isAlwaysUninit */ true, + DiagnoseUninitializedUse(S, vd, + UninitUse(vd->getInit()->IgnoreParenCasts(), + /* isAlwaysUninit */ true), /* alwaysReportSelfInit */ true); else { // Sort the uses by their SourceLocations. While not strictly @@ -587,8 +950,10 @@ public: for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve; ++vi) { - if (DiagnoseUninitializedUse(S, vd, vi->first, - /*isAlwaysUninit=*/vi->second)) + // If we have self-init, downgrade all uses to 'may be uninitialized'. + UninitUse Use = hasSelfInit ? UninitUse(vi->getUser(), false) : *vi; + + if (DiagnoseUninitializedUse(S, vd, Use)) // Skip further diagnostics for this variable. We try to warn only // on the first point at which a variable is used uninitialized. break; @@ -604,7 +969,7 @@ public: private: static bool hasAlwaysUninitializedUse(const UsesVec* vec) { for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) { - if (i->second) { + if (i->getKind() == UninitUse::Always) { return true; } } @@ -696,6 +1061,9 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { case LEK_LockedAtEndOfFunction: DiagID = diag::warn_no_unlock; break; + case LEK_NotLockedAtEndOfFunction: + DiagID = diag::warn_expecting_locked; + break; } if (LocEndOfScope.isInvalid()) LocEndOfScope = FunEndLocation; @@ -830,7 +1198,7 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, const Stmt *Body = D->getBody(); assert(Body); - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D, 0); + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D); // Don't generate EH edges for CallExprs as we'd like to avoid the n^2 // explosion for destrutors that can result and the compile time hit. @@ -852,11 +1220,13 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, else { AC.getCFGBuildOptions() .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) .setAlwaysAdd(Stmt::BlockExprClass) .setAlwaysAdd(Stmt::CStyleCastExprClass) .setAlwaysAdd(Stmt::DeclRefExprClass) .setAlwaysAdd(Stmt::ImplicitCastExprClass) - .setAlwaysAdd(Stmt::UnaryOperatorClass); + .setAlwaysAdd(Stmt::UnaryOperatorClass) + .setAlwaysAdd(Stmt::AttributedStmtClass); } // Construct the analysis context with the specified CFG build options. @@ -945,6 +1315,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart()) != DiagnosticsEngine::Ignored || + Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart()) + != DiagnosticsEngine::Ignored || Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart()) != DiagnosticsEngine::Ignored) { if (CFG *cfg = AC.getCFG()) { @@ -968,6 +1340,16 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, } } + bool FallThroughDiagFull = + Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough, + D->getLocStart()) != DiagnosticsEngine::Ignored; + bool FallThroughDiagPerFunction = + Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough_per_function, + D->getLocStart()) != DiagnosticsEngine::Ignored; + if (FallThroughDiagFull || FallThroughDiagPerFunction) { + DiagnoseSwitchLabelsFallthrough(S, AC, !FallThroughDiagFull); + } + // Collect statistics about the CFG if it was built. if (S.CollectStats && AC.isCFGBuilt()) { ++NumFunctionsAnalyzed; diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index f142ab4c0a32..0f209fd7d623 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -12,9 +12,11 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/AttributeList.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/SmallString.h" using namespace clang; size_t AttributeList::allocated_size() const { @@ -94,10 +96,15 @@ AttributePool::createIntegerAttribute(ASTContext &C, IdentifierInfo *Name, SourceLocation TokLoc, int Arg) { Expr *IArg = IntegerLiteral::Create(C, llvm::APInt(32, (uint64_t) Arg), C.IntTy, TokLoc); - return create(Name, TokLoc, 0, TokLoc, 0, TokLoc, &IArg, 1, 0); + return create(Name, TokLoc, 0, TokLoc, 0, TokLoc, &IArg, 1, + AttributeList::AS_GNU); } -AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { +#include "clang/Sema/AttrParsedAttrKinds.inc" + +AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name, + const IdentifierInfo *ScopeName, + Syntax SyntaxUsed) { StringRef AttrName = Name->getName(); // Normalize the attribute name, __foo__ becomes foo. @@ -105,22 +112,14 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { AttrName.size() >= 4) AttrName = AttrName.substr(2, AttrName.size() - 4); - return llvm::StringSwitch<AttributeList::Kind>(AttrName) - #include "clang/Sema/AttrParsedAttrKinds.inc" - .Case("address_space", AT_address_space) - .Case("align", AT_aligned) // FIXME - should it be "aligned"? - .Case("base_check", AT_base_check) - .Case("bounded", IgnoredAttribute) // OpenBSD - .Case("__const", AT_const) // some GCC headers do contain this spelling - .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased) - .Case("mode", AT_mode) - .Case("vec_type_hint", IgnoredAttribute) - .Case("ext_vector_type", AT_ext_vector_type) - .Case("neon_vector_type", AT_neon_vector_type) - .Case("neon_polyvector_type", AT_neon_polyvector_type) - .Case("opencl_image_access", AT_opencl_image_access) - .Case("objc_gc", AT_objc_gc) - .Case("objc_ownership", AT_objc_ownership) - .Case("vector_size", AT_vector_size) - .Default(UnknownAttribute); + SmallString<64> Buf; + if (ScopeName) + Buf += ScopeName->getName(); + // Ensure that in the case of C++11 attributes, we look for '::foo' if it is + // unscoped. + if (ScopeName || SyntaxUsed == AS_CXX11) + Buf += "::"; + Buf += AttrName; + + return ::getAttrKind(Buf); } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 07734c7c7b57..46dfa05adec1 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -1,9 +1,8 @@ -set(LLVM_USED_LIBS - clangAST - clangAnalysis - clangBasic - clangEdit - clangLex +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + support + mc ) add_clang_library(clangSema @@ -50,9 +49,27 @@ add_clang_library(clangSema TargetAttributesSema.cpp ) -add_dependencies(clangSema ClangARMNeon ClangAttrClasses ClangAttrList - ClangDiagnosticSema ClangDeclNodes ClangStmtNodes - ClangAttrTemplateInstantiate ClangAttrParsedAttrList - ClangAttrParsedAttrKinds) - +add_dependencies(clangSema + ClangARMNeon + ClangAttrClasses + ClangAttrList + ClangAttrParsedAttrList + ClangAttrParsedAttrKinds + ClangAttrTemplateInstantiate + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticAST + ClangDiagnosticComment + ClangDiagnosticCommon + ClangDiagnosticParse + ClangDiagnosticSema + ClangStmtNodes + ) +target_link_libraries(clangSema + clangAST + clangAnalysis + clangBasic + clangEdit + clangLex + ) diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index ce9bbb9238a4..a8357255343d 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -194,10 +194,11 @@ CodeCompletionString::CodeCompletionString(const Chunk *Chunks, const char **Annotations, unsigned NumAnnotations, CXCursorKind ParentKind, - StringRef ParentName) + StringRef ParentName, + const char *BriefComment) : NumChunks(NumChunks), NumAnnotations(NumAnnotations), Priority(Priority), Availability(Availability), ParentKind(ParentKind), - ParentName(ParentName) + ParentName(ParentName), BriefComment(BriefComment) { assert(NumChunks <= 0xffff); assert(NumAnnotations <= 0xffff); @@ -338,7 +339,7 @@ CodeCompletionString *CodeCompletionBuilder::TakeString() { = new (Mem) CodeCompletionString(Chunks.data(), Chunks.size(), Priority, Availability, Annotations.data(), Annotations.size(), - ParentKind, ParentName); + ParentKind, ParentName, BriefComment); Chunks.clear(); return Result; } @@ -394,6 +395,10 @@ void CodeCompletionBuilder::addParentContext(DeclContext *DC) { ParentName = getCodeCompletionTUInfo().getParentName(DC); } +void CodeCompletionBuilder::addBriefComment(StringRef Comment) { + BriefComment = Allocator.CopyString(Comment); +} + unsigned CodeCompletionResult::getPriorityFromDecl(NamedDecl *ND) { if (!ND) return CCP_Unlikely; @@ -474,8 +479,11 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, OS << " (Hidden)"; if (CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef, getAllocator(), - CCTUInfo)) { + CCTUInfo, + includeBriefComments())) { OS << " : " << CCS->getAsString(); + if (const char *BriefComment = CCS->getBriefComment()) + OS << " : " << BriefComment; } OS << '\n'; @@ -489,7 +497,8 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, OS << Results[I].Macro->getName(); if (CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef, getAllocator(), - CCTUInfo)) { + CCTUInfo, + includeBriefComments())) { OS << " : " << CCS->getAsString(); } OS << '\n'; @@ -573,14 +582,8 @@ void CodeCompletionResult::computeCursorKindAndAvailability(bool Accessible) { } case RK_Macro: - Availability = CXAvailability_Available; - CursorKind = CXCursor_MacroDefinition; - break; - case RK_Keyword: - Availability = CXAvailability_Available; - CursorKind = CXCursor_NotImplemented; - break; + llvm_unreachable("Macro and keyword kinds are handled by the constructors"); } if (!Accessible) diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index b531accf868a..d12ca7839095 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -145,6 +145,7 @@ CXXScopeSpec::getWithLocInContext(ASTContext &Context) const { /// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function. /// "TheDeclarator" is the declarator that this will be added to. DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, + bool isAmbiguous, SourceLocation EllipsisLoc, ParamInfo *ArgInfo, unsigned NumArgs, @@ -165,7 +166,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, SourceLocation LocalRangeBegin, SourceLocation LocalRangeEnd, Declarator &TheDeclarator, - ParsedType TrailingReturnType) { + TypeResult TrailingReturnType) { DeclaratorChunk I; I.Kind = Function; I.Loc = LocalRangeBegin; @@ -173,6 +174,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, I.Fun.AttrList = 0; I.Fun.hasPrototype = hasProto; I.Fun.isVariadic = isVariadic; + I.Fun.isAmbiguous = isAmbiguous; I.Fun.EllipsisLoc = EllipsisLoc.getRawEncoding(); I.Fun.DeleteArgInfo = false; I.Fun.TypeQuals = TypeQuals; @@ -188,7 +190,9 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, I.Fun.NumExceptions = 0; I.Fun.Exceptions = 0; I.Fun.NoexceptExpr = 0; - I.Fun.TrailingReturnType = TrailingReturnType.getAsOpaquePtr(); + I.Fun.HasTrailingReturnType = TrailingReturnType.isUsable() || + TrailingReturnType.isInvalid(); + I.Fun.TrailingReturnType = TrailingReturnType.get(); // new[] an argument array if needed. if (NumArgs) { @@ -418,19 +422,27 @@ const char *DeclSpec::getSpecifierName(TQ T) { bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - // OpenCL 1.1 6.8g: "The extern, static, auto and register storage-class - // specifiers are not supported." + // OpenCL v1.1 s6.8g: "The extern, static, auto and register storage-class + // specifiers are not supported. // It seems sensible to prohibit private_extern too // The cl_clang_storage_class_specifiers extension enables support for // these storage-class specifiers. + // OpenCL v1.2 s6.8 changes this to "The auto and register storage-class + // specifiers are not supported." if (S.getLangOpts().OpenCL && !S.getOpenCLOptions().cl_clang_storage_class_specifiers) { switch (SC) { case SCS_extern: case SCS_private_extern: + case SCS_static: + if (S.getLangOpts().OpenCLVersion < 120) { + DiagID = diag::err_not_opencl_storage_class_specifier; + PrevSpec = getSpecifierName(SC); + return true; + } + break; case SCS_auto: case SCS_register: - case SCS_static: DiagID = diag::err_not_opencl_storage_class_specifier; PrevSpec = getSpecifierName(SC); return true; @@ -658,9 +670,11 @@ bool DeclSpec::SetTypeSpecError() { } bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec, - unsigned &DiagID, const LangOptions &Lang) { - // Duplicates turn into warnings pre-C99. - if ((TypeQualifiers & T) && !Lang.C99) + unsigned &DiagID, const LangOptions &Lang, + bool IsTypeSpec) { + // Duplicates are permitted in C99, and are permitted in C++11 unless the + // cv-qualifier appears as a type-specifier. + if ((TypeQualifiers & T) && !Lang.C99 && (!Lang.CPlusPlus0x || IsTypeSpec)) return BadSpecifier(T, T, PrevSpec, DiagID); TypeQualifiers |= T; @@ -751,7 +765,7 @@ void DeclSpec::SaveWrittenBuiltinSpecs() { writtenBS.ModeAttr = false; AttributeList* attrs = getAttributes().getList(); while (attrs) { - if (attrs->getKind() == AttributeList::AT_mode) { + if (attrs->getKind() == AttributeList::AT_Mode) { writtenBS.ModeAttr = true; break; } @@ -935,13 +949,6 @@ bool DeclSpec::isMissingDeclaratorOk() { StorageClassSpec != DeclSpec::SCS_typedef; } -void UnqualifiedId::clear() { - Kind = IK_Identifier; - Identifier = 0; - StartLocation = SourceLocation(); - EndLocation = SourceLocation(); -} - void UnqualifiedId::setOperatorFunctionId(SourceLocation OperatorLoc, OverloadedOperatorKind Op, SourceLocation SymbolLocations[3]) { diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 30a9cd751a66..7f79f0c6d98a 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/APFloat.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "clang/Sema/CXXFieldCollector.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/ExternalSemaSource.h" @@ -29,6 +30,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclFriend.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -90,13 +92,13 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, PackContext(0), MSStructPragmaOn(false), VisContext(0), ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0), IdResolver(pp), StdInitializerList(0), CXXTypeInfoDecl(0), MSVCGuidDecl(0), - NSNumberDecl(0), NSArrayDecl(0), ArrayWithObjectsMethod(0), + NSNumberDecl(0), + NSStringDecl(0), StringWithUTF8StringMethod(0), + NSArrayDecl(0), ArrayWithObjectsMethod(0), NSDictionaryDecl(0), DictionaryWithObjectsMethod(0), GlobalNewDeleteDeclared(false), - ObjCShouldCallSuperDealloc(false), - ObjCShouldCallSuperFinalize(false), TUKind(TUKind), - NumSFINAEErrors(0), InFunctionDeclarator(0), SuppressAccessChecking(false), + NumSFINAEErrors(0), InFunctionDeclarator(0), AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(0), TyposCorrected(0), @@ -176,6 +178,10 @@ void Sema::Initialize() { if (IdResolver.begin(Protocol) == IdResolver.end()) PushOnScopeChains(Context.getObjCProtocolDecl(), TUScope); } + + DeclarationName BuiltinVaList = &Context.Idents.get("__builtin_va_list"); + if (IdResolver.begin(BuiltinVaList) == IdResolver.end()) + PushOnScopeChains(Context.getBuiltinVaListDecl(), TUScope); } Sema::~Sema() { @@ -199,7 +205,6 @@ Sema::~Sema() { ExternalSema->ForgetSema(); } - /// makeUnavailableInSystemHeader - There is an error in the current /// context. If we're still in a system header, and we can plausibly /// make the relevant declaration unavailable instead of erroring, do @@ -420,10 +425,88 @@ void Sema::LoadExternalWeakUndeclaredIdentifiers() { } } + +typedef llvm::DenseMap<const CXXRecordDecl*, bool> RecordCompleteMap; + +/// \brief Returns true, if all methods and nested classes of the given +/// CXXRecordDecl are defined in this translation unit. +/// +/// Should only be called from ActOnEndOfTranslationUnit so that all +/// definitions are actually read. +static bool MethodsAndNestedClassesComplete(const CXXRecordDecl *RD, + RecordCompleteMap &MNCComplete) { + RecordCompleteMap::iterator Cache = MNCComplete.find(RD); + if (Cache != MNCComplete.end()) + return Cache->second; + if (!RD->isCompleteDefinition()) + return false; + bool Complete = true; + for (DeclContext::decl_iterator I = RD->decls_begin(), + E = RD->decls_end(); + I != E && Complete; ++I) { + if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I)) + Complete = M->isDefined() || (M->isPure() && !isa<CXXDestructorDecl>(M)); + else if (const FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(*I)) + Complete = F->getTemplatedDecl()->isDefined(); + else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) { + if (R->isInjectedClassName()) + continue; + if (R->hasDefinition()) + Complete = MethodsAndNestedClassesComplete(R->getDefinition(), + MNCComplete); + else + Complete = false; + } + } + MNCComplete[RD] = Complete; + return Complete; +} + +/// \brief Returns true, if the given CXXRecordDecl is fully defined in this +/// translation unit, i.e. all methods are defined or pure virtual and all +/// friends, friend functions and nested classes are fully defined in this +/// translation unit. +/// +/// Should only be called from ActOnEndOfTranslationUnit so that all +/// definitions are actually read. +static bool IsRecordFullyDefined(const CXXRecordDecl *RD, + RecordCompleteMap &RecordsComplete, + RecordCompleteMap &MNCComplete) { + RecordCompleteMap::iterator Cache = RecordsComplete.find(RD); + if (Cache != RecordsComplete.end()) + return Cache->second; + bool Complete = MethodsAndNestedClassesComplete(RD, MNCComplete); + for (CXXRecordDecl::friend_iterator I = RD->friend_begin(), + E = RD->friend_end(); + I != E && Complete; ++I) { + // Check if friend classes and methods are complete. + if (TypeSourceInfo *TSI = (*I)->getFriendType()) { + // Friend classes are available as the TypeSourceInfo of the FriendDecl. + if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl()) + Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete); + else + Complete = false; + } else { + // Friend functions are available through the NamedDecl of FriendDecl. + if (const FunctionDecl *FD = + dyn_cast<FunctionDecl>((*I)->getFriendDecl())) + Complete = FD->isDefined(); + else + // This is a template friend, give up. + Complete = false; + } + } + RecordsComplete[RD] = Complete; + return Complete; +} + /// ActOnEndOfTranslationUnit - This is called at the very end of the /// translation unit when EOF is reached and all but the top-level scope is /// popped. void Sema::ActOnEndOfTranslationUnit() { + assert(DelayedDiagnostics.getCurrentPool() == NULL + && "reached end of translation unit with a pool attached?"); + // Only complete translation units define vtables and perform implicit // instantiations. if (TUKind == TU_Complete) { @@ -597,9 +680,17 @@ void Sema::ActOnEndOfTranslationUnit() { if (isa<CXXMethodDecl>(DiagD)) Diag(DiagD->getLocation(), diag::warn_unneeded_member_function) << DiagD->getDeclName(); - else - Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl) - << /*function*/0 << DiagD->getDeclName(); + else { + if (FD->getStorageClassAsWritten() == SC_Static && + !FD->isInlineSpecified() && + !SourceMgr.isFromMainFile( + SourceMgr.getExpansionLoc(FD->getLocation()))) + Diag(DiagD->getLocation(), diag::warn_unneeded_static_internal_decl) + << DiagD->getDeclName(); + else + Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl) + << /*function*/0 << DiagD->getDeclName(); + } } else { Diag(DiagD->getLocation(), isa<CXXMethodDecl>(DiagD) ? diag::warn_unused_member_function @@ -623,6 +714,23 @@ void Sema::ActOnEndOfTranslationUnit() { checkUndefinedInternals(*this); } + if (Diags.getDiagnosticLevel(diag::warn_unused_private_field, + SourceLocation()) + != DiagnosticsEngine::Ignored) { + RecordCompleteMap RecordsComplete; + RecordCompleteMap MNCComplete; + for (NamedDeclSetType::iterator I = UnusedPrivateFields.begin(), + E = UnusedPrivateFields.end(); I != E; ++I) { + const NamedDecl *D = *I; + const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext()); + if (RD && !RD->isUnion() && + IsRecordFullyDefined(RD, RecordsComplete, MNCComplete)) { + Diag(D->getLocation(), diag::warn_unused_private_field) + << D->getDeclName(); + } + } + } + // Check we've noticed that we're no longer parsing the initializer for every // variable. If we miss cases, then at best we have a performance issue and // at worst a rejects-valid bug. @@ -693,6 +801,15 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) { // Count this failure so that we know that template argument deduction // has failed. ++NumSFINAEErrors; + + // Make a copy of this suppressed diagnostic and store it with the + // template-deduction information. + if (*Info && !(*Info)->hasSFINAEDiagnostic()) { + Diagnostic DiagInfo(&Diags); + (*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(), + PartialDiagnostic(DiagInfo, Context.getDiagAllocator())); + } + Diags.setLastDiagnosticIgnored(); Diags.Clear(); return; @@ -709,6 +826,15 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) { // Suppress this diagnostic. ++NumSFINAEErrors; + + // Make a copy of this suppressed diagnostic and store it with the + // template-deduction information. + if (*Info && !(*Info)->hasSFINAEDiagnostic()) { + Diagnostic DiagInfo(&Diags); + (*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(), + PartialDiagnostic(DiagInfo, Context.getDiagAllocator())); + } + Diags.setLastDiagnosticIgnored(); Diags.Clear(); @@ -725,13 +851,13 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) { case DiagnosticIDs::SFINAE_Suppress: // Make a copy of this suppressed diagnostic and store it with the // template-deduction information; - Diagnostic DiagInfo(&Diags); - - if (*Info) + if (*Info) { + Diagnostic DiagInfo(&Diags); (*Info)->addSuppressedDiagnostic(DiagInfo.getLocation(), - PartialDiagnostic(DiagInfo,Context.getDiagAllocator())); - - // Suppress this diagnostic. + PartialDiagnostic(DiagInfo, Context.getDiagAllocator())); + } + + // Suppress this diagnostic. Diags.setLastDiagnosticIgnored(); Diags.Clear(); return; @@ -894,6 +1020,29 @@ LambdaScopeInfo *Sema::getCurLambda() { return dyn_cast<LambdaScopeInfo>(FunctionScopes.back()); } +void Sema::ActOnComment(SourceRange Comment) { + RawComment RC(SourceMgr, Comment); + if (RC.isAlmostTrailingComment()) { + SourceRange MagicMarkerRange(Comment.getBegin(), + Comment.getBegin().getLocWithOffset(3)); + StringRef MagicMarkerText; + switch (RC.getKind()) { + case RawComment::RCK_OrdinaryBCPL: + MagicMarkerText = "///<"; + break; + case RawComment::RCK_OrdinaryC: + MagicMarkerText = "/**<"; + break; + default: + llvm_unreachable("if this is an almost Doxygen comment, " + "it should be ordinary"); + } + Diag(Comment.getBegin(), diag::warn_not_a_doxygen_trailing_member_comment) << + FixItHint::CreateReplacement(MagicMarkerRange, MagicMarkerText); + } + Context.addComment(RC); +} + // Pin this vtable to this file. ExternalSemaSource::~ExternalSemaSource() {} diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 01c141e57f92..3481171c7735 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -152,7 +152,8 @@ struct AccessTarget : public AccessedEntity { CXXRecordDecl *NamingClass, DeclAccessPair FoundDecl, QualType BaseObjectType) - : AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType) { + : AccessedEntity(Context.getDiagAllocator(), Member, NamingClass, + FoundDecl, BaseObjectType) { initialize(); } @@ -161,7 +162,8 @@ struct AccessTarget : public AccessedEntity { CXXRecordDecl *BaseClass, CXXRecordDecl *DerivedClass, AccessSpecifier Access) - : AccessedEntity(Context, Base, BaseClass, DerivedClass, Access) { + : AccessedEntity(Context.getDiagAllocator(), Base, BaseClass, DerivedClass, + Access) { initialize(); } @@ -781,7 +783,7 @@ static AccessResult HasAccess(Sema &S, // Emulate a MSVC bug where the creation of pointer-to-member // to protected member of base class is allowed but only from - // a static function member functions. + // static member functions. if (S.getLangOpts().MicrosoftMode && !EC.Functions.empty()) if (CXXMethodDecl* MD = dyn_cast<CXXMethodDecl>(EC.Functions.front())) if (MD->isStatic()) return AR_accessible; @@ -1391,9 +1393,6 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, if (Entity.getAccess() == AS_public) return Sema::AR_accessible; - if (S.SuppressAccessChecking) - return Sema::AR_accessible; - // If we're currently parsing a declaration, we may need to delay // access control checking, because our effective context might be // different based on what the declaration comes out as. @@ -1633,25 +1632,6 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, return CheckAccess(*this, UseLoc, AccessEntity); } -/// Checks direct (i.e. non-inherited) access to an arbitrary class -/// member. -Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc, - NamedDecl *Target, - const PartialDiagnostic &Diag) { - AccessSpecifier Access = Target->getAccess(); - if (!getLangOpts().AccessControl || - Access == AS_public) - return AR_accessible; - - CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext()); - AccessTarget Entity(Context, AccessTarget::Member, NamingClass, - DeclAccessPair::make(Target, Access), - QualType()); - Entity.setDiag(Diag); - return CheckAccess(*this, UseLoc, Entity); -} - - /// Checks access to an overloaded operator new or delete. Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, SourceRange PlacementRange, @@ -1694,6 +1674,44 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, return CheckAccess(*this, OpLoc, Entity); } +/// Checks access to the target of a friend declaration. +Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) { + assert(isa<CXXMethodDecl>(target) || + (isa<FunctionTemplateDecl>(target) && + isa<CXXMethodDecl>(cast<FunctionTemplateDecl>(target) + ->getTemplatedDecl()))); + + // Friendship lookup is a redeclaration lookup, so there's never an + // inheritance path modifying access. + AccessSpecifier access = target->getAccess(); + + if (!getLangOpts().AccessControl || access == AS_public) + return AR_accessible; + + CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(target); + if (!method) + method = cast<CXXMethodDecl>( + cast<FunctionTemplateDecl>(target)->getTemplatedDecl()); + assert(method->getQualifier()); + + AccessTarget entity(Context, AccessTarget::Member, + cast<CXXRecordDecl>(target->getDeclContext()), + DeclAccessPair::make(target, access), + /*no instance context*/ QualType()); + entity.setDiag(diag::err_access_friend_function) + << method->getQualifierLoc().getSourceRange(); + + // We need to bypass delayed-diagnostics because we might be called + // while the ParsingDeclarator is active. + EffectiveContext EC(CurContext); + switch (CheckEffectiveAccess(*this, EC, target->getLocation(), entity)) { + case AR_accessible: return Sema::AR_accessible; + case AR_inaccessible: return Sema::AR_inaccessible; + case AR_dependent: return Sema::AR_dependent; + } + llvm_unreachable("falling off end"); +} + Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair Found) { if (!getLangOpts().AccessControl || @@ -1714,13 +1732,10 @@ Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, /// Checks access for a hierarchy conversion. /// -/// \param IsBaseToDerived whether this is a base-to-derived conversion (true) -/// or a derived-to-base conversion (false) /// \param ForceCheck true if this check should be performed even if access /// control is disabled; some things rely on this for semantics /// \param ForceUnprivileged true if this check should proceed as if the /// context had no special privileges -/// \param ADK controls the kind of diagnostics that are used Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, QualType Base, QualType Derived, @@ -1836,15 +1851,3 @@ bool Sema::IsSimplyAccessible(NamedDecl *Decl, DeclContext *Ctx) { return true; } - -void Sema::ActOnStartSuppressingAccessChecks() { - assert(!SuppressAccessChecking && - "Tried to start access check suppression when already started."); - SuppressAccessChecking = true; -} - -void Sema::ActOnStopSuppressingAccessChecks() { - assert(SuppressAccessChecking && - "Tried to stop access check suprression when already stopped."); - SuppressAccessChecking = false; -} diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 5a0fceca20da..0de9dd5f64c9 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -227,9 +227,8 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS, if (loc.isInvalid()) loc = SS.getRange().getBegin(); // The type must be complete. - if (RequireCompleteType(loc, type, - PDiag(diag::err_incomplete_nested_name_spec) - << SS.getRange())) { + if (RequireCompleteType(loc, type, diag::err_incomplete_nested_name_spec, + SS.getRange())) { SS.SetInvalid(SS.getRange()); return true; } @@ -539,8 +538,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NamedDecl *SD = Found.getAsSingle<NamedDecl>(); if (isAcceptableNestedNameSpecifier(SD)) { - if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) { - // C++ [basic.lookup.classref]p4: + if (!ObjectType.isNull() && !ObjectTypeSearchedInScope && + !getLangOpts().CPlusPlus0x) { + // C++03 [basic.lookup.classref]p4: // [...] If the name is found in both contexts, the // class-name-or-namespace-name shall refer to the same entity. // @@ -548,6 +548,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, // into the current scope (the scope of the postfix-expression) to // see if we can find the same name there. As above, if there is no // scope, reconstruct the result from the template instantiation itself. + // + // Note that C++11 does *not* perform this redundant lookup. NamedDecl *OuterDecl; if (S) { LookupResult FoundOuter(*this, &Identifier, IdentifierLoc, diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 54683e127ec1..819975143dca 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -561,8 +561,8 @@ void CastOperation::CheckDynamicCast() { assert(DestPointer && "Reference to void is not possible"); } else if (DestRecord) { if (Self.RequireCompleteType(OpRange.getBegin(), DestPointee, - Self.PDiag(diag::err_bad_dynamic_cast_incomplete) - << DestRange)) + diag::err_bad_dynamic_cast_incomplete, + DestRange)) return; } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class) @@ -597,8 +597,8 @@ void CastOperation::CheckDynamicCast() { const RecordType *SrcRecord = SrcPointee->getAs<RecordType>(); if (SrcRecord) { if (Self.RequireCompleteType(OpRange.getBegin(), SrcPointee, - Self.PDiag(diag::err_bad_dynamic_cast_incomplete) - << SrcExpr.get()->getSourceRange())) + diag::err_bad_dynamic_cast_incomplete, + SrcExpr.get())) return; } else { Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class) @@ -1075,8 +1075,8 @@ TryStaticDowncast(Sema &Self, CanQualType SrcType, CanQualType DestType, QualType OrigDestType, unsigned &msg, CastKind &Kind, CXXCastPath &BasePath) { // We can only work with complete types. But don't complain if it doesn't work - if (Self.RequireCompleteType(OpRange.getBegin(), SrcType, Self.PDiag(0)) || - Self.RequireCompleteType(OpRange.getBegin(), DestType, Self.PDiag(0))) + if (Self.RequireCompleteType(OpRange.getBegin(), SrcType, 0) || + Self.RequireCompleteType(OpRange.getBegin(), DestType, 0)) return TC_NotApplicable; // Downcast can only happen in class hierarchies, so we need classes. @@ -1302,7 +1302,9 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, CastKind &Kind, bool ListInitialization) { if (DestType->isRecordType()) { if (Self.RequireCompleteType(OpRange.getBegin(), DestType, - diag::err_bad_dynamic_cast_incomplete)) { + diag::err_bad_dynamic_cast_incomplete) || + Self.RequireNonAbstractType(OpRange.getBegin(), DestType, + diag::err_allocation_of_abstract_type)) { msg = 0; return TC_Failed; } @@ -1504,10 +1506,9 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, } if (const ReferenceType *DestTypeTmp = DestType->getAs<ReferenceType>()) { - bool LValue = DestTypeTmp->isLValueReferenceType(); - if (LValue && !SrcExpr.get()->isLValue()) { - // Cannot cast non-lvalue to lvalue reference type. See the similar - // comment in const_cast. + if (!SrcExpr.get()->isGLValue()) { + // Cannot cast non-glvalue to (lvalue or rvalue) reference type. See the + // similar comment in const_cast. msg = diag::err_bad_cxx_cast_rvalue; return TC_NotApplicable; } @@ -1915,10 +1916,6 @@ void CastOperation::CheckCStyleCast() { return; QualType SrcType = SrcExpr.get()->getType(); - // You can cast an _Atomic(T) to anything you can cast a T to. - if (const AtomicType *AtomicSrcType = SrcType->getAs<AtomicType>()) - SrcType = AtomicSrcType->getValueType(); - assert(!SrcType->isPlaceholderType()); if (Self.RequireCompleteType(OpRange.getBegin(), DestType, @@ -2105,6 +2102,9 @@ ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo, Op.CheckCXXCStyleCast(/*FunctionalStyle=*/true, /*ListInit=*/false); if (Op.SrcExpr.isInvalid()) return ExprError(); + + if (CXXConstructExpr *ConstructExpr = dyn_cast<CXXConstructExpr>(Op.SrcExpr.get())) + ConstructExpr->setParenRange(SourceRange(LPLoc, RPLoc)); return Op.complete(CXXFunctionalCastExpr::Create(Context, Op.ResultType, Op.ValueKind, CastTypeInfo, Op.DestRange.getBegin(), diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 0d15ce24b0c3..2594648b56f8 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -16,12 +16,14 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Analysis/Analyses/FormatString.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -66,16 +68,31 @@ static bool checkArgCount(Sema &S, CallExpr *call, unsigned desiredArgCount) { << call->getArg(1)->getSourceRange(); } -/// CheckBuiltinAnnotationString - Checks that string argument to the builtin -/// annotation is a non wide string literal. -static bool CheckBuiltinAnnotationString(Sema &S, Expr *Arg) { - Arg = Arg->IgnoreParenCasts(); - StringLiteral *Literal = dyn_cast<StringLiteral>(Arg); +/// Check that the first argument to __builtin_annotation is an integer +/// and the second argument is a non-wide string literal. +static bool SemaBuiltinAnnotation(Sema &S, CallExpr *TheCall) { + if (checkArgCount(S, TheCall, 2)) + return true; + + // First argument should be an integer. + Expr *ValArg = TheCall->getArg(0); + QualType Ty = ValArg->getType(); + if (!Ty->isIntegerType()) { + S.Diag(ValArg->getLocStart(), diag::err_builtin_annotation_first_arg) + << ValArg->getSourceRange(); + return true; + } + + // Second argument should be a constant string. + Expr *StrArg = TheCall->getArg(1)->IgnoreParenCasts(); + StringLiteral *Literal = dyn_cast<StringLiteral>(StrArg); if (!Literal || !Literal->isAscii()) { - S.Diag(Arg->getLocStart(), diag::err_builtin_annotation_not_string_constant) - << Arg->getSourceRange(); + S.Diag(StrArg->getLocStart(), diag::err_builtin_annotation_second_arg) + << StrArg->getSourceRange(); return true; } + + TheCall->setType(Ty); return false; } @@ -256,7 +273,7 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return SemaAtomicOpsOverloaded(move(TheCallResult), AtomicExpr::AO##ID); #include "clang/Basic/Builtins.def" case Builtin::BI__builtin_annotation: - if (CheckBuiltinAnnotationString(*this, TheCall->getArg(1))) + if (SemaBuiltinAnnotation(*this, TheCall)) return ExprError(); break; } @@ -270,6 +287,13 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { if (CheckARMBuiltinFunctionCall(BuiltinID, TheCall)) return ExprError(); break; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + if (CheckMipsBuiltinFunctionCall(BuiltinID, TheCall)) + return ExprError(); + break; default: break; } @@ -331,7 +355,7 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context) { bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { llvm::APSInt Result; - unsigned mask = 0; + uint64_t mask = 0; unsigned TV = 0; int PtrArgNum = -1; bool HasConstPtr = false; @@ -349,7 +373,7 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; TV = Result.getLimitedValue(64); - if ((TV > 63) || (mask & (1 << TV)) == 0) + if ((TV > 63) || (mask & (1ULL << TV)) == 0) return Diag(TheCall->getLocStart(), diag::err_invalid_neon_type_code) << TheCall->getArg(ImmArg)->getSourceRange(); } @@ -388,6 +412,11 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { #undef GET_NEON_IMMEDIATE_CHECK }; + // We can't check the value of a dependent argument. + if (TheCall->getArg(i)->isTypeDependent() || + TheCall->getArg(i)->isValueDependent()) + return false; + // Check that the immediate argument is actually a constant. if (SemaBuiltinConstantArg(TheCall, i, Result)) return true; @@ -402,34 +431,119 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return false; } -/// CheckFunctionCall - Check a direct function call for various correctness -/// and safety properties not strictly enforced by the C type system. -bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { - // Get the IdentifierInfo* for the called function. - IdentifierInfo *FnInfo = FDecl->getIdentifier(); +bool Sema::CheckMipsBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + unsigned i = 0, l = 0, u = 0; + switch (BuiltinID) { + default: return false; + case Mips::BI__builtin_mips_wrdsp: i = 1; l = 0; u = 63; break; + case Mips::BI__builtin_mips_rddsp: i = 0; l = 0; u = 63; break; + }; - // None of the checks below are needed for functions that don't have - // simple names (e.g., C++ conversion functions). - if (!FnInfo) + // We can't check the value of a dependent argument. + if (TheCall->getArg(i)->isTypeDependent() || + TheCall->getArg(i)->isValueDependent()) return false; + // Check that the immediate argument is actually a constant. + llvm::APSInt Result; + if (SemaBuiltinConstantArg(TheCall, i, Result)) + return true; + + // Range check against the upper/lower values for this instruction. + unsigned Val = Result.getZExtValue(); + if (Val < l || Val > u) + return Diag(TheCall->getLocStart(), diag::err_argument_invalid_range) + << l << u << TheCall->getArg(i)->getSourceRange(); + + return false; +} + +/// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo +/// parameter with the FormatAttr's correct format_idx and firstDataArg. +/// Returns true when the format fits the function and the FormatStringInfo has +/// been populated. +bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, + FormatStringInfo *FSI) { + FSI->HasVAListArg = Format->getFirstArg() == 0; + FSI->FormatIdx = Format->getFormatIdx() - 1; + FSI->FirstDataArg = FSI->HasVAListArg ? 0 : Format->getFirstArg() - 1; + + // The way the format attribute works in GCC, the implicit this argument + // of member functions is counted. However, it doesn't appear in our own + // lists, so decrement format_idx in that case. + if (IsCXXMember) { + if(FSI->FormatIdx == 0) + return false; + --FSI->FormatIdx; + if (FSI->FirstDataArg != 0) + --FSI->FirstDataArg; + } + return true; +} + +/// Handles the checks for format strings, non-POD arguments to vararg +/// functions, and NULL arguments passed to non-NULL parameters. +void Sema::checkCall(NamedDecl *FDecl, Expr **Args, + unsigned NumArgs, + unsigned NumProtoArgs, + bool IsMemberFunction, + SourceLocation Loc, + SourceRange Range, + VariadicCallType CallType) { // FIXME: This mechanism should be abstracted to be less fragile and // more efficient. For example, just map function ids to custom // handlers. // Printf and scanf checking. + bool HandledFormatString = false; for (specific_attr_iterator<FormatAttr> - i = FDecl->specific_attr_begin<FormatAttr>(), - e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { - CheckFormatArguments(*i, TheCall); - } + I = FDecl->specific_attr_begin<FormatAttr>(), + E = FDecl->specific_attr_end<FormatAttr>(); I != E ; ++I) + if (CheckFormatArguments(*I, Args, NumArgs, IsMemberFunction, CallType, + Loc, Range)) + HandledFormatString = true; + + // Refuse POD arguments that weren't caught by the format string + // checks above. + if (!HandledFormatString && CallType != VariadicDoesNotApply) + for (unsigned ArgIdx = NumProtoArgs; ArgIdx < NumArgs; ++ArgIdx) + variadicArgumentPODCheck(Args[ArgIdx], CallType); for (specific_attr_iterator<NonNullAttr> - i = FDecl->specific_attr_begin<NonNullAttr>(), - e = FDecl->specific_attr_end<NonNullAttr>(); i != e; ++i) { - CheckNonNullArguments(*i, TheCall->getArgs(), - TheCall->getCallee()->getLocStart()); - } + I = FDecl->specific_attr_begin<NonNullAttr>(), + E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) + CheckNonNullArguments(*I, Args, Loc); +} + +/// CheckConstructorCall - Check a constructor call for correctness and safety +/// properties not enforced by the C type system. +void Sema::CheckConstructorCall(FunctionDecl *FDecl, Expr **Args, + unsigned NumArgs, + const FunctionProtoType *Proto, + SourceLocation Loc) { + VariadicCallType CallType = + Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; + checkCall(FDecl, Args, NumArgs, Proto->getNumArgs(), + /*IsMemberFunction=*/true, Loc, SourceRange(), CallType); +} + +/// CheckFunctionCall - Check a direct function call for various correctness +/// and safety properties not strictly enforced by the C type system. +bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, + const FunctionProtoType *Proto) { + bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall); + VariadicCallType CallType = getVariadicCallType(FDecl, Proto, + TheCall->getCallee()); + unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0; + checkCall(FDecl, TheCall->getArgs(), TheCall->getNumArgs(), NumProtoArgs, + IsMemberFunction, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); + + IdentifierInfo *FnInfo = FDecl->getIdentifier(); + // None of the checks below are needed for functions that don't have + // simple names (e.g., C++ conversion functions). + if (!FnInfo) + return false; unsigned CMId = FDecl->getMemoryFunctionKind(); if (CMId == 0) @@ -448,25 +562,18 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, Expr **Args, unsigned NumArgs) { - for (specific_attr_iterator<FormatAttr> - i = Method->specific_attr_begin<FormatAttr>(), - e = Method->specific_attr_end<FormatAttr>(); i != e ; ++i) { - - CheckFormatArguments(*i, Args, NumArgs, false, lbrac, - Method->getSourceRange()); - } + VariadicCallType CallType = + Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; - // diagnose nonnull arguments. - for (specific_attr_iterator<NonNullAttr> - i = Method->specific_attr_begin<NonNullAttr>(), - e = Method->specific_attr_end<NonNullAttr>(); i != e; ++i) { - CheckNonNullArguments(*i, Args, lbrac); - } + checkCall(Method, Args, NumArgs, Method->param_size(), + /*IsMemberFunction=*/false, + lbrac, Method->getSourceRange(), CallType); return false; } -bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) { +bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall, + const FunctionProtoType *Proto) { const VarDecl *V = dyn_cast<VarDecl>(NDecl); if (!V) return false; @@ -475,13 +582,15 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) { if (!Ty->isBlockPointerType()) return false; - // format string checking. - for (specific_attr_iterator<FormatAttr> - i = NDecl->specific_attr_begin<FormatAttr>(), - e = NDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { - CheckFormatArguments(*i, TheCall); - } + VariadicCallType CallType = + Proto && Proto->isVariadic() ? VariadicBlock : VariadicDoesNotApply ; + unsigned NumProtoArgs = Proto ? Proto->getNumArgs() : 0; + checkCall(NDecl, TheCall->getArgs(), TheCall->getNumArgs(), + NumProtoArgs, /*IsMemberFunction=*/false, + TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); + return false; } @@ -1260,7 +1369,7 @@ bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall) { // If the common type isn't a real floating type, then the arguments were // invalid for this operation. - if (!Res->isRealFloatingType()) + if (Res.isNull() || !Res->isRealFloatingType()) return Diag(OrigArg0.get()->getLocStart(), diag::err_typecheck_call_invalid_ordered_compare) << OrigArg0.get()->getType() << OrigArg1.get()->getType() @@ -1409,7 +1518,11 @@ bool Sema::SemaBuiltinPrefetch(CallExpr *TheCall) { // constant integers. for (unsigned i = 1; i != NumArgs; ++i) { Expr *Arg = TheCall->getArg(i); - + + // We can't check the value of a dependent argument. + if (Arg->isTypeDependent() || Arg->isValueDependent()) + continue; + llvm::APSInt Result; if (SemaBuiltinConstantArg(TheCall, i, Result)) return true; @@ -1454,7 +1567,12 @@ bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum, // For compatibility check 0-3, llvm only handles 0 and 2. bool Sema::SemaBuiltinObjectSize(CallExpr *TheCall) { llvm::APSInt Result; - + + // We can't check the value of a dependent argument. + if (TheCall->getArg(1)->isTypeDependent() || + TheCall->getArg(1)->isValueDependent()) + return false; + // Check constant-ness first. if (SemaBuiltinConstantArg(TheCall, 1, Result)) return true; @@ -1485,14 +1603,19 @@ bool Sema::SemaBuiltinLongjmp(CallExpr *TheCall) { return false; } -// Handle i > 1 ? "x" : "y", recursively. -bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, - unsigned NumArgs, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg, - FormatStringType Type, bool inFunctionCall) { +// Determine if an expression is a string literal or constant string. +// If this function returns false on the arguments to a function expecting a +// format string, we will usually need to emit a warning. +// True string literals are then checked by CheckFormatString. +Sema::StringLiteralCheckType +Sema::checkFormatStringExpr(const Expr *E, Expr **Args, + unsigned NumArgs, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg, + FormatStringType Type, VariadicCallType CallType, + bool inFunctionCall) { tryAgain: if (E->isTypeDependent() || E->isValueDependent()) - return false; + return SLCT_NotALiteral; E = E->IgnoreParenCasts(); @@ -1501,18 +1624,26 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, // The behavior of printf and friends in this case is implementation // dependent. Ideally if the format string cannot be null then // it should have a 'nonnull' attribute in the function prototype. - return true; + return SLCT_CheckedLiteral; switch (E->getStmtClass()) { case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { - const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(E); - return SemaCheckStringLiteral(C->getTrueExpr(), Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall) - && SemaCheckStringLiteral(C->getFalseExpr(), Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall); + // The expression is a literal if both sub-expressions were, and it was + // completely checked only if both sub-expressions were checked. + const AbstractConditionalOperator *C = + cast<AbstractConditionalOperator>(E); + StringLiteralCheckType Left = + checkFormatStringExpr(C->getTrueExpr(), Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, CallType, inFunctionCall); + if (Left == SLCT_NotALiteral) + return SLCT_NotALiteral; + StringLiteralCheckType Right = + checkFormatStringExpr(C->getFalseExpr(), Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, CallType, inFunctionCall); + return Left < Right ? Left : Right; } case Stmt::ImplicitCastExprClass: { @@ -1525,13 +1656,13 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, E = src; goto tryAgain; } - return false; + return SLCT_NotALiteral; case Stmt::PredefinedExprClass: // While __func__, etc., are technically not string literals, they // cannot contain format specifiers and thus are not a security // liability. - return true; + return SLCT_UncheckedLiteral; case Stmt::DeclRefExprClass: { const DeclRefExpr *DR = cast<DeclRefExpr>(E); @@ -1554,10 +1685,17 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, } if (isConstant) { - if (const Expr *Init = VD->getAnyInitializer()) - return SemaCheckStringLiteral(Init, Args, NumArgs, - HasVAListArg, format_idx, firstDataArg, - Type, /*inFunctionCall*/false); + if (const Expr *Init = VD->getAnyInitializer()) { + // Look through initializers like const char c[] = { "foo" } + if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) { + if (InitList->isStringLiteralInit()) + Init = InitList->getInit(0)->IgnoreParenImpCasts(); + } + return checkFormatStringExpr(Init, Args, NumArgs, + HasVAListArg, format_idx, + firstDataArg, Type, CallType, + /*inFunctionCall*/false); + } } // For vprintf* functions (i.e., HasVAListArg==true), we add a @@ -1590,14 +1728,14 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, // We can't pass a 'scanf' string to a 'printf' function. if (PVIndex == PVFormat->getFormatIdx() && Type == GetFormatStringType(PVFormat)) - return true; + return SLCT_UncheckedLiteral; } } } } } - return false; + return SLCT_NotALiteral; } case Stmt::CallExprClass: @@ -1611,13 +1749,23 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, --ArgIndex; const Expr *Arg = CE->getArg(ArgIndex - 1); - return SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type, - inFunctionCall); + return checkFormatStringExpr(Arg, Args, NumArgs, + HasVAListArg, format_idx, firstDataArg, + Type, CallType, inFunctionCall); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + unsigned BuiltinID = FD->getBuiltinID(); + if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString || + BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) { + const Expr *Arg = CE->getArg(0); + return checkFormatStringExpr(Arg, Args, NumArgs, + HasVAListArg, format_idx, + firstDataArg, Type, CallType, + inFunctionCall); + } } } - return false; + return SLCT_NotALiteral; } case Stmt::ObjCStringLiteralClass: case Stmt::StringLiteralClass: { @@ -1630,15 +1778,15 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, if (StrE) { CheckFormatString(StrE, E, Args, NumArgs, HasVAListArg, format_idx, - firstDataArg, Type, inFunctionCall); - return true; + firstDataArg, Type, inFunctionCall, CallType); + return SLCT_CheckedLiteral; } - return false; + return SLCT_NotALiteral; } default: - return false; + return SLCT_NotALiteral; } } @@ -1667,44 +1815,30 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) { .Default(FST_Unknown); } -/// CheckPrintfScanfArguments - Check calls to printf and scanf (and similar +/// CheckFormatArguments - Check calls to printf and scanf (and similar /// functions) for correct use of format strings. -void Sema::CheckFormatArguments(const FormatAttr *Format, CallExpr *TheCall) { - bool IsCXXMember = false; - // The way the format attribute works in GCC, the implicit this argument - // of member functions is counted. However, it doesn't appear in our own - // lists, so decrement format_idx in that case. - IsCXXMember = isa<CXXMemberCallExpr>(TheCall); - CheckFormatArguments(Format, TheCall->getArgs(), TheCall->getNumArgs(), - IsCXXMember, TheCall->getRParenLoc(), - TheCall->getCallee()->getSourceRange()); -} - -void Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args, +/// Returns true if a format string has been fully checked. +bool Sema::CheckFormatArguments(const FormatAttr *Format, Expr **Args, unsigned NumArgs, bool IsCXXMember, + VariadicCallType CallType, SourceLocation Loc, SourceRange Range) { - bool HasVAListArg = Format->getFirstArg() == 0; - unsigned format_idx = Format->getFormatIdx() - 1; - unsigned firstDataArg = HasVAListArg ? 0 : Format->getFirstArg() - 1; - if (IsCXXMember) { - if (format_idx == 0) - return; - --format_idx; - if(firstDataArg != 0) - --firstDataArg; - } - CheckFormatArguments(Args, NumArgs, HasVAListArg, format_idx, - firstDataArg, GetFormatStringType(Format), Loc, Range); + FormatStringInfo FSI; + if (getFormatStringInfo(Format, IsCXXMember, &FSI)) + return CheckFormatArguments(Args, NumArgs, FSI.HasVAListArg, FSI.FormatIdx, + FSI.FirstDataArg, GetFormatStringType(Format), + CallType, Loc, Range); + return false; } -void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, +bool Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, + VariadicCallType CallType, SourceLocation Loc, SourceRange Range) { // CHECK: printf/scanf-like function is called with no format string. if (format_idx >= NumArgs) { Diag(Loc, diag::warn_missing_format_string) << Range; - return; + return false; } const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts(); @@ -1721,21 +1855,25 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, // C string (e.g. "%d") // ObjC string uses the same format specifiers as C string, so we can use // the same format string checking logic for both ObjC and C strings. - if (SemaCheckStringLiteral(OrigFormatExpr, Args, NumArgs, HasVAListArg, - format_idx, firstDataArg, Type)) - return; // Literal format string found, check done! + StringLiteralCheckType CT = + checkFormatStringExpr(OrigFormatExpr, Args, NumArgs, HasVAListArg, + format_idx, firstDataArg, Type, CallType); + if (CT != SLCT_NotALiteral) + // Literal format string found, check done! + return CT == SLCT_CheckedLiteral; // Strftime is particular as it always uses a single 'time' argument, // so it is safe to pass a non-literal string. if (Type == FST_Strftime) - return; + return false; // Do not emit diag when the string param is a macro expansion and the // format is either NSString or CFString. This is a hack to prevent // diag when using the NSLocalizedString and CFCopyLocalizedString macros // which are usually used in place of NS and CF string literals. - if (Type == FST_NSString && Args[format_idx]->getLocStart().isMacroID()) - return; + if (Type == FST_NSString && + SourceMgr.isInSystemMacro(Args[format_idx]->getLocStart())) + return false; // If there are no arguments specified, warn with -Wformat-security, otherwise // warn only with -Wformat-nonliteral. @@ -1747,6 +1885,7 @@ void Sema::CheckFormatArguments(Expr **Args, unsigned NumArgs, Diag(Args[format_idx]->getLocStart(), diag::warn_format_nonliteral) << OrigFormatExpr->getSourceRange(); + return false; } namespace { @@ -1757,7 +1896,6 @@ protected: const Expr *OrigFormatExpr; const unsigned FirstDataArg; const unsigned NumDataArgs; - const bool IsObjCLiteral; const char *Beg; // Start of format string. const bool HasVAListArg; const Expr * const *Args; @@ -1767,21 +1905,20 @@ protected: bool usesPositionalArgs; bool atFirstArg; bool inFunctionCall; + Sema::VariadicCallType CallType; public: CheckFormatHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, bool isObjCLiteral, - const char *beg, bool hasVAListArg, + unsigned numDataArgs, const char *beg, bool hasVAListArg, Expr **args, unsigned numArgs, - unsigned formatIdx, bool inFunctionCall) + unsigned formatIdx, bool inFunctionCall, + Sema::VariadicCallType callType) : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), - FirstDataArg(firstDataArg), - NumDataArgs(numDataArgs), - IsObjCLiteral(isObjCLiteral), Beg(beg), - HasVAListArg(hasVAListArg), + FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), + Beg(beg), HasVAListArg(hasVAListArg), Args(args), NumArgs(numArgs), FormatIdx(formatIdx), usesPositionalArgs(false), atFirstArg(true), - inFunctionCall(inFunctionCall) { + inFunctionCall(inFunctionCall), CallType(callType) { CoveredArgs.resize(numDataArgs); CoveredArgs.reset(); } @@ -1938,7 +2075,7 @@ void CheckFormatHandler::HandleZeroPosition(const char *startPos, } void CheckFormatHandler::HandleNullChar(const char *nullCharacter) { - if (!IsObjCLiteral) { + if (!isa<ObjCStringLiteral>(OrigFormatExpr)) { // The presence of a null character is likely an error. EmitFormatDiagnostic( S.PDiag(diag::warn_printf_format_string_contains_null_char), @@ -1947,6 +2084,8 @@ void CheckFormatHandler::HandleNullChar(const char *nullCharacter) { } } +// Note that this may return NULL if there was an error parsing or building +// one of the argument expressions. const Expr *CheckFormatHandler::getDataArg(unsigned i) const { return Args[FirstDataArg + i]; } @@ -1960,9 +2099,14 @@ void CheckFormatHandler::DoneProcessing() { signed notCoveredArg = CoveredArgs.find_first(); if (notCoveredArg >= 0) { assert((unsigned)notCoveredArg < NumDataArgs); - EmitFormatDiagnostic(S.PDiag(diag::warn_printf_data_arg_not_used), - getDataArg((unsigned) notCoveredArg)->getLocStart(), - /*IsStringLocation*/false, getFormatStringRange()); + if (const Expr *E = getDataArg((unsigned) notCoveredArg)) { + SourceLocation Loc = E->getLocStart(); + if (!S.getSourceManager().isInSystemMacro(Loc)) { + EmitFormatDiagnostic(S.PDiag(diag::warn_printf_data_arg_not_used), + Loc, /*IsStringLocation*/false, + getFormatStringRange()); + } + } } } } @@ -2086,17 +2230,20 @@ void CheckFormatHandler::EmitFormatDiagnostic(Sema &S, bool InFunctionCall, namespace { class CheckPrintfHandler : public CheckFormatHandler { + bool ObjCContext; public: CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, bool isObjCLiteral, + unsigned numDataArgs, bool isObjC, const char *beg, bool hasVAListArg, Expr **Args, unsigned NumArgs, - unsigned formatIdx, bool inFunctionCall) + unsigned formatIdx, bool inFunctionCall, + Sema::VariadicCallType CallType) : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, - numDataArgs, isObjCLiteral, beg, hasVAListArg, - Args, NumArgs, formatIdx, inFunctionCall) {} - + numDataArgs, beg, hasVAListArg, Args, NumArgs, + formatIdx, inFunctionCall, CallType), ObjCContext(isObjC) + {} + bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, @@ -2106,7 +2253,11 @@ public: bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen); - + bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen, + const Expr *E); + bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k, const char *startSpecifier, unsigned specifierLen); void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS, @@ -2120,6 +2271,9 @@ public: const analyze_printf::OptionalFlag &ignoredFlag, const analyze_printf::OptionalFlag &flag, const char *startSpecifier, unsigned specifierLen); + bool checkForCStrMembers(const analyze_printf::ArgType &AT, + const Expr *E, const CharSourceRange &CSR); + }; } @@ -2161,14 +2315,17 @@ bool CheckPrintfHandler::HandleAmount( // doesn't emit a warning for that case. CoveredArgs.set(argIndex); const Expr *Arg = getDataArg(argIndex); + if (!Arg) + return false; + QualType T = Arg->getType(); - const analyze_printf::ArgTypeResult &ATR = Amt.getArgType(S.Context); - assert(ATR.isValid()); + const analyze_printf::ArgType &AT = Amt.getArgType(S.Context); + assert(AT.isValid()); - if (!ATR.matchesType(S.Context, T)) { + if (!AT.matchesType(S.Context, T)) { EmitFormatDiagnostic(S.PDiag(diag::warn_printf_asterisk_wrong_type) - << k << ATR.getRepresentativeTypeName(S.Context) + << k << AT.getRepresentativeTypeName(S.Context) << T << Arg->getSourceRange(), getLocationOfByte(Amt.getStart()), /*IsStringLocation*/true, @@ -2237,6 +2394,64 @@ void CheckPrintfHandler::HandleIgnoredFlag( getSpecifierRange(ignoredFlag.getPosition(), 1))); } +// Determines if the specified is a C++ class or struct containing +// a member with the specified name and kind (e.g. a CXXMethodDecl named +// "c_str()"). +template<typename MemberKind> +static llvm::SmallPtrSet<MemberKind*, 1> +CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) { + const RecordType *RT = Ty->getAs<RecordType>(); + llvm::SmallPtrSet<MemberKind*, 1> Results; + + if (!RT) + return Results; + const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); + if (!RD) + return Results; + + LookupResult R(S, &S.PP.getIdentifierTable().get(Name), SourceLocation(), + Sema::LookupMemberName); + + // We just need to include all members of the right kind turned up by the + // filter, at this point. + if (S.LookupQualifiedName(R, RT->getDecl())) + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + NamedDecl *decl = (*I)->getUnderlyingDecl(); + if (MemberKind *FK = dyn_cast<MemberKind>(decl)) + Results.insert(FK); + } + return Results; +} + +// Check if a (w)string was passed when a (w)char* was needed, and offer a +// better diagnostic if so. AT is assumed to be valid. +// Returns true when a c_str() conversion method is found. +bool CheckPrintfHandler::checkForCStrMembers( + const analyze_printf::ArgType &AT, const Expr *E, + const CharSourceRange &CSR) { + typedef llvm::SmallPtrSet<CXXMethodDecl*, 1> MethodSet; + + MethodSet Results = + CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType()); + + for (MethodSet::iterator MI = Results.begin(), ME = Results.end(); + MI != ME; ++MI) { + const CXXMethodDecl *Method = *MI; + if (Method->getNumParams() == 0 && + AT.matchesType(S.Context, Method->getResultType())) { + // FIXME: Suggest parens if the expression needs them. + SourceLocation EndLoc = + S.getPreprocessor().getLocForEndOfToken(E->getLocEnd()); + S.Diag(E->getLocStart(), diag::note_printf_c_str) + << "c_str()" + << FixItHint::CreateInsertion(EndLoc, ".c_str()"); + return true; + } + } + + return false; +} + bool CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, @@ -2288,7 +2503,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier // Check for using an Objective-C specific conversion specifier // in a non-ObjC literal. - if (!IsObjCLiteral && CS.isObjCArg()) { + if (!ObjCContext && CS.isObjCArg()) { return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, specifierLen); } @@ -2346,17 +2561,6 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier HandleNonStandardConversionSpecification(LM, CS, startSpecifier, specifierLen); - // Are we using '%n'? - if (CS.getKind() == ConversionSpecifier::nArg) { - // Issue a warning about this being a possible security issue. - EmitFormatDiagnostic(S.PDiag(diag::warn_printf_write_back), - getLocationOfByte(CS.getStart()), - /*IsStringLocation*/true, - getSpecifierRange(startSpecifier, specifierLen)); - // Continue checking the other format specifiers. - return true; - } - // The remaining checks depend on the data arguments. if (HasVAListArg) return true; @@ -2364,54 +2568,98 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex)) return false; + const Expr *Arg = getDataArg(argIndex); + if (!Arg) + return true; + + return checkFormatExpr(FS, startSpecifier, specifierLen, Arg); +} + +bool +CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen, + const Expr *E) { + using namespace analyze_format_string; + using namespace analyze_printf; // Now type check the data expression that matches the // format specifier. - const Expr *Ex = getDataArg(argIndex); - const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context, - IsObjCLiteral); - if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) { - // Check if we didn't match because of an implicit cast from a 'char' - // or 'short' to an 'int'. This is done because printf is a varargs - // function. - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex)) - if (ICE->getType() == S.Context.IntTy) { - // All further checking is done on the subexpression. - Ex = ICE->getSubExpr(); - if (ATR.matchesType(S.Context, Ex->getType())) - return true; + const analyze_printf::ArgType &AT = FS.getArgType(S.Context, + ObjCContext); + if (AT.isValid() && !AT.matchesType(S.Context, E->getType())) { + // Look through argument promotions for our error message's reported type. + // This includes the integral and floating promotions, but excludes array + // and function pointer decay; seeing that an argument intended to be a + // string has type 'char [6]' is probably more confusing than 'char *'. + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { + if (ICE->getCastKind() == CK_IntegralCast || + ICE->getCastKind() == CK_FloatingCast) { + E = ICE->getSubExpr(); + + // Check if we didn't match because of an implicit cast from a 'char' + // or 'short' to an 'int'. This is done because printf is a varargs + // function. + if (ICE->getType() == S.Context.IntTy || + ICE->getType() == S.Context.UnsignedIntTy) { + // All further checking is done on the subexpression. + if (AT.matchesType(S.Context, E->getType())) + return true; + } } + } // We may be able to offer a FixItHint if it is a supported type. PrintfSpecifier fixedFS = FS; - bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(), - S.Context, IsObjCLiteral); + bool success = fixedFS.fixType(E->getType(), S.getLangOpts(), + S.Context, ObjCContext); if (success) { // Get the fix string from the fixed format specifier - SmallString<128> buf; + SmallString<16> buf; llvm::raw_svector_ostream os(buf); fixedFS.toString(os); EmitFormatDiagnostic( S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() - << Ex->getSourceRange(), - getLocationOfByte(CS.getStart()), - /*IsStringLocation*/true, - getSpecifierRange(startSpecifier, specifierLen), + << AT.getRepresentativeTypeName(S.Context) << E->getType() + << E->getSourceRange(), + E->getLocStart(), + /*IsStringLocation*/false, + getSpecifierRange(StartSpecifier, SpecifierLen), FixItHint::CreateReplacement( - getSpecifierRange(startSpecifier, specifierLen), + getSpecifierRange(StartSpecifier, SpecifierLen), os.str())); - } - else { - EmitFormatDiagnostic( - S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() - << getSpecifierRange(startSpecifier, specifierLen) - << Ex->getSourceRange(), - getLocationOfByte(CS.getStart()), - true, - getSpecifierRange(startSpecifier, specifierLen)); + } else { + const CharSourceRange &CSR = getSpecifierRange(StartSpecifier, + SpecifierLen); + // Since the warning for passing non-POD types to variadic functions + // was deferred until now, we emit a warning for non-POD + // arguments here. + if (S.isValidVarArgType(E->getType()) == Sema::VAK_Invalid) { + unsigned DiagKind; + if (E->getType()->isObjCObjectType()) + DiagKind = diag::err_cannot_pass_objc_interface_to_vararg_format; + else + DiagKind = diag::warn_non_pod_vararg_with_format_string; + + EmitFormatDiagnostic( + S.PDiag(DiagKind) + << S.getLangOpts().CPlusPlus0x + << E->getType() + << CallType + << AT.getRepresentativeTypeName(S.Context) + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); + + checkForCStrMembers(AT, E, CSR); + } else + EmitFormatDiagnostic( + S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) + << AT.getRepresentativeTypeName(S.Context) << E->getType() + << CSR + << E->getSourceRange(), + E->getLocStart(), /*IsStringLocation*/false, CSR); } } @@ -2425,13 +2673,14 @@ class CheckScanfHandler : public CheckFormatHandler { public: CheckScanfHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, bool isObjCLiteral, - const char *beg, bool hasVAListArg, + unsigned numDataArgs, const char *beg, bool hasVAListArg, Expr **Args, unsigned NumArgs, - unsigned formatIdx, bool inFunctionCall) + unsigned formatIdx, bool inFunctionCall, + Sema::VariadicCallType CallType) : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, - numDataArgs, isObjCLiteral, beg, hasVAListArg, - Args, NumArgs, formatIdx, inFunctionCall) {} + numDataArgs, beg, hasVAListArg, + Args, NumArgs, formatIdx, inFunctionCall, CallType) + {} bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, @@ -2548,8 +2797,11 @@ bool CheckScanfHandler::HandleScanfSpecifier( // Check that the argument type matches the format specifier. const Expr *Ex = getDataArg(argIndex); - const analyze_scanf::ScanfArgTypeResult &ATR = FS.getArgType(S.Context); - if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) { + if (!Ex) + return true; + + const analyze_format_string::ArgType &AT = FS.getArgType(S.Context); + if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType())) { ScanfSpecifier fixedFS = FS; bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(), S.Context); @@ -2562,10 +2814,10 @@ bool CheckScanfHandler::HandleScanfSpecifier( EmitFormatDiagnostic( S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() + << AT.getRepresentativeTypeName(S.Context) << Ex->getType() << Ex->getSourceRange(), - getLocationOfByte(CS.getStart()), - /*IsStringLocation*/true, + Ex->getLocStart(), + /*IsStringLocation*/false, getSpecifierRange(startSpecifier, specifierLen), FixItHint::CreateReplacement( getSpecifierRange(startSpecifier, specifierLen), @@ -2573,10 +2825,10 @@ bool CheckScanfHandler::HandleScanfSpecifier( } else { EmitFormatDiagnostic( S.PDiag(diag::warn_printf_conversion_argument_type_mismatch) - << ATR.getRepresentativeTypeName(S.Context) << Ex->getType() + << AT.getRepresentativeTypeName(S.Context) << Ex->getType() << Ex->getSourceRange(), - getLocationOfByte(CS.getStart()), - /*IsStringLocation*/true, + Ex->getLocStart(), + /*IsStringLocation*/false, getSpecifierRange(startSpecifier, specifierLen)); } } @@ -2589,10 +2841,10 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, Expr **Args, unsigned NumArgs, bool HasVAListArg, unsigned format_idx, unsigned firstDataArg, FormatStringType Type, - bool inFunctionCall) { + bool inFunctionCall, VariadicCallType CallType) { // CHECK: is the format string a wide literal? - if (!FExpr->isAscii()) { + if (!FExpr->isAscii() && !FExpr->isUTF8()) { CheckFormatHandler::EmitFormatDiagnostic( *this, inFunctionCall, Args[format_idx], PDiag(diag::warn_format_string_is_wide_literal), FExpr->getLocStart(), @@ -2617,18 +2869,17 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, if (Type == FST_Printf || Type == FST_NSString) { CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, - numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr), + numDataArgs, (Type == FST_NSString), Str, HasVAListArg, Args, NumArgs, format_idx, - inFunctionCall); + inFunctionCall, CallType); if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen, getLangOpts())) H.DoneProcessing(); } else if (Type == FST_Scanf) { - CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, - numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr), + CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs, Str, HasVAListArg, Args, NumArgs, format_idx, - inFunctionCall); + inFunctionCall, CallType); if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen, getLangOpts())) @@ -2728,19 +2979,43 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call, // TODO: For strncpy() and friends, this could suggest sizeof(dst) // over sizeof(src) as well. unsigned ActionIdx = 0; // Default is to suggest dereferencing. + StringRef ReadableName = FnName->getName(); + if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest)) if (UnaryOp->getOpcode() == UO_AddrOf) ActionIdx = 1; // If its an address-of operator, just remove it. if (Context.getTypeSize(PointeeTy) == Context.getCharWidth()) ActionIdx = 2; // If the pointee's size is sizeof(char), // suggest an explicit length. - unsigned DestSrcSelect = - (BId == Builtin::BIstrndup ? 1 : ArgIdx); - DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest, + + // If the function is defined as a builtin macro, do not show macro + // expansion. + SourceLocation SL = SizeOfArg->getExprLoc(); + SourceRange DSR = Dest->getSourceRange(); + SourceRange SSR = SizeOfArg->getSourceRange(); + SourceManager &SM = PP.getSourceManager(); + + if (SM.isMacroArgExpansion(SL)) { + ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts); + SL = SM.getSpellingLoc(SL); + DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()), + SM.getSpellingLoc(DSR.getEnd())); + SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()), + SM.getSpellingLoc(SSR.getEnd())); + } + + DiagRuntimeBehavior(SL, SizeOfArg, PDiag(diag::warn_sizeof_pointer_expr_memaccess) - << FnName << DestSrcSelect << ActionIdx - << Dest->getSourceRange() - << SizeOfArg->getSourceRange()); + << ReadableName + << PointeeTy + << DestTy + << DSR + << SSR); + DiagRuntimeBehavior(SL, SizeOfArg, + PDiag(diag::warn_sizeof_pointer_expr_memaccess_note) + << ActionIdx + << SSR); + break; } } @@ -2826,6 +3101,19 @@ static const Expr *ignoreLiteralAdditions(const Expr *Ex, ASTContext &Ctx) { return Ex; } +static bool isConstantSizeArrayWithMoreThanOneElement(QualType Ty, + ASTContext &Context) { + // Only handle constant-sized or VLAs, but not flexible members. + if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(Ty)) { + // Only issue the FIXIT for arrays of size > 1. + if (CAT->getSize().getSExtValue() <= 1) + return false; + } else if (!Ty->isVariableArrayType()) { + return false; + } + return true; +} + // Warn if the user has made the 'size' argument to strlcpy or strlcat // be the size of the source, instead of the destination. void Sema::CheckStrlcpycatArguments(const CallExpr *Call, @@ -2876,16 +3164,8 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call, // pointers if we know the actual size, like if DstArg is 'array+2' // we could say 'sizeof(array)-2'. const Expr *DstArg = Call->getArg(0)->IgnoreParenImpCasts(); - QualType DstArgTy = DstArg->getType(); - - // Only handle constant-sized or VLAs, but not flexible members. - if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(DstArgTy)) { - // Only issue the FIXIT for arrays of size > 1. - if (CAT->getSize().getSExtValue() <= 1) - return; - } else if (!DstArgTy->isVariableArrayType()) { + if (!isConstantSizeArrayWithMoreThanOneElement(DstArg->getType(), Context)) return; - } SmallString<128> sizeString; llvm::raw_svector_ostream OS(sizeString); @@ -2967,26 +3247,23 @@ void Sema::CheckStrncatArguments(const CallExpr *CE, SM.getSpellingLoc(SR.getEnd())); } + // Check if the destination is an array (rather than a pointer to an array). + QualType DstTy = DstArg->getType(); + bool isKnownSizeArray = isConstantSizeArrayWithMoreThanOneElement(DstTy, + Context); + if (!isKnownSizeArray) { + if (PatternType == 1) + Diag(SL, diag::warn_strncat_wrong_size) << SR; + else + Diag(SL, diag::warn_strncat_src_size) << SR; + return; + } + if (PatternType == 1) Diag(SL, diag::warn_strncat_large_size) << SR; else Diag(SL, diag::warn_strncat_src_size) << SR; - // Output a FIXIT hint if the destination is an array (rather than a - // pointer to an array). This could be enhanced to handle some - // pointers if we know the actual size, like if DstArg is 'array+2' - // we could say 'sizeof(array)-2'. - QualType DstArgTy = DstArg->getType(); - - // Only handle constant-sized or VLAs, but not flexible members. - if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(DstArgTy)) { - // Only issue the FIXIT for arrays of size > 1. - if (CAT->getSize().getSExtValue() <= 1) - return; - } else if (!DstArgTy->isVariableArrayType()) { - return; - } - SmallString<128> sizeString; llvm::raw_svector_ostream OS(sizeString); OS << "sizeof("; @@ -3002,8 +3279,10 @@ void Sema::CheckStrncatArguments(const CallExpr *CE, //===--- CHECK: Return Address of Stack Variable --------------------------===// -static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars); -static Expr *EvalAddr(Expr* E, SmallVectorImpl<DeclRefExpr *> &refVars); +static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars, + Decl *ParentDecl); +static Expr *EvalAddr(Expr* E, SmallVectorImpl<DeclRefExpr *> &refVars, + Decl *ParentDecl); /// CheckReturnStackAddr - Check if a return statement returns the address /// of a stack variable. @@ -3018,9 +3297,9 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, // label addresses or references to temporaries. if (lhsType->isPointerType() || (!getLangOpts().ObjCAutoRefCount && lhsType->isBlockPointerType())) { - stackE = EvalAddr(RetValExp, refVars); + stackE = EvalAddr(RetValExp, refVars, /*ParentDecl=*/0); } else if (lhsType->isReferenceType()) { - stackE = EvalVal(RetValExp, refVars); + stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/0); } if (stackE == 0) @@ -3094,7 +3373,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, /// * arbitrary interplay between "&" and "*" operators /// * pointer arithmetic from an address of a stack variable /// * taking the address of an array element where the array is on the stack -static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { +static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars, + Decl *ParentDecl) { if (E->isTypeDependent()) return NULL; @@ -3120,7 +3400,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { V->getType()->isReferenceType() && V->hasInit()) { // Add the reference variable to the "trail". refVars.push_back(DR); - return EvalAddr(V->getInit(), refVars); + return EvalAddr(V->getInit(), refVars, ParentDecl); } return NULL; @@ -3132,7 +3412,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { UnaryOperator *U = cast<UnaryOperator>(E); if (U->getOpcode() == UO_AddrOf) - return EvalVal(U->getSubExpr(), refVars); + return EvalVal(U->getSubExpr(), refVars, ParentDecl); else return NULL; } @@ -3153,7 +3433,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { if (!Base->getType()->isPointerType()) Base = B->getRHS(); assert (Base->getType()->isPointerType()); - return EvalAddr(Base, refVars); + return EvalAddr(Base, refVars, ParentDecl); } // For conditional operators we need to see if either the LHS or RHS are @@ -3165,7 +3445,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { if (Expr *lhsExpr = C->getLHS()) { // In C++, we can have a throw-expression, which has 'void' type. if (!lhsExpr->getType()->isVoidType()) - if (Expr* LHS = EvalAddr(lhsExpr, refVars)) + if (Expr* LHS = EvalAddr(lhsExpr, refVars, ParentDecl)) return LHS; } @@ -3173,7 +3453,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { if (C->getRHS()->getType()->isVoidType()) return NULL; - return EvalAddr(C->getRHS(), refVars); + return EvalAddr(C->getRHS(), refVars, ParentDecl); } case Stmt::BlockExprClass: @@ -3185,7 +3465,8 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { return E; // address of label. case Stmt::ExprWithCleanupsClass: - return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars); + return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars, + ParentDecl); // For casts, we need to handle conversions from arrays to // pointer values, and pointer-to-pointer conversions. @@ -3209,10 +3490,10 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: - return EvalAddr(SubExpr, refVars); + return EvalAddr(SubExpr, refVars, ParentDecl); case CK_ArrayToPointerDecay: - return EvalVal(SubExpr, refVars); + return EvalVal(SubExpr, refVars, ParentDecl); default: return 0; @@ -3222,7 +3503,7 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { case Stmt::MaterializeTemporaryExprClass: if (Expr *Result = EvalAddr( cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(), - refVars)) + refVars, ParentDecl)) return Result; return E; @@ -3236,7 +3517,8 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { /// EvalVal - This function is complements EvalAddr in the mutual recursion. /// See the comments for EvalAddr for more details. -static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars) { +static Expr *EvalVal(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars, + Decl *ParentDecl) { do { // We should only be called for evaluating non-pointer expressions, or // expressions with a pointer type that are not used as references but instead @@ -3258,7 +3540,7 @@ do { } case Stmt::ExprWithCleanupsClass: - return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars); + return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,ParentDecl); case Stmt::DeclRefExprClass: { // When we hit a DeclRefExpr we are looking at code that refers to a @@ -3266,7 +3548,11 @@ do { // local storage within the function, and if so, return the expression. DeclRefExpr *DR = cast<DeclRefExpr>(E); - if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) + if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) { + // Check if it refers to itself, e.g. "int& i = i;". + if (V == ParentDecl) + return DR; + if (V->hasLocalStorage()) { if (!V->getType()->isReferenceType()) return DR; @@ -3276,9 +3562,10 @@ do { if (V->hasInit()) { // Add the reference variable to the "trail". refVars.push_back(DR); - return EvalVal(V->getInit(), refVars); + return EvalVal(V->getInit(), refVars, V); } } + } return NULL; } @@ -3290,7 +3577,7 @@ do { UnaryOperator *U = cast<UnaryOperator>(E); if (U->getOpcode() == UO_Deref) - return EvalAddr(U->getSubExpr(), refVars); + return EvalAddr(U->getSubExpr(), refVars, ParentDecl); return NULL; } @@ -3299,7 +3586,7 @@ do { // Array subscripts are potential references to data on the stack. We // retrieve the DeclRefExpr* for the array variable if it indeed // has local storage. - return EvalAddr(cast<ArraySubscriptExpr>(E)->getBase(), refVars); + return EvalAddr(cast<ArraySubscriptExpr>(E)->getBase(), refVars,ParentDecl); } case Stmt::ConditionalOperatorClass: { @@ -3309,10 +3596,10 @@ do { // Handle the GNU extension for missing LHS. if (Expr *lhsExpr = C->getLHS()) - if (Expr *LHS = EvalVal(lhsExpr, refVars)) + if (Expr *LHS = EvalVal(lhsExpr, refVars, ParentDecl)) return LHS; - return EvalVal(C->getRHS(), refVars); + return EvalVal(C->getRHS(), refVars, ParentDecl); } // Accesses to members are potential references to data on the stack. @@ -3328,13 +3615,13 @@ do { if (M->getMemberDecl()->getType()->isReferenceType()) return NULL; - return EvalVal(M->getBase(), refVars); + return EvalVal(M->getBase(), refVars, ParentDecl); } case Stmt::MaterializeTemporaryExprClass: if (Expr *Result = EvalVal( cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(), - refVars)) + refVars, ParentDecl)) return Result; return E; @@ -3357,8 +3644,6 @@ do { /// Issue a warning if these are no self-comparisons, as they are not likely /// to do what the programmer intended. void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) { - bool EmitWarning = true; - Expr* LeftExprSansParen = LHS->IgnoreParenImpCasts(); Expr* RightExprSansParen = RHS->IgnoreParenImpCasts(); @@ -3367,7 +3652,7 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) { if (DeclRefExpr* DRL = dyn_cast<DeclRefExpr>(LeftExprSansParen)) if (DeclRefExpr* DRR = dyn_cast<DeclRefExpr>(RightExprSansParen)) if (DRL->getDecl() == DRR->getDecl()) - EmitWarning = false; + return; // Special case: check for comparisons against literals that can be exactly @@ -3375,32 +3660,26 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) { // is a heuristic: often comparison against such literals are used to // detect if a value in a variable has not changed. This clearly can // lead to false negatives. - if (EmitWarning) { - if (FloatingLiteral* FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) { - if (FLL->isExact()) - EmitWarning = false; - } else - if (FloatingLiteral* FLR = dyn_cast<FloatingLiteral>(RightExprSansParen)){ - if (FLR->isExact()) - EmitWarning = false; - } - } + if (FloatingLiteral* FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) { + if (FLL->isExact()) + return; + } else + if (FloatingLiteral* FLR = dyn_cast<FloatingLiteral>(RightExprSansParen)) + if (FLR->isExact()) + return; // Check for comparisons with builtin types. - if (EmitWarning) - if (CallExpr* CL = dyn_cast<CallExpr>(LeftExprSansParen)) - if (CL->isBuiltinCall()) - EmitWarning = false; + if (CallExpr* CL = dyn_cast<CallExpr>(LeftExprSansParen)) + if (CL->isBuiltinCall()) + return; - if (EmitWarning) - if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen)) - if (CR->isBuiltinCall()) - EmitWarning = false; + if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen)) + if (CR->isBuiltinCall()) + return; // Emit the diagnostic. - if (EmitWarning) - Diag(Loc, diag::warn_floatingpoint_eq) - << LHS->getSourceRange() << RHS->getSourceRange(); + Diag(Loc, diag::warn_floatingpoint_eq) + << LHS->getSourceRange() << RHS->getSourceRange(); } //===--- CHECK: Integer mixed-sign comparisons (-Wsign-compare) --------===// @@ -3927,9 +4206,10 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) { return; } - S.Diag(E->getOperatorLoc(), diag::warn_mixed_sign_comparison) - << LHS->getType() << RHS->getType() - << LHS->getSourceRange() << RHS->getSourceRange(); + S.DiagRuntimeBehavior(E->getOperatorLoc(), E, + S.PDiag(diag::warn_mixed_sign_comparison) + << LHS->getType() << RHS->getType() + << LHS->getSourceRange() << RHS->getSourceRange()); } /// Analyzes an attempt to assign the given value to a bitfield. @@ -3970,7 +4250,7 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init, // Check whether the stored value is equal to the original value. TruncatedValue = TruncatedValue.extend(OriginalWidth); - if (Value == TruncatedValue) + if (llvm::APSInt::isSameValue(Value, TruncatedValue)) return false; // Special-case bitfields of width 1: booleans are naturally 0/1, and @@ -4044,8 +4324,17 @@ void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T, == llvm::APFloat::opOK && isExact) return; + SmallString<16> PrettySourceValue; + Value.toString(PrettySourceValue); + SmallString<16> PrettyTargetValue; + if (T->isSpecificBuiltinType(BuiltinType::Bool)) + PrettyTargetValue = IntegerValue == 0 ? "false" : "true"; + else + IntegerValue.toString(PrettyTargetValue); + S.Diag(FL->getExprLoc(), diag::warn_impcast_literal_float_to_integer) - << FL->getType() << T << FL->getSourceRange() << SourceRange(CContext); + << FL->getType() << T.getUnqualifiedType() << PrettySourceValue + << PrettyTargetValue << FL->getSourceRange() << SourceRange(CContext); } std::string PrettyPrintInRange(const llvm::APSInt &Value, IntRange Range) { @@ -4112,7 +4401,6 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, } } } - return; // Other casts to bool are not checked. } // Strip vector types. @@ -4176,7 +4464,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, } // If the target is integral, always warn. - if ((TargetBT && TargetBT->isInteger())) { + if (TargetBT && TargetBT->isInteger()) { if (S.SourceMgr.isInSystemMacro(CC)) return; @@ -4196,19 +4484,26 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; } - if (!Source->isIntegerType() || !Target->isIntegerType()) - return; - if ((E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull) - == Expr::NPCK_GNUNull) && Target->isIntegerType()) { + == Expr::NPCK_GNUNull) && !Target->isAnyPointerType() + && !Target->isBlockPointerType() && !Target->isMemberPointerType()) { SourceLocation Loc = E->getSourceRange().getBegin(); if (Loc.isMacroID()) Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first; - S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer) - << T << Loc << clang::SourceRange(CC); - return; + if (!Loc.isMacroID() || CC.isMacroID()) + S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer) + << T << clang::SourceRange(CC) + << FixItHint::CreateReplacement(Loc, S.getFixItZeroLiteralForType(T)); } + if (!Source->isIntegerType() || !Target->isIntegerType()) + return; + + // TODO: remove this early return once the false positives for constant->bool + // in templates, macros, etc, are reduced or removed. + if (Target->isSpecificBuiltinType(BuiltinType::Bool)) + return; + IntRange SourceRange = GetExprRange(S.Context, E); IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target); @@ -4293,14 +4588,15 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; } -void CheckConditionalOperator(Sema &S, ConditionalOperator *E, QualType T); +void CheckConditionalOperator(Sema &S, ConditionalOperator *E, + SourceLocation CC, QualType T); void CheckConditionalOperand(Sema &S, Expr *E, QualType T, SourceLocation CC, bool &ICContext) { E = E->IgnoreParenImpCasts(); if (isa<ConditionalOperator>(E)) - return CheckConditionalOperator(S, cast<ConditionalOperator>(E), T); + return CheckConditionalOperator(S, cast<ConditionalOperator>(E), CC, T); AnalyzeImplicitConversions(S, E, CC); if (E->getType() != T) @@ -4308,9 +4604,8 @@ void CheckConditionalOperand(Sema &S, Expr *E, QualType T, return; } -void CheckConditionalOperator(Sema &S, ConditionalOperator *E, QualType T) { - SourceLocation CC = E->getQuestionLoc(); - +void CheckConditionalOperator(Sema &S, ConditionalOperator *E, + SourceLocation CC, QualType T) { AnalyzeImplicitConversions(S, E->getCond(), CC); bool Suspicious = false; @@ -4352,7 +4647,7 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) { // were being fed directly into the output. if (isa<ConditionalOperator>(E)) { ConditionalOperator *CO = cast<ConditionalOperator>(E); - CheckConditionalOperator(S, CO, T); + CheckConditionalOperator(S, CO, CC, T); return; } @@ -4417,7 +4712,7 @@ void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC) { /// conversion void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) { // Don't diagnose in unevaluated contexts. - if (ExprEvalContexts.back().Context == Sema::Unevaluated) + if (isUnevaluatedContext()) return; // Don't diagnose for value- or type-dependent expressions. @@ -4457,7 +4752,7 @@ bool Sema::CheckParmsForFunctionDef(ParmVarDecl **P, ParmVarDecl **PEnd, // This is also C++ [dcl.fct]p6. if (!Param->isInvalidDecl() && RequireCompleteType(Param->getLocation(), Param->getType(), - diag::err_typecheck_decl_incomplete_type)) { + diag::err_typecheck_decl_incomplete_type)) { Param->setInvalidDecl(); HasInvalidParm = true; } @@ -4478,7 +4773,7 @@ bool Sema::CheckParmsForFunctionDef(ParmVarDecl **P, ParmVarDecl **PEnd, QualType PType = Param->getOriginalType(); if (const ArrayType *AT = Context.getAsArrayType(PType)) { if (AT->getSizeModifier() == ArrayType::Star) { - // FIXME: This diagnosic should point the the '[*]' if source-location + // FIXME: This diagnosic should point the '[*]' if source-location // information is added for it. Diag(Param->getLocation(), diag::err_array_star_in_function_definition); } @@ -4556,11 +4851,23 @@ static bool IsTailPaddedMemberArray(Sema &S, llvm::APInt Size, // Don't consider sizes resulting from macro expansions or template argument // substitution to form C89 tail-padded arrays. - ConstantArrayTypeLoc TL = - cast<ConstantArrayTypeLoc>(FD->getTypeSourceInfo()->getTypeLoc()); - const Expr *SizeExpr = dyn_cast<IntegerLiteral>(TL.getSizeExpr()); - if (!SizeExpr || SizeExpr->getExprLoc().isMacroID()) - return false; + + TypeSourceInfo *TInfo = FD->getTypeSourceInfo(); + while (TInfo) { + TypeLoc TL = TInfo->getTypeLoc(); + // Look through typedefs. + const TypedefTypeLoc *TTL = dyn_cast<TypedefTypeLoc>(&TL); + if (TTL) { + const TypedefNameDecl *TDL = TTL->getTypedefNameDecl(); + TInfo = TDL->getTypeSourceInfo(); + continue; + } + ConstantArrayTypeLoc CTL = cast<ConstantArrayTypeLoc>(TL); + const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr()); + if (!SizeExpr || SizeExpr->getExprLoc().isMacroID()) + return false; + break; + } const RecordDecl *RD = dyn_cast<RecordDecl>(FD->getDeclContext()); if (!RD) return false; @@ -4966,7 +5273,7 @@ bool Sema::checkUnsafeAssigns(SourceLocation Loc, while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) { if (cast->getCastKind() == CK_ARCConsumeObject) { Diag(Loc, diag::warn_arc_retained_assign) - << (LT == Qualifiers::OCL_ExplicitNone) + << (LT == Qualifiers::OCL_ExplicitNone) << 1 << RHS->getSourceRange(); return true; } @@ -5023,6 +5330,16 @@ void Sema::checkUnsafeExprAssigns(SourceLocation Loc, RHS = cast->getSubExpr(); } } + else if (Attributes & ObjCPropertyDecl::OBJC_PR_weak) { + while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) { + if (cast->getCastKind() == CK_ARCConsumeObject) { + Diag(Loc, diag::warn_arc_retained_assign) + << 0 << 0<< RHS->getSourceRange(); + return; + } + RHS = cast->getSubExpr(); + } + } } } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 1ee75329e086..9fa757d50a29 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -158,7 +158,7 @@ namespace { /// \brief The completion context in which we are gathering results. CodeCompletionContext CompletionContext; - /// \brief If we are in an instance method definition, the @implementation + /// \brief If we are in an instance method definition, the \@implementation /// object. ObjCImplementationDecl *ObjCImplementation; @@ -1181,7 +1181,7 @@ bool ResultBuilder::IsImpossibleToSatisfy(NamedDecl *ND) const { return false; } -/// \rief Determines whether the given declaration is an Objective-C +/// \brief Determines whether the given declaration is an Objective-C /// instance variable. bool ResultBuilder::IsObjCIvar(NamedDecl *ND) const { return isa<ObjCIvarDecl>(ND); @@ -1414,7 +1414,7 @@ static const char *GetCompletionTypeString(QualType T, if (!T.getLocalQualifiers()) { // Built-in type names are constant strings. if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) - return BT->getName(Policy); + return BT->getNameAsCString(Policy); // Anonymous tag types are constant strings. if (const TagType *TagT = dyn_cast<TagType>(T)) @@ -1955,6 +1955,19 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, AddObjCExpressionResults(Results, true); } + if (SemaRef.getLangOpts().C11) { + // _Alignof + Builder.AddResultTypeChunk("size_t"); + if (SemaRef.getASTContext().Idents.get("alignof").hasMacroDefinition()) + Builder.AddTypedTextChunk("alignof"); + else + Builder.AddTypedTextChunk("_Alignof"); + Builder.AddChunk(CodeCompletionString::CK_LeftParen); + Builder.AddPlaceholderChunk("type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Results.AddResult(Result(Builder.TakeString())); + } + // sizeof expression Builder.AddResultTypeChunk("size_t"); Builder.AddTypedTextChunk("sizeof"); @@ -2356,11 +2369,11 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result, // Handle multiple qualifiers. std::string QualsStr; - if (Proto->getTypeQuals() & Qualifiers::Const) + if (Proto->isConst()) QualsStr += " const"; - if (Proto->getTypeQuals() & Qualifiers::Volatile) + if (Proto->isVolatile()) QualsStr += " volatile"; - if (Proto->getTypeQuals() & Qualifiers::Restrict) + if (Proto->isRestrict()) QualsStr += " restrict"; Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr)); } @@ -2440,8 +2453,10 @@ static void AddTypedNameChunk(ASTContext &Context, const PrintingPolicy &Policy, CodeCompletionString *CodeCompletionResult::CreateCodeCompletionString(Sema &S, CodeCompletionAllocator &Allocator, - CodeCompletionTUInfo &CCTUInfo) { - return CreateCodeCompletionString(S.Context, S.PP, Allocator, CCTUInfo); + CodeCompletionTUInfo &CCTUInfo, + bool IncludeBriefComments) { + return CreateCodeCompletionString(S.Context, S.PP, Allocator, CCTUInfo, + IncludeBriefComments); } /// \brief If possible, create a new code completion string for the given @@ -2454,7 +2469,8 @@ CodeCompletionString * CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx, Preprocessor &PP, CodeCompletionAllocator &Allocator, - CodeCompletionTUInfo &CCTUInfo) { + CodeCompletionTUInfo &CCTUInfo, + bool IncludeBriefComments) { CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability); PrintingPolicy Policy = getCompletionPrintingPolicy(Ctx, PP); @@ -2524,7 +2540,14 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx, assert(Kind == RK_Declaration && "Missed a result kind?"); NamedDecl *ND = Declaration; Result.addParentContext(ND->getDeclContext()); - + + if (IncludeBriefComments) { + // Add documentation comment, if it exists. + if (const RawComment *RC = Ctx.getRawCommentForAnyRedecl(ND)) { + Result.addBriefComment(RC->getBriefText(Ctx)); + } + } + if (StartsNestedNameSpecifier) { Result.AddTypedTextChunk( Result.getAllocator().CopyString(ND->getNameAsString())); @@ -2842,6 +2865,7 @@ CXCursorKind clang::getCursorKindForDecl(Decl *D) { case Decl::ClassTemplatePartialSpecialization: return CXCursor_ClassTemplatePartialSpecialization; case Decl::UsingDirective: return CXCursor_UsingDirective; + case Decl::TranslationUnit: return CXCursor_TranslationUnit; case Decl::Using: case Decl::UnresolvedUsingValue: @@ -3270,9 +3294,6 @@ struct Sema::CodeCompleteExpressionData { /// \brief Perform code-completion in an expression context when we know what /// type we're looking for. -/// -/// \param IntegralConstantExpression Only permit integral constant -/// expressions. void Sema::CodeCompleteExpression(Scope *S, const CodeCompleteExpressionData &Data) { typedef CodeCompletionResult Result; @@ -3333,7 +3354,25 @@ void Sema::CodeCompletePostfixExpression(Scope *S, ExprResult E) { /// property name. typedef llvm::SmallPtrSet<IdentifierInfo*, 16> AddedPropertiesSet; -static void AddObjCProperties(ObjCContainerDecl *Container, +/// \brief Retrieve the container definition, if any? +static ObjCContainerDecl *getContainerDef(ObjCContainerDecl *Container) { + if (ObjCInterfaceDecl *Interface = dyn_cast<ObjCInterfaceDecl>(Container)) { + if (Interface->hasDefinition()) + return Interface->getDefinition(); + + return Interface; + } + + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) { + if (Protocol->hasDefinition()) + return Protocol->getDefinition(); + + return Protocol; + } + return Container; +} + +static void AddObjCProperties(ObjCContainerDecl *Container, bool AllowCategories, bool AllowNullaryMethods, DeclContext *CurContext, @@ -3341,6 +3380,9 @@ static void AddObjCProperties(ObjCContainerDecl *Container, ResultBuilder &Results) { typedef CodeCompletionResult Result; + // Retrieve the definition. + Container = getContainerDef(Container); + // Add properties in this container. for (ObjCContainerDecl::prop_iterator P = Container->prop_begin(), PEnd = Container->prop_end(); @@ -3616,6 +3658,8 @@ void Sema::CodeCompleteCase(Scope *S) { // Code-complete the cases of a switch statement over an enumeration type // by providing the list of EnumDecl *Enum = type->castAs<EnumType>()->getDecl(); + if (EnumDecl *Def = Enum->getDefinition()) + Enum = Def; // Determine which enumerators we have already seen in the switch statement. // FIXME: Ideally, we would also be able to look *past* the code-completion @@ -4273,27 +4317,28 @@ void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro, Results.data(), Results.size()); } -// Macro that expands to @Keyword or Keyword, depending on whether NeedAt is -// true or false. -#define OBJC_AT_KEYWORD_NAME(NeedAt,Keyword) NeedAt? "@" #Keyword : #Keyword +/// Macro that optionally prepends an "@" to the string literal passed in via +/// Keyword, depending on whether NeedAt is true or false. +#define OBJC_AT_KEYWORD_NAME(NeedAt,Keyword) ((NeedAt)? "@" Keyword : Keyword) + static void AddObjCImplementationResults(const LangOptions &LangOpts, ResultBuilder &Results, bool NeedAt) { typedef CodeCompletionResult Result; // Since we have an implementation, we can end it. - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,end))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"end"))); CodeCompletionBuilder Builder(Results.getAllocator(), Results.getCodeCompletionTUInfo()); if (LangOpts.ObjC2) { // @dynamic - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,dynamic)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"dynamic")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("property"); Results.AddResult(Result(Builder.TakeString())); // @synthesize - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,synthesize)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"synthesize")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("property"); Results.AddResult(Result(Builder.TakeString())); @@ -4306,17 +4351,17 @@ static void AddObjCInterfaceResults(const LangOptions &LangOpts, typedef CodeCompletionResult Result; // Since we have an interface or protocol, we can end it. - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,end))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"end"))); if (LangOpts.ObjC2) { // @property - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,property))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"property"))); // @required - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,required))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"required"))); // @optional - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,optional))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"optional"))); } } @@ -4326,7 +4371,7 @@ static void AddObjCTopLevelResults(ResultBuilder &Results, bool NeedAt) { Results.getCodeCompletionTUInfo()); // @class name ; - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,class)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"class")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("name"); Results.AddResult(Result(Builder.TakeString())); @@ -4335,26 +4380,26 @@ static void AddObjCTopLevelResults(ResultBuilder &Results, bool NeedAt) { // @interface name // FIXME: Could introduce the whole pattern, including superclasses and // such. - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,interface)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"interface")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("class"); Results.AddResult(Result(Builder.TakeString())); // @protocol name - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,protocol)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"protocol")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("protocol"); Results.AddResult(Result(Builder.TakeString())); // @implementation name - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,implementation)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"implementation")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("class"); Results.AddResult(Result(Builder.TakeString())); } // @compatibility_alias name - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,compatibility_alias)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"compatibility_alias")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("alias"); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); @@ -4389,9 +4434,9 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) { const char *EncodeType = "char[]"; if (Results.getSema().getLangOpts().CPlusPlus || Results.getSema().getLangOpts().ConstStrings) - EncodeType = " const char[]"; + EncodeType = "const char[]"; Builder.AddResultTypeChunk(EncodeType); - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,encode)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"encode")); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("type-name"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -4399,7 +4444,7 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) { // @protocol ( protocol-name ) Builder.AddResultTypeChunk("Protocol *"); - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,protocol)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"protocol")); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("protocol-name"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -4407,31 +4452,43 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) { // @selector ( selector ) Builder.AddResultTypeChunk("SEL"); - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,selector)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"selector")); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("selector"); Builder.AddChunk(CodeCompletionString::CK_RightParen); Results.AddResult(Result(Builder.TakeString())); - - // @[ objects, ... ] - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,[)); - Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + + // @"string" + Builder.AddResultTypeChunk("NSString *"); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"\"")); + Builder.AddPlaceholderChunk("string"); + Builder.AddTextChunk("\""); + Results.AddResult(Result(Builder.TakeString())); + + // @[objects, ...] + Builder.AddResultTypeChunk("NSArray *"); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"[")); Builder.AddPlaceholderChunk("objects, ..."); - Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_RightBracket); Results.AddResult(Result(Builder.TakeString())); - // @{ key : object, ... } - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,{)); - Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + // @{key : object, ...} + Builder.AddResultTypeChunk("NSDictionary *"); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"{")); Builder.AddPlaceholderChunk("key"); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_Colon); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("object, ..."); - Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_RightBrace); Results.AddResult(Result(Builder.TakeString())); + + // @(expression) + Builder.AddResultTypeChunk("id"); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt, "(")); + Builder.AddPlaceholderChunk("expression"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Results.AddResult(Result(Builder.TakeString())); } static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) { @@ -4442,7 +4499,7 @@ static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) { if (Results.includeCodePatterns()) { // @try { statements } @catch ( declaration ) { statements } @finally // { statements } - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,try)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"try")); Builder.AddChunk(CodeCompletionString::CK_LeftBrace); Builder.AddPlaceholderChunk("statements"); Builder.AddChunk(CodeCompletionString::CK_RightBrace); @@ -4461,14 +4518,14 @@ static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) { } // @throw - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,throw)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"throw")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("expression"); Results.AddResult(Result(Builder.TakeString())); if (Results.includeCodePatterns()) { // @synchronized ( expression ) { statements } - Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,synchronized)); + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"synchronized")); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("expression"); @@ -4484,11 +4541,11 @@ static void AddObjCVisibilityResults(const LangOptions &LangOpts, ResultBuilder &Results, bool NeedAt) { typedef CodeCompletionResult Result; - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,private))); - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,protected))); - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,public))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"private"))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"protected"))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"public"))); if (LangOpts.ObjC2) - Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,package))); + Results.AddResult(Result(OBJC_AT_KEYWORD_NAME(NeedAt,"package"))); } void Sema::CodeCompleteObjCAtVisibility(Scope *S) { @@ -4616,12 +4673,12 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { Results.data(),Results.size()); } -/// \brief Descripts the kind of Objective-C method that we want to find +/// \brief Describes the kind of Objective-C method that we want to find /// via code completion. enum ObjCMethodKind { - MK_Any, //< Any kind of method, provided it means other specified criteria. - MK_ZeroArgSelector, //< Zero-argument (unary) selector. - MK_OneArgSelector //< One-argument selector. + MK_Any, ///< Any kind of method, provided it means other specified criteria. + MK_ZeroArgSelector, ///< Zero-argument (unary) selector. + MK_OneArgSelector ///< One-argument selector. }; static bool isAcceptableObjCSelector(Selector Sel, @@ -4673,8 +4730,8 @@ namespace { /// /// \param Container the container in which we'll look to find methods. /// -/// \param WantInstance whether to add instance methods (only); if false, this -/// routine will add factory methods (only). +/// \param WantInstanceMethods Whether to add instance methods (only); if +/// false, this routine will add factory methods (only). /// /// \param CurContext the context in which we're performing the lookup that /// finds methods. @@ -4694,17 +4751,18 @@ static void AddObjCMethods(ObjCContainerDecl *Container, ResultBuilder &Results, bool InOriginalClass = true) { typedef CodeCompletionResult Result; + Container = getContainerDef(Container); for (ObjCContainerDecl::method_iterator M = Container->meth_begin(), MEnd = Container->meth_end(); M != MEnd; ++M) { - if ((*M)->isInstanceMethod() == WantInstanceMethods) { + if (M->isInstanceMethod() == WantInstanceMethods) { // Check whether the selector identifiers we've been given are a // subset of the identifiers for this particular method. if (!isAcceptableObjCMethod(*M, WantKind, SelIdents, NumSelIdents, AllowSameLength)) continue; - if (!Selectors.insert((*M)->getSelector())) + if (!Selectors.insert(M->getSelector())) continue; Result R = Result(*M, 0); @@ -5825,7 +5883,8 @@ void Sema::CodeCompleteObjCPropertyDefinition(Scope *S) { return; // Ignore any properties that have already been implemented. - for (DeclContext::decl_iterator D = Container->decls_begin(), + Container = getContainerDef(Container); + for (DeclContext::decl_iterator D = Container->decls_begin(), DEnd = Container->decls_end(); D != DEnd; ++D) if (ObjCPropertyImplDecl *PropertyImpl = dyn_cast<ObjCPropertyImplDecl>(*D)) @@ -5958,9 +6017,12 @@ static void FindImplementableMethods(ASTContext &Context, KnownMethodsMap &KnownMethods, bool InOriginalClass = true) { if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container)) { - // Recurse into protocols. + // Make sure we have a definition; that's what we'll walk. if (!IFace->hasDefinition()) return; + + IFace = IFace->getDefinition(); + Container = IFace; const ObjCList<ObjCProtocolDecl> &Protocols = IFace->getReferencedProtocols(); @@ -6002,16 +6064,20 @@ static void FindImplementableMethods(ASTContext &Context, } if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) { - if (Protocol->hasDefinition()) { - // Recurse into protocols. - const ObjCList<ObjCProtocolDecl> &Protocols - = Protocol->getReferencedProtocols(); - for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), - E = Protocols.end(); - I != E; ++I) - FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType, - KnownMethods, false); - } + // Make sure we have a definition; that's what we'll walk. + if (!Protocol->hasDefinition()) + return; + Protocol = Protocol->getDefinition(); + Container = Protocol; + + // Recurse into protocols. + const ObjCList<ObjCProtocolDecl> &Protocols + = Protocol->getReferencedProtocols(); + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), + E = Protocols.end(); + I != E; ++I) + FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType, + KnownMethods, false); } // Add methods in this container. This operation occurs last because @@ -6020,12 +6086,12 @@ static void FindImplementableMethods(ASTContext &Context, for (ObjCContainerDecl::method_iterator M = Container->meth_begin(), MEnd = Container->meth_end(); M != MEnd; ++M) { - if ((*M)->isInstanceMethod() == WantInstanceMethods) { + if (M->isInstanceMethod() == WantInstanceMethods) { if (!ReturnType.isNull() && - !Context.hasSameUnqualifiedType(ReturnType, (*M)->getResultType())) + !Context.hasSameUnqualifiedType(ReturnType, M->getResultType())) continue; - KnownMethods[(*M)->getSelector()] = std::make_pair(*M, InOriginalClass); + KnownMethods[M->getSelector()] = std::make_pair(*M, InOriginalClass); } } } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 1227e92f76eb..3aae99ab74e2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -21,6 +21,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/CommentDiagnostic.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -60,7 +61,8 @@ namespace { class TypeNameValidatorCCC : public CorrectionCandidateCallback { public: - TypeNameValidatorCCC(bool AllowInvalid) : AllowInvalidDecl(AllowInvalid) { + TypeNameValidatorCCC(bool AllowInvalid, bool WantClass=false) + : AllowInvalidDecl(AllowInvalid), WantClassName(WantClass) { WantExpressionKeywords = false; WantCXXNamedCasts = false; WantRemainingKeywords = false; @@ -71,15 +73,52 @@ class TypeNameValidatorCCC : public CorrectionCandidateCallback { return (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND)) && (AllowInvalidDecl || !ND->isInvalidDecl()); else - return candidate.isKeyword(); + return !WantClassName && candidate.isKeyword(); } private: bool AllowInvalidDecl; + bool WantClassName; }; } +/// \brief Determine whether the token kind starts a simple-type-specifier. +bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { + switch (Kind) { + // FIXME: Take into account the current language when deciding whether a + // token kind is a valid type specifier + case tok::kw_short: + case tok::kw_long: + case tok::kw___int64: + case tok::kw___int128: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_half: + case tok::kw_float: + case tok::kw_double: + case tok::kw_wchar_t: + case tok::kw_bool: + case tok::kw___underlying_type: + return true; + + case tok::annot_typename: + case tok::kw_char16_t: + case tok::kw_char32_t: + case tok::kw_typeof: + case tok::kw_decltype: + return getLangOpts().CPlusPlus; + + default: + break; + } + + return false; +} + /// \brief If the identifier refers to a type name within this scope, /// return the declaration of that type. /// @@ -173,7 +212,7 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: if (CorrectedII) { - TypeNameValidatorCCC Validator(true); + TypeNameValidatorCCC Validator(true, isClassName); TypoCorrection Correction = CorrectTypo(Result.getLookupNameInfo(), Kind, S, SS, Validator); IdentifierInfo *NewII = Correction.getCorrectionAsIdentifierInfo(); @@ -202,8 +241,8 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, std::string CorrectedStr(Correction.getAsString(getLangOpts())); std::string CorrectedQuotedStr( Correction.getQuoted(getLangOpts())); - Diag(NameLoc, diag::err_unknown_typename_suggest) - << Result.getLookupName() << CorrectedQuotedStr + Diag(NameLoc, diag::err_unknown_type_or_class_name_suggest) + << Result.getLookupName() << CorrectedQuotedStr << isClassName << FixItHint::CreateReplacement(SourceRange(NameLoc), CorrectedStr); if (NamedDecl *FirstDecl = Correction.getCorrectionDecl()) @@ -359,7 +398,7 @@ bool Sema::isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S) { return CurContext->isFunctionOrMethod() || S->isFunctionPrototypeScope(); } -bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, +bool Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, SourceLocation IILoc, Scope *S, CXXScopeSpec *SS, @@ -370,7 +409,7 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, // There may have been a typo in the name of the type. Look up typo // results, in case we have something that we can suggest. TypeNameValidatorCCC Validator(false); - if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(&II, IILoc), + if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(II, IILoc), LookupOrdinaryName, S, SS, Validator)) { std::string CorrectedStr(Corrected.getAsString(getLangOpts())); @@ -378,19 +417,23 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, if (Corrected.isKeyword()) { // We corrected to a keyword. - // FIXME: Actually recover with the keyword we suggest, and emit a fix-it. + IdentifierInfo *NewII = Corrected.getCorrectionAsIdentifierInfo(); + if (!isSimpleTypeSpecifier(NewII->getTokenID())) + CorrectedQuotedStr = "the keyword " + CorrectedQuotedStr; Diag(IILoc, diag::err_unknown_typename_suggest) - << &II << CorrectedQuotedStr; + << II << CorrectedQuotedStr + << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); + II = NewII; } else { NamedDecl *Result = Corrected.getCorrectionDecl(); // We found a similarly-named type or interface; suggest that. if (!SS || !SS->isSet()) Diag(IILoc, diag::err_unknown_typename_suggest) - << &II << CorrectedQuotedStr + << II << CorrectedQuotedStr << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); else if (DeclContext *DC = computeDeclContext(*SS, false)) Diag(IILoc, diag::err_unknown_nested_typename_suggest) - << &II << DC << CorrectedQuotedStr << SS->getRange() + << II << DC << CorrectedQuotedStr << SS->getRange() << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); else llvm_unreachable("could not have corrected a typo here"); @@ -409,7 +452,7 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, if (getLangOpts().CPlusPlus) { // See if II is a class template that the user forgot to pass arguments to. UnqualifiedId Name; - Name.setIdentifier(&II, IILoc); + Name.setIdentifier(II, IILoc); CXXScopeSpec EmptySS; TemplateTy TemplateResult; bool MemberOfUnknownSpecialization; @@ -430,21 +473,21 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, // (struct, union, enum) from Parser::ParseImplicitInt here, instead? if (!SS || (!SS->isSet() && !SS->isInvalid())) - Diag(IILoc, diag::err_unknown_typename) << &II; + Diag(IILoc, diag::err_unknown_typename) << II; else if (DeclContext *DC = computeDeclContext(*SS, false)) Diag(IILoc, diag::err_typename_nested_not_found) - << &II << DC << SS->getRange(); + << II << DC << SS->getRange(); else if (isDependentScopeSpecifier(*SS)) { unsigned DiagID = diag::err_typename_missing; if (getLangOpts().MicrosoftMode && isMicrosoftMissingTypename(SS, S)) DiagID = diag::warn_typename_missing; Diag(SS->getRange().getBegin(), DiagID) - << (NestedNameSpecifier *)SS->getScopeRep() << II.getName() + << (NestedNameSpecifier *)SS->getScopeRep() << II->getName() << SourceRange(SS->getRange().getBegin(), IILoc) << FixItHint::CreateInsertion(SS->getRange().getBegin(), "typename "); - SuggestedType = ActOnTypenameType(S, SourceLocation(), *SS, II, IILoc) - .get(); + SuggestedType = ActOnTypenameType(S, SourceLocation(), + *SS, *II, IILoc).get(); } else { assert(SS && SS->isInvalid() && "Invalid scope specifier has already been diagnosed"); @@ -470,6 +513,55 @@ static bool isResultTypeOrTemplate(LookupResult &R, const Token &NextToken) { return false; } +static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, + Scope *S, CXXScopeSpec &SS, + IdentifierInfo *&Name, + SourceLocation NameLoc) { + Result.clear(Sema::LookupTagName); + SemaRef.LookupParsedName(Result, S, &SS); + if (TagDecl *Tag = Result.getAsSingle<TagDecl>()) { + const char *TagName = 0; + const char *FixItTagName = 0; + switch (Tag->getTagKind()) { + case TTK_Class: + TagName = "class"; + FixItTagName = "class "; + break; + + case TTK_Enum: + TagName = "enum"; + FixItTagName = "enum "; + break; + + case TTK_Struct: + TagName = "struct"; + FixItTagName = "struct "; + break; + + case TTK_Union: + TagName = "union"; + FixItTagName = "union "; + break; + } + + SemaRef.Diag(NameLoc, diag::err_use_of_tag_name_without_tag) + << Name << TagName << SemaRef.getLangOpts().CPlusPlus + << FixItHint::CreateInsertion(NameLoc, FixItTagName); + + LookupResult R(SemaRef, Name, NameLoc, Sema::LookupOrdinaryName); + if (SemaRef.LookupParsedName(R, S, &SS)) { + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); + I != IEnd; ++I) + SemaRef.Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) + << Name << TagName; + } + return true; + } + + Result.clear(Sema::LookupOrdinaryName); + return false; +} + Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, @@ -533,41 +625,9 @@ Corrected: // In C, we first see whether there is a tag type by the same name, in // which case it's likely that the user just forget to write "enum", // "struct", or "union". - if (!getLangOpts().CPlusPlus && !SecondTry) { - Result.clear(LookupTagName); - LookupParsedName(Result, S, &SS); - if (TagDecl *Tag = Result.getAsSingle<TagDecl>()) { - const char *TagName = 0; - const char *FixItTagName = 0; - switch (Tag->getTagKind()) { - case TTK_Class: - TagName = "class"; - FixItTagName = "class "; - break; - - case TTK_Enum: - TagName = "enum"; - FixItTagName = "enum "; - break; - - case TTK_Struct: - TagName = "struct"; - FixItTagName = "struct "; - break; - - case TTK_Union: - TagName = "union"; - FixItTagName = "union "; - break; - } - - Diag(NameLoc, diag::err_use_of_tag_name_without_tag) - << Name << TagName << getLangOpts().CPlusPlus - << FixItHint::CreateInsertion(NameLoc, FixItTagName); - break; - } - - Result.clear(LookupOrdinaryName); + if (!getLangOpts().CPlusPlus && !SecondTry && + isTagTypeWithMissingTag(*this, Result, S, SS, Name, NameLoc)) { + break; } // Perform typo correction to determine if there is another name that is @@ -575,6 +635,19 @@ Corrected: if (!SecondTry) { SecondTry = true; CorrectionCandidateCallback DefaultValidator; + // Try to limit which sets of keywords should be included in typo + // correction based on what the next token is. + DefaultValidator.WantTypeSpecifiers = + NextToken.is(tok::l_paren) || NextToken.is(tok::less) || + NextToken.is(tok::identifier) || NextToken.is(tok::star) || + NextToken.is(tok::amp) || NextToken.is(tok::l_square); + DefaultValidator.WantExpressionKeywords = + NextToken.is(tok::l_paren) || NextToken.is(tok::identifier) || + NextToken.is(tok::arrow) || NextToken.is(tok::period); + DefaultValidator.WantRemainingKeywords = + NextToken.is(tok::l_paren) || NextToken.is(tok::semi) || + NextToken.is(tok::identifier) || NextToken.is(tok::l_brace); + DefaultValidator.WantCXXNamedCasts = false; if (TypoCorrection Corrected = CorrectTypo(Result.getLookupNameInfo(), Result.getLookupKind(), S, &SS, DefaultValidator)) { @@ -740,7 +813,7 @@ Corrected: if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); QualType T = Context.getTypeDeclType(Type); - return ParsedType::make(T); + return ParsedType::make(T); } ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(FirstDecl); @@ -764,6 +837,23 @@ Corrected: QualType T = Context.getObjCInterfaceType(Class); return ParsedType::make(T); } + + // Check for a tag type hidden by a non-type decl in a few cases where it + // seems likely a type is wanted instead of the non-type that was found. + if (!getLangOpts().ObjC1 && FirstDecl && !isa<ClassTemplateDecl>(FirstDecl) && + !isa<TypeAliasTemplateDecl>(FirstDecl)) { + bool NextIsOp = NextToken.is(tok::amp) || NextToken.is(tok::star); + if ((NextToken.is(tok::identifier) || + (NextIsOp && FirstDecl->isFunctionOrFunctionTemplate())) && + isTagTypeWithMissingTag(*this, Result, S, SS, Name, NameLoc)) { + FirstDecl = (*Result.begin())->getUnderlyingDecl(); + if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) { + DiagnoseUseOfDecl(Type, NameLoc); + QualType T = Context.getTypeDeclType(Type); + return ParsedType::make(T); + } + } + } if (!Result.empty() && (*Result.begin())->isCXXClassMember()) return BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, 0); @@ -1132,9 +1222,9 @@ void Sema::MarkUnusedFileScopedDecl(const DeclaratorDecl *D) { return; // First should already be in the vector. } - if (ShouldWarnIfUnusedFileScopedDecl(D)) - UnusedFileScopedDecls.push_back(D); - } + if (ShouldWarnIfUnusedFileScopedDecl(D)) + UnusedFileScopedDecls.push_back(D); +} static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) { if (D->isInvalidDecl()) @@ -1281,7 +1371,7 @@ void Sema::ActOnEndFunctionDeclarator() { /// /// \param IdLoc The location of the name in the translation unit. /// -/// \param TypoCorrection If true, this routine will attempt typo correction +/// \param DoTypoCorrection If true, this routine will attempt typo correction /// if there is no class with the given name. /// /// \returns The declaration of the named Objective-C class, or NULL if the @@ -1484,12 +1574,22 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { switch (TypeID->getLength()) { default: break; case 2: - if (!TypeID->isStr("id")) - break; - Context.setObjCIdRedefinitionType(New->getUnderlyingType()); - // Install the built-in type for 'id', ignoring the current definition. - New->setTypeForDecl(Context.getObjCIdType().getTypePtr()); - return; + { + if (!TypeID->isStr("id")) + break; + QualType T = New->getUnderlyingType(); + if (!T->isPointerType()) + break; + if (!T->isVoidPointerType()) { + QualType PT = T->getAs<PointerType>()->getPointeeType(); + if (!PT->isStructureType()) + break; + } + Context.setObjCIdRedefinitionType(T); + // Install the built-in type for 'id', ignoring the current definition. + New->setTypeForDecl(Context.getObjCIdType().getTypePtr()); + return; + } case 5: if (!TypeID->isStr("Class")) break; @@ -1599,6 +1699,13 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { /// attribute. static bool DeclHasAttr(const Decl *D, const Attr *A) { + // There can be multiple AvailabilityAttr in a Decl. Make sure we copy + // all of them. It is mergeAvailabilityAttr in SemaDeclAttr.cpp that is + // responsible for making sure they are consistent. + const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(A); + if (AA) + return false; + const OwnershipAttr *OA = dyn_cast<OwnershipAttr>(A); const AnnotateAttr *Ann = dyn_cast<AnnotateAttr>(A); for (Decl::attr_iterator i = D->attr_begin(), e = D->attr_end(); i != e; ++i) @@ -1617,9 +1724,90 @@ DeclHasAttr(const Decl *D, const Attr *A) { return false; } +bool Sema::mergeDeclAttribute(Decl *D, InheritableAttr *Attr) { + InheritableAttr *NewAttr = NULL; + if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr)) + NewAttr = mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), + AA->getIntroduced(), AA->getDeprecated(), + AA->getObsoleted(), AA->getUnavailable(), + AA->getMessage()); + else if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr)) + NewAttr = mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility()); + else if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr)) + NewAttr = mergeDLLImportAttr(D, ImportA->getRange()); + else if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr)) + NewAttr = mergeDLLExportAttr(D, ExportA->getRange()); + else if (FormatAttr *FA = dyn_cast<FormatAttr>(Attr)) + NewAttr = mergeFormatAttr(D, FA->getRange(), FA->getType(), + FA->getFormatIdx(), FA->getFirstArg()); + else if (SectionAttr *SA = dyn_cast<SectionAttr>(Attr)) + NewAttr = mergeSectionAttr(D, SA->getRange(), SA->getName()); + else if (!DeclHasAttr(D, Attr)) + NewAttr = cast<InheritableAttr>(Attr->clone(Context)); + + if (NewAttr) { + NewAttr->setInherited(true); + D->addAttr(NewAttr); + return true; + } + + return false; +} + +static const Decl *getDefinition(const Decl *D) { + if (const TagDecl *TD = dyn_cast<TagDecl>(D)) + return TD->getDefinition(); + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) + return VD->getDefinition(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + const FunctionDecl* Def; + if (FD->hasBody(Def)) + return Def; + } + return NULL; +} + +static bool hasAttribute(const Decl *D, attr::Kind Kind) { + for (Decl::attr_iterator I = D->attr_begin(), E = D->attr_end(); + I != E; ++I) { + Attr *Attribute = *I; + if (Attribute->getKind() == Kind) + return true; + } + return false; +} + +/// checkNewAttributesAfterDef - If we already have a definition, check that +/// there are no new attributes in this declaration. +static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { + if (!New->hasAttrs()) + return; + + const Decl *Def = getDefinition(Old); + if (!Def || Def == New) + return; + + AttrVec &NewAttributes = New->getAttrs(); + for (unsigned I = 0, E = NewAttributes.size(); I != E;) { + const Attr *NewAttribute = NewAttributes[I]; + if (hasAttribute(Def, NewAttribute->getKind())) { + ++I; + continue; // regular attr merging will take care of validating this. + } + S.Diag(NewAttribute->getLocation(), + diag::warn_attribute_precede_definition); + S.Diag(Def->getLocation(), diag::note_previous_definition); + NewAttributes.erase(NewAttributes.begin() + I); + --E; + } +} + /// mergeDeclAttributes - Copy attributes from the Old decl to the New one. void Sema::mergeDeclAttributes(Decl *New, Decl *Old, bool MergeDeprecation) { + // attributes declared post-definition are currently ignored + checkNewAttributesAfterDef(*this, New, Old); + if (!Old->hasAttrs()) return; @@ -1640,12 +1828,8 @@ void Sema::mergeDeclAttributes(Decl *New, Decl *Old, isa<AvailabilityAttr>(*i))) continue; - if (!DeclHasAttr(New, *i)) { - InheritableAttr *newAttr = cast<InheritableAttr>((*i)->clone(Context)); - newAttr->setInherited(true); - New->addAttr(newAttr); + if (mergeDeclAttribute(New, *i)) foundAny = true; - } } if (!foundAny) New->dropAttrs(); @@ -1909,22 +2093,27 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); return true; } - + // C++ [class.mem]p1: // [...] A member shall not be declared twice in the // member-specification, except that a nested class or member // class template can be declared and then later defined. - unsigned NewDiag; - if (isa<CXXConstructorDecl>(OldMethod)) - NewDiag = diag::err_constructor_redeclared; - else if (isa<CXXDestructorDecl>(NewMethod)) - NewDiag = diag::err_destructor_redeclared; - else if (isa<CXXConversionDecl>(NewMethod)) - NewDiag = diag::err_conv_function_redeclared; - else - NewDiag = diag::err_member_redeclared; + if (ActiveTemplateInstantiations.empty()) { + unsigned NewDiag; + if (isa<CXXConstructorDecl>(OldMethod)) + NewDiag = diag::err_constructor_redeclared; + else if (isa<CXXDestructorDecl>(NewMethod)) + NewDiag = diag::err_destructor_redeclared; + else if (isa<CXXConversionDecl>(NewMethod)) + NewDiag = diag::err_conv_function_redeclared; + else + NewDiag = diag::err_member_redeclared; - Diag(New->getLocation(), NewDiag); + Diag(New->getLocation(), NewDiag); + } else { + Diag(New->getLocation(), diag::err_member_redeclared_in_instantiation) + << New << New->getType(); + } Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); // Complain if this is an explicit declaration of a special @@ -1941,7 +2130,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { << New << getSpecialMember(OldMethod); return true; } - } else if (OldMethod->isExplicitlyDefaulted()) { + } else if (OldMethod->isExplicitlyDefaulted() && !isFriend) { Diag(NewMethod->getLocation(), diag::err_definition_of_explicitly_defaulted_member) << getSpecialMember(OldMethod); @@ -2142,18 +2331,16 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, ObjCMethodDecl *oldMethod) { - // We don't want to merge unavailable and deprecated attributes - // except from interface to implementation. - bool mergeDeprecation = isa<ObjCImplDecl>(newMethod->getDeclContext()); - // Merge the attributes. - mergeDeclAttributes(newMethod, oldMethod, mergeDeprecation); + // Merge the attributes, including deprecated/unavailable + mergeDeclAttributes(newMethod, oldMethod, /* mergeDeprecation */true); // Merge attributes from the parameters. - ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin(); + ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin(), + oe = oldMethod->param_end(); for (ObjCMethodDecl::param_iterator ni = newMethod->param_begin(), ne = newMethod->param_end(); - ni != ne; ++ni, ++oi) + ni != ne && oi != oe; ++ni, ++oi) mergeParamDeclAttributes(*ni, *oi, Context); CheckObjCMethodOverride(newMethod, oldMethod, true); @@ -2552,6 +2739,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, } } + ActOnDocumentableDecl(TagD); + return TagD; } @@ -2873,7 +3062,7 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, Context.getTypeDeclType(Record), TInfo, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + /*InitStyle=*/ICIS_NoInit); Anon->setAccess(AS); if (getLangOpts().CPlusPlus) FieldCollector->Add(cast<FieldDecl>(Anon)); @@ -2970,7 +3159,7 @@ Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS, Context.getTypeDeclType(Record), TInfo, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + /*InitStyle=*/ICIS_NoInit); Anon->setImplicit(); // Add the anonymous struct object to the current context. @@ -3225,7 +3414,7 @@ Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) { Decl *Dcl = HandleDeclarator(S, D, MultiTemplateParamsArg(*this)); if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer() && - Dcl->getDeclContext()->isFileContext()) + Dcl && Dcl->getDeclContext()->isFileContext()) Dcl->setTopLevelDeclInObjCContainer(); return Dcl; @@ -3794,8 +3983,6 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, Context.setsigjmp_bufDecl(NewTD); else if (II->isStr("ucontext_t")) Context.setucontext_tDecl(NewTD); - else if (II->isStr("__builtin_va_list")) - Context.setBuiltinVaListType(Context.getTypedefType(NewTD)); } return NewTD; @@ -4173,18 +4360,6 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, CheckMemberSpecialization(NewVD, Previous)) NewVD->setInvalidDecl(); } - - // attributes declared post-definition are currently ignored - // FIXME: This should be handled in attribute merging, not - // here. - if (Previous.isSingleResult()) { - VarDecl *Def = dyn_cast<VarDecl>(Previous.getFoundDecl()); - if (Def && (Def = Def->getDefinition()) && - Def != NewVD && D.hasAttributes()) { - Diag(NewVD->getLocation(), diag::warn_attribute_precede_definition); - Diag(Def->getLocation(), diag::note_previous_definition); - } - } // If this is a locally-scoped extern C variable, update the map of // such variables. @@ -4334,6 +4509,15 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, return false; } + // OpenCL v1.2 s6.8 -- The static qualifier is valid only in program + // scope. + if ((getLangOpts().OpenCLVersion >= 120) + && NewVD->isStaticLocal()) { + Diag(NewVD->getLocation(), diag::err_static_function_scope); + NewVD->setInvalidDecl(); + return false; + } + if (NewVD->hasLocalStorage() && T.isObjCGCWeak() && !NewVD->hasAttr<BlocksAttr>()) { if (getLangOpts().getGC() != LangOptions::NonGC) @@ -4418,7 +4602,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, if (NewVD->isConstexpr() && !T->isDependentType() && RequireLiteralType(NewVD->getLocation(), T, - PDiag(diag::err_constexpr_var_non_literal))) { + diag::err_constexpr_var_non_literal)) { NewVD->setInvalidDecl(); return false; } @@ -4471,11 +4655,6 @@ static bool FindOverriddenMethod(const CXXBaseSpecifier *Specifier, return false; } -static bool hasDelayedExceptionSpec(CXXMethodDecl *Method) { - const FunctionProtoType *Proto =Method->getType()->getAs<FunctionProtoType>(); - return Proto && Proto->getExceptionSpecType() == EST_Delayed; -} - /// AddOverriddenMethods - See if a method overrides any in the base classes, /// and if so, check that it's a valid override and remember it. bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { @@ -4491,8 +4670,7 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) { MD->addOverriddenMethod(OldMD->getCanonicalDecl()); if (!CheckOverridingFunctionReturnType(MD, OldMD) && - (hasDelayedExceptionSpec(MD) || - !CheckOverridingFunctionExceptionSpec(MD, OldMD)) && + !CheckOverridingFunctionExceptionSpec(MD, OldMD) && !CheckIfOverriddenFunctionIsMarkedFinal(MD, OldMD)) { AddedAny = true; } @@ -4520,22 +4698,39 @@ namespace { // Also only accept corrections that have the same parent decl. class DifferentNameValidatorCCC : public CorrectionCandidateCallback { public: - DifferentNameValidatorCCC(CXXRecordDecl *Parent) - : ExpectedParent(Parent ? Parent->getCanonicalDecl() : 0) {} + DifferentNameValidatorCCC(ASTContext &Context, FunctionDecl *TypoFD, + CXXRecordDecl *Parent) + : Context(Context), OriginalFD(TypoFD), + ExpectedParent(Parent ? Parent->getCanonicalDecl() : 0) {} virtual bool ValidateCandidate(const TypoCorrection &candidate) { if (candidate.getEditDistance() == 0) return false; - if (CXXMethodDecl *MD = candidate.getCorrectionDeclAs<CXXMethodDecl>()) { - CXXRecordDecl *Parent = MD->getParent(); - return Parent && Parent->getCanonicalDecl() == ExpectedParent; + llvm::SmallVector<unsigned, 1> MismatchedParams; + for (TypoCorrection::const_decl_iterator CDecl = candidate.begin(), + CDeclEnd = candidate.end(); + CDecl != CDeclEnd; ++CDecl) { + FunctionDecl *FD = dyn_cast<FunctionDecl>(*CDecl); + + if (FD && !FD->hasBody() && + hasSimilarParameters(Context, FD, OriginalFD, MismatchedParams)) { + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { + CXXRecordDecl *Parent = MD->getParent(); + if (Parent && Parent->getCanonicalDecl() == ExpectedParent) + return true; + } else if (!ExpectedParent) { + return true; + } + } } - return !ExpectedParent; + return false; } private: + ASTContext &Context; + FunctionDecl *OriginalFD; CXXRecordDecl *ExpectedParent; }; @@ -4571,7 +4766,8 @@ static NamedDecl* DiagnoseInvalidRedeclaration( assert(!Prev.isAmbiguous() && "Cannot have an ambiguity in previous-declaration lookup"); CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD); - DifferentNameValidatorCCC Validator(MD ? MD->getParent() : 0); + DifferentNameValidatorCCC Validator(SemaRef.Context, NewFD, + MD ? MD->getParent() : 0); if (!Prev.empty()) { for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end(); Func != FuncEnd; ++Func) { @@ -4601,8 +4797,8 @@ static NamedDecl* DiagnoseInvalidRedeclaration( CDeclEnd = Correction.end(); CDecl != CDeclEnd; ++CDecl) { FunctionDecl *FD = dyn_cast<FunctionDecl>(*CDecl); - if (FD && hasSimilarParameters(SemaRef.Context, FD, NewFD, - MismatchedParams)) { + if (FD && !FD->hasBody() && + hasSimilarParameters(SemaRef.Context, FD, NewFD, MismatchedParams)) { Previous.addDecl(FD); } } @@ -4640,19 +4836,23 @@ static NamedDecl* DiagnoseInvalidRedeclaration( } } - if (Correction) - SemaRef.Diag(NewFD->getLocation(), DiagMsg) + if (Correction) { + SourceRange FixItLoc(NewFD->getLocation()); + CXXScopeSpec &SS = ExtraArgs.D.getCXXScopeSpec(); + if (Correction.getCorrectionSpecifier() && SS.isValid()) + FixItLoc.setBegin(SS.getBeginLoc()); + SemaRef.Diag(NewFD->getLocStart(), DiagMsg) << Name << NewDC << Correction.getQuoted(SemaRef.getLangOpts()) << FixItHint::CreateReplacement( - NewFD->getLocation(), - Correction.getAsString(SemaRef.getLangOpts())); - else + FixItLoc, Correction.getAsString(SemaRef.getLangOpts())); + } else { SemaRef.Diag(NewFD->getLocation(), DiagMsg) << Name << NewDC << NewFD->getLocation(); + } bool NewFDisConst = false; if (CXXMethodDecl *NewMD = dyn_cast<CXXMethodDecl>(NewFD)) - NewFDisConst = NewMD->getTypeQualifiers() & Qualifiers::Const; + NewFDisConst = NewMD->isConst(); for (llvm::SmallVector<std::pair<FunctionDecl*, unsigned>, 1>::iterator NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end(); @@ -4660,7 +4860,7 @@ static NamedDecl* DiagnoseInvalidRedeclaration( FunctionDecl *FD = NearMatch->first; bool FDisConst = false; if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) - FDisConst = MD->getTypeQualifiers() & Qualifiers::Const; + FDisConst = MD->isConst(); if (unsigned Idx = NearMatch->second) { ParmVarDecl *FDParam = FD->getParamDecl(Idx-1); @@ -4911,7 +5111,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, FunctionTemplateDecl *FunctionTemplate = 0; bool isExplicitSpecialization = false; bool isFunctionTemplateSpecialization = false; + bool isDependentClassScopeExplicitSpecialization = false; + bool HasExplicitTemplateArgs = false; + TemplateArgumentListInfo TemplateArgs; + bool isVirtualOkay = false; FunctionDecl *NewFD = CreateNewFunctionDecl(*this, D, DC, R, TInfo, SC, @@ -5041,56 +5245,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, FunctionTemplate->setInvalidDecl(); } - // If we see "T var();" at block scope, where T is a class type, it is - // probably an attempt to initialize a variable, not a function declaration. - // We don't catch this case earlier, since there is no ambiguity here. - if (!FunctionTemplate && D.getFunctionDefinitionKind() == FDK_Declaration && - CurContext->isFunctionOrMethod() && - D.getNumTypeObjects() == 1 && D.isFunctionDeclarator() && - D.getDeclSpec().getStorageClassSpecAsWritten() - == DeclSpec::SCS_unspecified) { - QualType T = R->getAs<FunctionType>()->getResultType(); - DeclaratorChunk &C = D.getTypeObject(0); - if (!T->isVoidType() && C.Fun.NumArgs == 0 && !C.Fun.isVariadic && - !C.Fun.TrailingReturnType && - C.Fun.getExceptionSpecType() == EST_None) { - SourceRange ParenRange(C.Loc, C.EndLoc); - Diag(C.Loc, diag::warn_empty_parens_are_function_decl) << ParenRange; - - // If the declaration looks like: - // T var1, - // f(); - // and name lookup finds a function named 'f', then the ',' was - // probably intended to be a ';'. - if (!D.isFirstDeclarator() && D.getIdentifier()) { - FullSourceLoc Comma(D.getCommaLoc(), SourceMgr); - FullSourceLoc Name(D.getIdentifierLoc(), SourceMgr); - if (Comma.getFileID() != Name.getFileID() || - Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { - LookupResult Result(*this, D.getIdentifier(), SourceLocation(), - LookupOrdinaryName); - if (LookupName(Result, S)) - Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) - << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << NewFD; - } - } - const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); - // Empty parens mean value-initialization, and no parens mean default - // initialization. These are equivalent if the default constructor is - // user-provided, or if zero-initialization is a no-op. - if (RD && RD->hasDefinition() && - (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) - Diag(C.Loc, diag::note_empty_parens_default_ctor) - << FixItHint::CreateRemoval(ParenRange); - else if (const char *Init = getFixItZeroInitializerForType(T)) - Diag(C.Loc, diag::note_empty_parens_zero_initialize) - << FixItHint::CreateReplacement(ParenRange, Init); - else if (LangOpts.CPlusPlus0x) - Diag(C.Loc, diag::note_empty_parens_zero_initialize) - << FixItHint::CreateReplacement(ParenRange, "{}"); - } - } - // C++ [dcl.fct.spec]p5: // The virtual specifier shall only be used in declarations of // nonstatic class member functions that appear within a @@ -5333,6 +5487,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD->setInvalidDecl(); } + // Handle attributes. + ProcessDeclAttributes(S, NewFD, D, + /*NonInheritable=*/false, /*Inheritable=*/true); + if (!getLangOpts().CPlusPlus) { // Perform semantic checking on the function declaration. bool isExplicitSpecialization=false; @@ -5348,8 +5506,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } else { // If the declarator is a template-id, translate the parser's template // argument list into our AST format. - bool HasExplicitTemplateArgs = false; - TemplateArgumentListInfo TemplateArgs; if (D.getName().getKind() == UnqualifiedId::IK_TemplateId) { TemplateIdAnnotation *TemplateId = D.getName().TemplateId; TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc); @@ -5580,25 +5736,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, << D.getCXXScopeSpec().getRange(); } } - - - // Handle attributes. We need to have merged decls when handling attributes - // (for example to check for conflicts, etc). - // FIXME: This needs to happen before we merge declarations. Then, - // let attribute merging cope with attribute conflicts. - ProcessDeclAttributes(S, NewFD, D, - /*NonInheritable=*/false, /*Inheritable=*/true); - - // attributes declared post-definition are currently ignored - // FIXME: This should happen during attribute merging - if (D.isRedeclaration() && Previous.isSingleResult()) { - const FunctionDecl *Def; - FunctionDecl *PrevFD = dyn_cast<FunctionDecl>(Previous.getFoundDecl()); - if (PrevFD && PrevFD->isDefined(Def) && D.hasAttributes()) { - Diag(NewFD->getLocation(), diag::warn_attribute_precede_definition); - Diag(Def->getLocation(), diag::note_previous_definition); - } - } AddKnownFunctionAttributes(NewFD); @@ -5644,6 +5781,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } + // OpenCL v1.2 s6.8 static is invalid for kernel functions. + if ((getLangOpts().OpenCLVersion >= 120) + && NewFD->hasAttr<OpenCLKernelAttr>() + && (SC == SC_Static)) { + Diag(D.getIdentifierLoc(), diag::err_static_kernel); + D.setInvalidType(); + } + MarkUnusedFileScopedDecl(NewFD); if (getLangOpts().CUDA) @@ -5664,8 +5809,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (isDependentClassScopeExplicitSpecialization) { ClassScopeFunctionSpecializationDecl *NewSpec = ClassScopeFunctionSpecializationDecl::Create( - Context, CurContext, SourceLocation(), - cast<CXXMethodDecl>(NewFD)); + Context, CurContext, SourceLocation(), + cast<CXXMethodDecl>(NewFD), + HasExplicitTemplateArgs, TemplateArgs); CurContext->addDecl(NewSpec); AddToScope = false; } @@ -5683,12 +5829,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, /// that have been instantiated via C++ template instantiation (called /// via InstantiateDecl). /// -/// \param IsExplicitSpecialiation whether this new function declaration is +/// \param IsExplicitSpecialization whether this new function declaration is /// an explicit specialization of the previous declaration. /// /// This sets NewFD->isInvalidDecl() to true if there was an error. /// -/// Returns true if the function declaration is a redeclaration. +/// \returns true if the function declaration is a redeclaration. bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, LookupResult &Previous, bool IsExplicitSpecialization) { @@ -5882,10 +6028,12 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // compatible, and if it does, warn the user. if (NewFD->isExternC()) { QualType R = NewFD->getResultType(); - if (!R.isPODType(Context) && - !R->isVoidType()) - Diag( NewFD->getLocation(), diag::warn_return_value_udt ) - << NewFD << R; + if (R->isIncompleteType() && !R->isVoidType()) + Diag(NewFD->getLocation(), diag::warn_return_value_udt_incomplete) + << NewFD << R; + else if (!R.isPODType(Context) && !R->isVoidType() && + !R->isObjCObjectPointerType()) + Diag(NewFD->getLocation(), diag::warn_return_value_udt) << NewFD << R; } } return Redeclaration; @@ -6040,55 +6188,73 @@ namespace { } } - void VisitExpr(Expr *E) { - if (isa<ObjCMessageExpr>(*E)) return; - if (isRecordType) { - Expr *expr = E; - if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) { - ValueDecl *VD = ME->getMemberDecl(); - if (isa<EnumConstantDecl>(VD) || isa<VarDecl>(VD)) return; - expr = ME->getBase(); - } - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(expr)) { + // Sometimes, the expression passed in lacks the casts that are used + // to determine which DeclRefExpr's to check. Assume that the casts + // are present and continue visiting the expression. + void HandleExpr(Expr *E) { + // Skip checking T a = a where T is not a record type. Doing so is a + // way to silence uninitialized warnings. + if (isRecordType) + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) HandleDeclRefExpr(DRE); - return; - } + + if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) { + HandleValue(CO->getTrueExpr()); + HandleValue(CO->getFalseExpr()); + } + + Visit(E); + } + + // For most expressions, the cast is directly above the DeclRefExpr. + // For conditional operators, the cast can be outside the conditional + // operator if both expressions are DeclRefExpr's. + void HandleValue(Expr *E) { + E = E->IgnoreParenImpCasts(); + if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(E)) { + HandleDeclRefExpr(DRE); + return; + } + + if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) { + HandleValue(CO->getTrueExpr()); + HandleValue(CO->getFalseExpr()); } - Inherited::VisitExpr(E); + } + + void VisitImplicitCastExpr(ImplicitCastExpr *E) { + if ((!isRecordType && E->getCastKind() == CK_LValueToRValue) || + (isRecordType && E->getCastKind() == CK_NoOp)) + HandleValue(E->getSubExpr()); + + Inherited::VisitImplicitCastExpr(E); } void VisitMemberExpr(MemberExpr *E) { + // Don't warn on arrays since they can be treated as pointers. if (E->getType()->canDecayToPointerType()) return; + ValueDecl *VD = E->getMemberDecl(); - if (isa<FieldDecl>(VD) || isa<CXXMethodDecl>(VD)) + CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(VD); + if (isa<FieldDecl>(VD) || (MD && !MD->isStatic())) if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->getBase()->IgnoreParenImpCasts())) { HandleDeclRefExpr(DRE); return; } - Inherited::VisitMemberExpr(E); - } - void VisitImplicitCastExpr(ImplicitCastExpr *E) { - if ((!isRecordType &&E->getCastKind() == CK_LValueToRValue) || - (isRecordType && E->getCastKind() == CK_NoOp)) { - Expr* SubExpr = E->getSubExpr()->IgnoreParenImpCasts(); - if (MemberExpr *ME = dyn_cast<MemberExpr>(SubExpr)) - SubExpr = ME->getBase()->IgnoreParenImpCasts(); - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SubExpr)) { - HandleDeclRefExpr(DRE); - return; - } - } - Inherited::VisitImplicitCastExpr(E); + Inherited::VisitMemberExpr(E); } void VisitUnaryOperator(UnaryOperator *E) { // For POD record types, addresses of its own members are well-defined. - if (isRecordType && isPODType) return; + if (E->getOpcode() == UO_AddrOf && isRecordType && isPODType && + isa<MemberExpr>(E->getSubExpr()->IgnoreParens())) return; Inherited::VisitUnaryOperator(E); } - + + void VisitObjCMessageExpr(ObjCMessageExpr *E) { return; } + void HandleDeclRefExpr(DeclRefExpr *DRE) { Decl* ReferenceDecl = DRE->getDecl(); if (OrigDecl != ReferenceDecl) return; @@ -6105,7 +6271,7 @@ namespace { /// CheckSelfReference - Warns if OrigDecl is used in expression E. void Sema::CheckSelfReference(Decl* OrigDecl, Expr *E) { - SelfReferenceChecker(*this, OrigDecl).VisitExpr(E); + SelfReferenceChecker(*this, OrigDecl).HandleExpr(E); } /// AddInitializerToDecl - Adds the initializer Init to the @@ -6145,13 +6311,19 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // Check for self-references within variable initializers. // Variables declared within a function/method body are handled // by a dataflow analysis. - if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal()) + // Record types initialized by initializer list are handled here. + // Initialization by constructors are handled in TryConstructorInitialization. + if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal() && + (isa<InitListExpr>(Init) || !VDecl->getType()->isRecordType())) CheckSelfReference(RealDecl, Init); ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init); // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. - if (TypeMayContainAuto && VDecl->getType()->getContainedAutoType()) { + AutoType *Auto = 0; + if (TypeMayContainAuto && + (Auto = VDecl->getType()->getContainedAutoType()) && + !Auto->isDeduced()) { Expr *DeduceInit = Init; // Initializer could be a C++ direct-initializer. Deduction only works if it // contains exactly one expression. @@ -6192,6 +6364,17 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) VDecl->setInvalidDecl(); + // Warn if we deduced 'id'. 'auto' usually implies type-safety, but using + // 'id' instead of a specific object type prevents most of our usual checks. + // We only want to warn outside of template instantiations, though: + // inside a template, the 'id' could have come from a parameter. + if (ActiveTemplateInstantiations.empty() && + DeducedType->getType()->isObjCIdType()) { + SourceLocation Loc = DeducedType->getTypeLoc().getBeginLoc(); + Diag(Loc, diag::warn_auto_var_is_id) + << VDecl->getDeclName() << DeduceInit->getSourceRange(); + } + // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDecl()) @@ -6906,9 +7089,55 @@ Sema::BuildDeclaratorGroup(Decl **Group, unsigned NumDecls, } } + ActOnDocumentableDecls(Group, NumDecls); + return DeclGroupPtrTy::make(DeclGroupRef::Create(Context, Group, NumDecls)); } +void Sema::ActOnDocumentableDecl(Decl *D) { + ActOnDocumentableDecls(&D, 1); +} + +void Sema::ActOnDocumentableDecls(Decl **Group, unsigned NumDecls) { + // Don't parse the comment if Doxygen diagnostics are ignored. + if (NumDecls == 0 || !Group[0]) + return; + + if (Diags.getDiagnosticLevel(diag::warn_doc_param_not_found, + Group[0]->getLocation()) + == DiagnosticsEngine::Ignored) + return; + + if (NumDecls >= 2) { + // This is a decl group. Normally it will contain only declarations + // procuded from declarator list. But in case we have any definitions or + // additional declaration references: + // 'typedef struct S {} S;' + // 'typedef struct S *S;' + // 'struct S *pS;' + // FinalizeDeclaratorGroup adds these as separate declarations. + Decl *MaybeTagDecl = Group[0]; + if (MaybeTagDecl && isa<TagDecl>(MaybeTagDecl)) { + Group++; + NumDecls--; + } + } + + // See if there are any new comments that are not attached to a decl. + ArrayRef<RawComment *> Comments = Context.getRawCommentList().getComments(); + if (!Comments.empty() && + !Comments.back()->isAttached()) { + // There is at least one comment that not attached to a decl. + // Maybe it should be attached to one of these decls? + // + // Note that this way we pick up not only comments that precede the + // declaration, but also comments that *follow* the declaration -- thanks to + // the lookahead in the lexer: we've consumed the semicolon and looked + // ahead through comments. + for (unsigned i = 0; i != NumDecls; ++i) + Context.getCommentForDecl(Group[i]); + } +} /// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator() /// to introduce parameters into function prototype scope. @@ -7132,9 +7361,10 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, // Parameter declarators cannot be interface types. All ObjC objects are // passed by reference. if (T->isObjCObjectType()) { + SourceLocation TypeEndLoc = TSInfo->getTypeLoc().getLocEnd(); Diag(NameLoc, diag::err_object_cannot_be_passed_returned_by_value) << 1 << T - << FixItHint::CreateInsertion(NameLoc, "*"); + << FixItHint::CreateInsertion(TypeEndLoc, "*"); T = Context.getObjCObjectPointerType(T); New->setType(T); } @@ -7225,6 +7455,10 @@ static bool ShouldWarnAboutMissingPrototype(const FunctionDecl *FD) { if (FD->isFunctionTemplateSpecialization()) return false; + // Don't warn for OpenCL kernels. + if (FD->hasAttr<OpenCLKernelAttr>()) + return false; + bool MissingPrototype = true; for (const FunctionDecl *Prev = FD->getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) { @@ -7253,6 +7487,7 @@ void Sema::CheckForFunctionRedefinition(FunctionDecl *FD) { else Diag(FD->getLocation(), diag::err_redefinition) << FD->getDeclName(); Diag(Definition->getLocation(), diag::note_previous_definition); + FD->setInvalidDecl(); } } @@ -7388,6 +7623,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) { << FD->getName() << "dllimport"; } } + ActOnDocumentableDecl(FD); return FD; } @@ -7463,7 +7699,12 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(FD)) MarkVTableUsed(FD->getLocation(), Constructor->getParent()); - computeNRVO(Body, getCurFunction()); + // Try to apply the named return value optimization. We have to check + // if we can do this here because lambdas keep return statements around + // to deduce an implicit return type. + if (getLangOpts().CPlusPlus && FD->getResultType()->isRecordType() && + !FD->isDependentContext()) + computeNRVO(Body, getCurFunction()); } assert((FD == getCurFunctionDecl() || getCurLambda()->CallOperator == FD) && @@ -7471,8 +7712,6 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, } else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) { assert(MD == getCurMethodDecl() && "Method parsing confused"); MD->setBody(Body); - if (Body) - MD->setEndLoc(Body->getLocEnd()); if (!MD->isInvalidDecl()) { DiagnoseUnusedParameters(MD->param_begin(), MD->param_end()); DiagnoseSizeOfParametersAndReturnValue(MD->param_begin(), MD->param_end(), @@ -7481,22 +7720,24 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (Body) computeNRVO(Body, getCurFunction()); } - if (ObjCShouldCallSuperDealloc) { + if (getCurFunction()->ObjCShouldCallSuperDealloc) { Diag(MD->getLocEnd(), diag::warn_objc_missing_super_dealloc); - ObjCShouldCallSuperDealloc = false; + getCurFunction()->ObjCShouldCallSuperDealloc = false; } - if (ObjCShouldCallSuperFinalize) { + if (getCurFunction()->ObjCShouldCallSuperFinalize) { Diag(MD->getLocEnd(), diag::warn_objc_missing_super_finalize); - ObjCShouldCallSuperFinalize = false; + getCurFunction()->ObjCShouldCallSuperFinalize = false; } } else { return 0; } - assert(!ObjCShouldCallSuperDealloc && "This should only be set for " - "ObjC methods, which should have been handled in the block above."); - assert(!ObjCShouldCallSuperFinalize && "This should only be set for " - "ObjC methods, which should have been handled in the block above."); + assert(!getCurFunction()->ObjCShouldCallSuperDealloc && + "This should only be set for ObjC methods, which should have been " + "handled in the block above."); + assert(!getCurFunction()->ObjCShouldCallSuperFinalize && + "This should only be set for ObjC methods, which should have been " + "handled in the block above."); // Verify and clean out per-function state. if (Body) { @@ -7630,10 +7871,10 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, (void)Error; // Silence warning. assert(!Error && "Error setting up implicit decl!"); Declarator D(DS, Declarator::BlockContext); - D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, - 0, 0, true, SourceLocation(), + D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, false, + SourceLocation(), 0, 0, 0, true, + SourceLocation(), SourceLocation(), SourceLocation(), SourceLocation(), - SourceLocation(), EST_None, SourceLocation(), 0, 0, 0, 0, Loc, Loc, D), DS.getAttributes(), @@ -7733,6 +7974,13 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) { "printf", 2, Name->isStr("vasprintf") ? 0 : 3)); } + + if (Name->isStr("__CFStringMakeConstantString")) { + // We already have a __builtin___CFStringMakeConstantString, + // but builds that use -fno-constant-cfstrings don't go through that. + if (!FD->getAttr<FormatArgAttr>()) + FD->addAttr(::new (Context) FormatArgAttr(FD->getLocation(), Context, 1)); + } } TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, @@ -8566,9 +8814,10 @@ CreateNewDecl: // many points during the parsing of a struct declaration (because // the #pragma tokens are effectively skipped over during the // parsing of the struct). - AddAlignmentAttributesForRecord(RD); - - AddMsStructLayoutForRecord(RD); + if (TUK == TUK_Definition) { + AddAlignmentAttributesForRecord(RD); + AddMsStructLayoutForRecord(RD); + } } if (ModulePrivateLoc.isValid()) { @@ -8653,6 +8902,13 @@ CreateNewDecl: InFunctionDeclarator && Name) DeclsInPrototypeScope.push_back(New); + if (PrevDecl) + mergeDeclAttributes(New, PrevDecl); + + // If there's a #pragma GCC visibility in scope, set the visibility of this + // record. + AddPushedVisibilityAttribute(New); + OwnedDecl = true; return New; } @@ -8663,6 +8919,12 @@ void Sema::ActOnTagStartDefinition(Scope *S, Decl *TagD) { // Enter the tag context. PushDeclContext(S, Tag); + + ActOnDocumentableDecl(TagD); + + // If there's a #pragma GCC visibility in scope, set the visibility of this + // record. + AddPushedVisibilityAttribute(Tag); } Decl *Sema::ActOnObjCContainerStartDefinition(Decl *IDecl) { @@ -8849,7 +9111,7 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart, Declarator &D, Expr *BitfieldWidth) { FieldDecl *Res = HandleField(S, cast_or_null<RecordDecl>(TagD), DeclStart, D, static_cast<Expr*>(BitfieldWidth), - /*HasInit=*/false, AS_public); + /*InitStyle=*/ICIS_NoInit, AS_public); return Res; } @@ -8857,7 +9119,8 @@ Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart, /// FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, SourceLocation DeclStart, - Declarator &D, Expr *BitWidth, bool HasInit, + Declarator &D, Expr *BitWidth, + InClassInitStyle InitStyle, AccessSpecifier AS) { IdentifierInfo *II = D.getIdentifier(); SourceLocation Loc = DeclStart; @@ -8919,7 +9182,7 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, = (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_mutable); SourceLocation TSSL = D.getLocStart(); FieldDecl *NewFD - = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, HasInit, + = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, InitStyle, TSSL, AS, PrevDecl, &D); if (NewFD->isInvalidDecl()) @@ -8952,7 +9215,8 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, TypeSourceInfo *TInfo, RecordDecl *Record, SourceLocation Loc, - bool Mutable, Expr *BitWidth, bool HasInit, + bool Mutable, Expr *BitWidth, + InClassInitStyle InitStyle, SourceLocation TSSL, AccessSpecifier AS, NamedDecl *PrevDecl, Declarator *D) { @@ -9042,7 +9306,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo, - BitWidth, Mutable, HasInit); + BitWidth, Mutable, InitStyle); if (InvalidDecl) NewFD->setInvalidDecl(); @@ -9213,10 +9477,9 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) { case CXXCopyAssignment: if (RD->hasUserDeclaredCopyAssignment()) { - // FIXME: this should use the location of the copy - // assignment, not the type. - SourceLocation TyLoc = RD->getLocStart(); - Diag(TyLoc, diag::note_nontrivial_user_defined) << QT << member; + SourceLocation AssignLoc = + RD->getCopyAssignmentOperator(0)->getLocation(); + Diag(AssignLoc, diag::note_nontrivial_user_defined) << QT << member; return; } break; @@ -9295,12 +9558,12 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) { typedef RecordDecl::field_iterator field_iter; for (field_iter fi = RD->field_begin(), fe = RD->field_end(); fi != fe; ++fi) { - QualType EltTy = Context.getBaseElementType((*fi)->getType()); + QualType EltTy = Context.getBaseElementType(fi->getType()); if (const RecordType *EltRT = EltTy->getAs<RecordType>()) { CXXRecordDecl* EltRD = cast<CXXRecordDecl>(EltRT->getDecl()); if (!(EltRD->*hasTrivial)()) { - SourceLocation FLoc = (*fi)->getLocation(); + SourceLocation FLoc = fi->getLocation(); Diag(FLoc, diag::note_nontrivial_has_nontrivial) << QT << 0 << member; DiagnoseNontrivial(EltRT, member); return; @@ -9316,7 +9579,7 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) { case Qualifiers::OCL_Autoreleasing: case Qualifiers::OCL_Weak: case Qualifiers::OCL_Strong: - Diag((*fi)->getLocation(), diag::note_nontrivial_objc_ownership) + Diag(fi->getLocation(), diag::note_nontrivial_objc_ownership) << QT << EltTy.getObjCLifetime(); return; } @@ -9390,7 +9653,7 @@ Decl *Sema::ActOnIvar(Scope *S, ObjCContainerDecl *EnclosingContext; if (ObjCImplementationDecl *IMPDecl = dyn_cast<ObjCImplementationDecl>(EnclosingDecl)) { - if (!LangOpts.ObjCNonFragileABI2) { + if (LangOpts.ObjCRuntime.isFragile()) { // Case of ivar declared in an implementation. Context is that of its class. EnclosingContext = IMPDecl->getClassInterface(); assert(EnclosingContext && "Implementation has no class interface!"); @@ -9400,7 +9663,7 @@ Decl *Sema::ActOnIvar(Scope *S, } else { if (ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(EnclosingDecl)) { - if (!LangOpts.ObjCNonFragileABI2 || !CDecl->IsClassExtension()) { + if (LangOpts.ObjCRuntime.isFragile() || !CDecl->IsClassExtension()) { Diag(Loc, diag::err_misplaced_ivar) << CDecl->IsClassExtension(); return 0; } @@ -9443,7 +9706,11 @@ Decl *Sema::ActOnIvar(Scope *S, S->AddDecl(NewID); IdResolver.AddDecl(NewID); } - + + if (LangOpts.ObjCRuntime.isNonFragile() && + !NewID->isInvalidDecl() && isa<ObjCInterfaceDecl>(EnclosingDecl)) + Diag(Loc, diag::warn_ivars_in_interface); + return NewID; } @@ -9453,7 +9720,7 @@ Decl *Sema::ActOnIvar(Scope *S, /// then add an implicit `char :0` ivar to the end of that interface. void Sema::ActOnLastBitfield(SourceLocation DeclLoc, SmallVectorImpl<Decl *> &AllIvarDecls) { - if (!LangOpts.ObjCNonFragileABI2 || AllIvarDecls.empty()) + if (LangOpts.ObjCRuntime.isFragile() || AllIvarDecls.empty()) return; Decl *ivarDecl = AllIvarDecls[AllIvarDecls.size()-1]; @@ -9492,11 +9759,23 @@ void Sema::ActOnFields(Scope* S, AttributeList *Attr) { assert(EnclosingDecl && "missing record or interface decl"); - // If the decl this is being inserted into is invalid, then it may be a - // redeclaration or some other bogus case. Don't try to add fields to it. - if (EnclosingDecl->isInvalidDecl()) - return; - + // If this is an Objective-C @implementation or category and we have + // new fields here we should reset the layout of the interface since + // it will now change. + if (!Fields.empty() && isa<ObjCContainerDecl>(EnclosingDecl)) { + ObjCContainerDecl *DC = cast<ObjCContainerDecl>(EnclosingDecl); + switch (DC->getKind()) { + default: break; + case Decl::ObjCCategory: + Context.ResetObjCLayout(cast<ObjCCategoryDecl>(DC)->getClassInterface()); + break; + case Decl::ObjCImplementation: + Context. + ResetObjCLayout(cast<ObjCImplementationDecl>(DC)->getClassInterface()); + break; + } + } + RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl); // Start counting up the number of named members; make sure to include @@ -9704,7 +9983,7 @@ void Sema::ActOnFields(Scope* S, // However, here we check whether this particular class is only // non-POD because of the presence of an Objective-C pointer member. // If so, objects of this type cannot be shared between code compiled - // with instant objects and code compiled with manual retain/release. + // with ARC and code compiled with manual retain/release. if (getLangOpts().ObjCAutoRefCount && CXXRecord->hasObjectMember() && CXXRecord->getLinkage() == ExternalLinkage) { @@ -9848,11 +10127,6 @@ void Sema::ActOnFields(Scope* S, if (Attr) ProcessDeclAttributeList(S, Record, Attr); - - // If there's a #pragma GCC visibility in scope, and this isn't a subclass, - // set the visibility of this record. - if (Record && !Record->getDeclContext()->isRecord()) - AddPushedVisibilityAttribute(Record); } /// \brief Determine whether the given integral value is representable within @@ -10106,15 +10380,16 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, } } - // C++ [class.mem]p13: - // If T is the name of a class, then each of the following shall have a - // name different from T: - // - every enumerator of every member of class T that is an enumerated - // type + // C++ [class.mem]p15: + // If T is the name of a class, then each of the following shall have a name + // different from T: + // - every enumerator of every member of class T that is an unscoped + // enumerated type if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>( TheEnumDecl->getDeclContext()->getRedeclContext())) - if (Record->getIdentifier() && Record->getIdentifier() == Id) + if (!TheEnumDecl->isScoped() && + Record->getIdentifier() && Record->getIdentifier() == Id) Diag(IdLoc, diag::err_member_name_of_class) << Id; EnumConstantDecl *New = @@ -10129,9 +10404,62 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, PushOnScopeChains(New, S); } + ActOnDocumentableDecl(New); + return New; } +// Emits a warning if every element in the enum is the same value and if +// every element is initialized with a integer or boolean literal. +static void CheckForUniqueEnumValues(Sema &S, Decl **Elements, + unsigned NumElements, EnumDecl *Enum, + QualType EnumType) { + if (S.Diags.getDiagnosticLevel(diag::warn_identical_enum_values, + Enum->getLocation()) == + DiagnosticsEngine::Ignored) + return; + + if (NumElements < 2) + return; + + if (!Enum->getIdentifier()) + return; + + llvm::APSInt FirstVal; + + for (unsigned i = 0; i != NumElements; ++i) { + EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(Elements[i]); + if (!ECD) + return; + + Expr *InitExpr = ECD->getInitExpr(); + if (!InitExpr) + return; + InitExpr = InitExpr->IgnoreImpCasts(); + if (!isa<IntegerLiteral>(InitExpr) && !isa<CXXBoolLiteralExpr>(InitExpr)) + return; + + if (i == 0) { + FirstVal = ECD->getInitVal(); + continue; + } + + if (!llvm::APSInt::isSameValue(FirstVal, ECD->getInitVal())) + return; + } + + S.Diag(Enum->getLocation(), diag::warn_identical_enum_values) + << EnumType << FirstVal.toString(10) + << Enum->getSourceRange(); + + EnumConstantDecl *Last = cast<EnumConstantDecl>(Elements[NumElements - 1]), + *Next = cast<EnumConstantDecl>(Elements[NumElements - 2]); + + S.Diag(Last->getLocation(), diag::note_identical_enum_values) + << FixItHint::CreateReplacement(Last->getInitExpr()->getSourceRange(), + Next->getName()); +} + void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, SourceLocation RBraceLoc, Decl *EnumDeclX, Decl **Elements, unsigned NumElements, @@ -10355,6 +10683,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, if (InFunctionDeclarator) DeclsInPrototypeScope.push_back(Enum); + CheckForUniqueEnumValues(*this, Elements, NumElements, Enum, EnumType); } Decl *Sema::ActOnFileScopeAsmDecl(Expr *expr, diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 5c6ddd2cb97d..22bff863c5c2 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -14,6 +14,7 @@ #include "clang/Sema/SemaInternal.h" #include "TargetAttributesSema.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclObjC.h" @@ -42,7 +43,8 @@ enum AttributeDeclKind { ExpectedMethod, ExpectedVariableFunctionOrLabel, ExpectedFieldOrGlobalVar, - ExpectedStruct + ExpectedStruct, + ExpectedTLSVar }; //===----------------------------------------------------------------------===// @@ -82,7 +84,7 @@ static bool isFunction(const Decl *D) { /// type (function or function-typed variable) or an Objective-C /// method. static bool isFunctionOrMethod(const Decl *D) { - return isFunction(D)|| isa<ObjCMethodDecl>(D); + return isFunction(D) || isa<ObjCMethodDecl>(D); } /// isFunctionOrMethodOrBlock - Return true if the given decl has function @@ -238,17 +240,45 @@ static bool isIntOrBool(Expr *Exp) { return QT->isBooleanType() || QT->isIntegerType(); } -/// + +// Check to see if the type is a smart pointer of some kind. We assume +// it's a smart pointer if it defines both operator-> and operator*. +static bool threadSafetyCheckIsSmartPointer(Sema &S, const RecordType* RT) { + DeclContextLookupConstResult Res1 = RT->getDecl()->lookup( + S.Context.DeclarationNames.getCXXOperatorName(OO_Star)); + if (Res1.first == Res1.second) + return false; + + DeclContextLookupConstResult Res2 = RT->getDecl()->lookup( + S.Context.DeclarationNames.getCXXOperatorName(OO_Arrow)); + if (Res2.first == Res2.second) + return false; + + return true; +} + /// \brief Check if passed in Decl is a pointer type. /// Note that this function may produce an error message. /// \return true if the Decl is a pointer type; false otherwise -/// -static bool checkIsPointer(Sema &S, const Decl *D, const AttributeList &Attr) { +static bool threadSafetyCheckIsPointer(Sema &S, const Decl *D, + const AttributeList &Attr) { if (const ValueDecl *vd = dyn_cast<ValueDecl>(D)) { QualType QT = vd->getType(); if (QT->isAnyPointerType()) return true; - S.Diag(Attr.getLoc(), diag::warn_pointer_attribute_wrong_type) + + if (const RecordType *RT = QT->getAs<RecordType>()) { + // If it's an incomplete type, it could be a smart pointer; skip it. + // (We don't want to force template instantiation if we can avoid it, + // since that would alter the order in which templates are instantiated.) + if (RT->isIncompleteType()) + return true; + + if (threadSafetyCheckIsSmartPointer(S, RT)) + return true; + } + + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_decl_not_pointer) << Attr.getName()->getName() << QT; } else { S.Diag(Attr.getLoc(), diag::err_attribute_can_be_applied_only_to_value_decl) @@ -270,35 +300,60 @@ static const RecordType *getRecordType(QualType QT) { return 0; } + +static bool checkBaseClassIsLockableCallback(const CXXBaseSpecifier *Specifier, + CXXBasePath &Path, void *Unused) { + const RecordType *RT = Specifier->getType()->getAs<RecordType>(); + if (RT->getDecl()->getAttr<LockableAttr>()) + return true; + return false; +} + + /// \brief Thread Safety Analysis: Checks that the passed in RecordType -/// resolves to a lockable object. May flag an error. +/// resolves to a lockable object. static void checkForLockableRecord(Sema &S, Decl *D, const AttributeList &Attr, QualType Ty) { const RecordType *RT = getRecordType(Ty); - + // Warn if could not get record type for this argument. if (!RT) { - S.Diag(Attr.getLoc(), diag::warn_attribute_argument_not_class) + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_argument_not_class) << Attr.getName() << Ty.getAsString(); return; } - // Don't check for lockable if the class hasn't been defined yet. + + // Don't check for lockable if the class hasn't been defined yet. if (RT->isIncompleteType()) return; - // Warn if the type is not lockable. - if (!RT->getDecl()->getAttr<LockableAttr>()) { - S.Diag(Attr.getLoc(), diag::warn_attribute_argument_not_lockable) - << Attr.getName() << Ty.getAsString(); + + // Allow smart pointers to be used as lockable objects. + // FIXME -- Check the type that the smart pointer points to. + if (threadSafetyCheckIsSmartPointer(S, RT)) + return; + + // Check if the type is lockable. + RecordDecl *RD = RT->getDecl(); + if (RD->getAttr<LockableAttr>()) return; + + // Else check if any base classes are lockable. + if (CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) { + CXXBasePaths BPaths(false, false); + if (CRD->lookupInBases(checkBaseClassIsLockableCallback, 0, BPaths)) + return; } + + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_argument_not_lockable) + << Attr.getName() << Ty.getAsString(); } /// \brief Thread Safety Analysis: Checks that all attribute arguments, starting -/// from Sidx, resolve to a lockable object. May flag an error. +/// from Sidx, resolve to a lockable object. /// \param Sidx The attribute argument index to start checking with. /// \param ParamIdxOk Whether an argument can be indexing into a function /// parameter list. -static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D, +static void checkAttrArgsAreLockableObjs(Sema &S, Decl *D, const AttributeList &Attr, SmallVectorImpl<Expr*> &Args, int Sidx = 0, @@ -307,13 +362,33 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D, Expr *ArgExp = Attr.getArg(Idx); if (ArgExp->isTypeDependent()) { - // FIXME -- need to processs this again on template instantiation + // FIXME -- need to check this again on template instantiation Args.push_back(ArgExp); continue; } + if (StringLiteral *StrLit = dyn_cast<StringLiteral>(ArgExp)) { + // Ignore empty strings without warnings + if (StrLit->getLength() == 0) + continue; + + // We allow constant strings to be used as a placeholder for expressions + // that are not valid C++ syntax, but warn that they are ignored. + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_ignored) << + Attr.getName(); + continue; + } + QualType ArgTy = ArgExp->getType(); + // A pointer to member expression of the form &MyClass::mu is treated + // specially -- we need to look at the type of the member. + if (UnaryOperator *UOp = dyn_cast<UnaryOperator>(ArgExp)) + if (UOp->getOpcode() == UO_AddrOf) + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(UOp->getSubExpr())) + if (DRE->getDecl()->isCXXInstanceMember()) + ArgTy = DRE->getDecl()->getType(); + // First see if we can just cast to record type, or point to record type. const RecordType *RT = getRecordType(ArgTy); @@ -329,7 +404,7 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D, if(!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_range) << Attr.getName() << Idx + 1 << NumParams; - return false; + continue; } ArgTy = FD->getParamDecl(ParamIdxFromZero)->getType(); } @@ -339,7 +414,6 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D, Args.push_back(ArgExp); } - return true; } //===----------------------------------------------------------------------===// @@ -350,78 +424,125 @@ static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D, // least add some helper functions to check most argument patterns (# // and types of args). -static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool pointer = false) { +enum ThreadAttributeDeclKind { + ThreadExpectedFieldOrGlobalVar, + ThreadExpectedFunctionOrMethod, + ThreadExpectedClassOrStruct +}; + +static bool checkGuardedVarAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr) { assert(!Attr.isInvalid()); if (!checkAttributeNumArgs(S, Attr, 0)) - return; + return false; // D must be either a member field or global (potentially shared) variable. if (!mayBeSharedVariable(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFieldOrGlobalVar; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFieldOrGlobalVar; + return false; } - if (pointer && !checkIsPointer(S, D, Attr)) + return true; +} + +static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkGuardedVarAttrCommon(S, D, Attr)) return; - if (pointer) - D->addAttr(::new (S.Context) PtGuardedVarAttr(Attr.getRange(), S.Context)); - else - D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getRange(), S.Context)); + D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getRange(), S.Context)); } -static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool pointer = false) { - assert(!Attr.isInvalid()); +static void handlePtGuardedVarAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkGuardedVarAttrCommon(S, D, Attr)) + return; - if (!checkAttributeNumArgs(S, Attr, 1)) + if (!threadSafetyCheckIsPointer(S, D, Attr)) return; - Expr *Arg = Attr.getArg(0); + D->addAttr(::new (S.Context) PtGuardedVarAttr(Attr.getRange(), S.Context)); +} + +static bool checkGuardedByAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr, + Expr* &Arg) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 1)) + return false; // D must be either a member field or global (potentially shared) variable. if (!mayBeSharedVariable(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFieldOrGlobalVar; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFieldOrGlobalVar; + return false; } - if (pointer && !checkIsPointer(S, D, Attr)) - return; + SmallVector<Expr*, 1> Args; + // check that all arguments are lockable objects + checkAttrArgsAreLockableObjs(S, D, Attr, Args); + unsigned Size = Args.size(); + if (Size != 1) + return false; - if (!Arg->isTypeDependent()) { - checkForLockableRecord(S, D, Attr, Arg->getType()); - } + Arg = Args[0]; - if (pointer) - D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(), - S.Context, Arg)); - else - D->addAttr(::new (S.Context) GuardedByAttr(Attr.getRange(), S.Context, Arg)); + return true; +} + +static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr) { + Expr *Arg = 0; + if (!checkGuardedByAttrCommon(S, D, Attr, Arg)) + return; + + D->addAttr(::new (S.Context) GuardedByAttr(Attr.getRange(), S.Context, Arg)); } +static void handlePtGuardedByAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + Expr *Arg = 0; + if (!checkGuardedByAttrCommon(S, D, Attr, Arg)) + return; + + if (!threadSafetyCheckIsPointer(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(), + S.Context, Arg)); +} -static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool scoped = false) { +static bool checkLockableAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr) { assert(!Attr.isInvalid()); if (!checkAttributeNumArgs(S, Attr, 0)) - return; + return false; // FIXME: Lockable structs for C code. if (!isa<CXXRecordDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedClass; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedClassOrStruct; + return false; } - if (scoped) - D->addAttr(::new (S.Context) ScopedLockableAttr(Attr.getRange(), S.Context)); - else - D->addAttr(::new (S.Context) LockableAttr(Attr.getRange(), S.Context)); + return true; +} + +static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkLockableAttrCommon(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) LockableAttr(Attr.getRange(), S.Context)); +} + +static void handleScopedLockableAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkLockableAttrCommon(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) ScopedLockableAttr(Attr.getRange(), S.Context)); } static void handleNoThreadSafetyAttr(Sema &S, Decl *D, @@ -432,8 +553,8 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D, return; if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; return; } @@ -442,7 +563,7 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D, } static void handleNoAddressSafetyAttr(Sema &S, Decl *D, - const AttributeList &Attr) { + const AttributeList &Attr) { assert(!Attr.isInvalid()); if (!checkAttributeNumArgs(S, Attr, 0)) @@ -455,154 +576,212 @@ static void handleNoAddressSafetyAttr(Sema &S, Decl *D, } D->addAttr(::new (S.Context) NoAddressSafetyAnalysisAttr(Attr.getRange(), - S.Context)); + S.Context)); } -static void handleAcquireOrderAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool before) { +static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr, + SmallVector<Expr*, 1> &Args) { assert(!Attr.isInvalid()); if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) - return; + return false; // D must be either a member field or global (potentially shared) variable. ValueDecl *VD = dyn_cast<ValueDecl>(D); if (!VD || !mayBeSharedVariable(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFieldOrGlobalVar; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFieldOrGlobalVar; + return false; } - // Check that this attribute only applies to lockable types + // Check that this attribute only applies to lockable types. QualType QT = VD->getType(); if (!QT->isDependentType()) { const RecordType *RT = getRecordType(QT); if (!RT || !RT->getDecl()->getAttr<LockableAttr>()) { - S.Diag(Attr.getLoc(), diag::warn_attribute_decl_not_lockable) - << Attr.getName(); - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_decl_not_lockable) + << Attr.getName(); + return false; } } + // Check that all arguments are lockable objects. + checkAttrArgsAreLockableObjs(S, D, Attr, Args); + if (Args.size() == 0) + return false; + + return true; +} + +static void handleAcquiredAfterAttr(Sema &S, Decl *D, + const AttributeList &Attr) { SmallVector<Expr*, 1> Args; - // check that all arguments are lockable objects - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args)) + if (!checkAcquireOrderAttrCommon(S, D, Attr, Args)) return; - unsigned Size = Args.size(); - assert(Size == Attr.getNumArgs()); - Expr **StartArg = Size == 0 ? 0 : &Args[0]; + Expr **StartArg = &Args[0]; + D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getRange(), S.Context, + StartArg, Args.size())); +} - if (before) - D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getRange(), S.Context, - StartArg, Size)); - else - D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getRange(), S.Context, - StartArg, Size)); +static void handleAcquiredBeforeAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + SmallVector<Expr*, 1> Args; + if (!checkAcquireOrderAttrCommon(S, D, Attr, Args)) + return; + + Expr **StartArg = &Args[0]; + D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getRange(), S.Context, + StartArg, Args.size())); } -static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { +static bool checkLockFunAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr, + SmallVector<Expr*, 1> &Args) { assert(!Attr.isInvalid()); // zero or more arguments ok // check that the attribute is applied to a function if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; + return false; } // check that all arguments are lockable objects + checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true); + + return true; +} + +static void handleSharedLockFunctionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { SmallVector<Expr*, 1> Args; - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true)) + if (!checkLockFunAttrCommon(S, D, Attr, Args)) return; unsigned Size = Args.size(); - assert(Size == Attr.getNumArgs()); Expr **StartArg = Size == 0 ? 0 : &Args[0]; + D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getRange(), + S.Context, + StartArg, Size)); +} - if (exclusive) - D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getRange(), - S.Context, StartArg, - Size)); - else - D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getRange(), - S.Context, StartArg, - Size)); +static void handleExclusiveLockFunctionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + SmallVector<Expr*, 1> Args; + if (!checkLockFunAttrCommon(S, D, Attr, Args)) + return; + + unsigned Size = Args.size(); + Expr **StartArg = Size == 0 ? 0 : &Args[0]; + D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getRange(), + S.Context, + StartArg, Size)); } -static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { +static bool checkTryLockFunAttrCommon(Sema &S, Decl *D, + const AttributeList &Attr, + SmallVector<Expr*, 2> &Args) { assert(!Attr.isInvalid()); if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) - return; - + return false; if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; + return false; } if (!isIntOrBool(Attr.getArg(0))) { S.Diag(Attr.getLoc(), diag::err_attribute_first_argument_not_int_or_bool) - << Attr.getName(); - return; + << Attr.getName(); + return false; } - SmallVector<Expr*, 2> Args; // check that all arguments are lockable objects - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1)) + checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1); + + return true; +} + +static void handleSharedTrylockFunctionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + SmallVector<Expr*, 2> Args; + if (!checkTryLockFunAttrCommon(S, D, Attr, Args)) return; unsigned Size = Args.size(); Expr **StartArg = Size == 0 ? 0 : &Args[0]; + D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getRange(), + S.Context, + Attr.getArg(0), + StartArg, Size)); +} - if (exclusive) - D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getRange(), - S.Context, - Attr.getArg(0), - StartArg, Size)); - else - D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getRange(), - S.Context, - Attr.getArg(0), - StartArg, Size)); +static void handleExclusiveTrylockFunctionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + SmallVector<Expr*, 2> Args; + if (!checkTryLockFunAttrCommon(S, D, Attr, Args)) + return; + + unsigned Size = Args.size(); + Expr **StartArg = Size == 0 ? 0 : &Args[0]; + D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getRange(), + S.Context, + Attr.getArg(0), + StartArg, Size)); } -static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr, - bool exclusive = false) { +static bool checkLocksRequiredCommon(Sema &S, Decl *D, + const AttributeList &Attr, + SmallVector<Expr*, 1> &Args) { assert(!Attr.isInvalid()); if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) - return; + return false; if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; - return; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; + return false; } // check that all arguments are lockable objects + checkAttrArgsAreLockableObjs(S, D, Attr, Args); + if (Args.size() == 0) + return false; + + return true; +} + +static void handleExclusiveLocksRequiredAttr(Sema &S, Decl *D, + const AttributeList &Attr) { SmallVector<Expr*, 1> Args; - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args)) + if (!checkLocksRequiredCommon(S, D, Attr, Args)) return; - unsigned Size = Args.size(); - assert(Size == Attr.getNumArgs()); - Expr **StartArg = Size == 0 ? 0 : &Args[0]; + Expr **StartArg = &Args[0]; + D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getRange(), + S.Context, + StartArg, + Args.size())); +} - if (exclusive) - D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getRange(), - S.Context, StartArg, - Size)); - else - D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getRange(), - S.Context, StartArg, - Size)); +static void handleSharedLocksRequiredAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + SmallVector<Expr*, 1> Args; + if (!checkLocksRequiredCommon(S, D, Attr, Args)) + return; + + Expr **StartArg = &Args[0]; + D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getRange(), + S.Context, + StartArg, + Args.size())); } static void handleUnlockFunAttr(Sema &S, Decl *D, @@ -612,18 +791,15 @@ static void handleUnlockFunAttr(Sema &S, Decl *D, // zero or more arguments ok if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; return; } // check that all arguments are lockable objects SmallVector<Expr*, 1> Args; - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true)) - return; - + checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true); unsigned Size = Args.size(); - assert(Size == Attr.getNumArgs()); Expr **StartArg = Size == 0 ? 0 : &Args[0]; D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getRange(), S.Context, @@ -639,8 +815,8 @@ static void handleLockReturnedAttr(Sema &S, Decl *D, Expr *Arg = Attr.getArg(0); if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; return; } @@ -648,9 +824,14 @@ static void handleLockReturnedAttr(Sema &S, Decl *D, return; // check that the argument is lockable object - checkForLockableRecord(S, D, Attr, Arg->getType()); + SmallVector<Expr*, 1> Args; + checkAttrArgsAreLockableObjs(S, D, Attr, Args); + unsigned Size = Args.size(); + if (Size == 0) + return; - D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getRange(), S.Context, Arg)); + D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getRange(), S.Context, + Args[0])); } static void handleLocksExcludedAttr(Sema &S, Decl *D, @@ -661,19 +842,18 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D, return; if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attr.getLoc(), diag::warn_thread_attribute_wrong_decl_type) + << Attr.getName() << ThreadExpectedFunctionOrMethod; return; } // check that all arguments are lockable objects SmallVector<Expr*, 1> Args; - if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args)) - return; - + checkAttrArgsAreLockableObjs(S, D, Attr, Args); unsigned Size = Args.size(); - assert(Size == Attr.getNumArgs()); - Expr **StartArg = Size == 0 ? 0 : &Args[0]; + if (Size == 0) + return; + Expr **StartArg = &Args[0]; D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getRange(), S.Context, StartArg, Size)); @@ -698,12 +878,12 @@ static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, SourceLocation TemplateKWLoc; UnqualifiedId id; id.setIdentifier(Attr.getParameterName(), Attr.getLoc()); - + ExprResult Size = S.ActOnIdExpression(scope, SS, TemplateKWLoc, id, false, false); if (Size.isInvalid()) return; - + sizeExpr = Size.get(); } else { // check the attribute arguments. @@ -854,6 +1034,75 @@ static void possibleTransparentUnionPointerType(QualType &T) { } } +static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isFunctionOrMethod(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << "alloc_size" << ExpectedFunctionOrMethod; + return; + } + + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + + // In C++ the implicit 'this' function parameter also counts, and they are + // counted from one. + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; + + SmallVector<unsigned, 8> SizeArgs; + + for (AttributeList::arg_iterator I = Attr.arg_begin(), + E = Attr.arg_end(); I!=E; ++I) { + // The argument must be an integer constant expression. + Expr *Ex = *I; + llvm::APSInt ArgNum; + if (Ex->isTypeDependent() || Ex->isValueDependent() || + !Ex->isIntegerConstantExpr(ArgNum, S.Context)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int) + << "alloc_size" << Ex->getSourceRange(); + return; + } + + uint64_t x = ArgNum.getZExtValue(); + + if (x < 1 || x > NumArgs) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) + << "alloc_size" << I.getArgNum() << Ex->getSourceRange(); + return; + } + + --x; + if (HasImplicitThisParam) { + if (x == 0) { + S.Diag(Attr.getLoc(), + diag::err_attribute_invalid_implicit_this_argument) + << "alloc_size" << Ex->getSourceRange(); + return; + } + --x; + } + + // check if the function argument is of an integer type + QualType T = getFunctionOrMethodArgType(D, x).getNonReferenceType(); + if (!T->isIntegerType()) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int) + << "alloc_size" << Ex->getSourceRange(); + return; + } + + SizeArgs.push_back(x); + } + + // check if the function returns a pointer + if (!getFunctionType(D)->getResultType()->isAnyPointerType()) { + S.Diag(Attr.getLoc(), diag::warn_ns_attribute_wrong_return_type) + << "alloc_size" << 0 /*function*/<< 1 /*pointer*/ << D->getSourceRange(); + } + + D->addAttr(::new (S.Context) AllocSizeAttr(Attr.getRange(), S.Context, + SizeArgs.data(), SizeArgs.size())); +} + static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { // GCC ignores the nonnull attribute on K&R style function prototypes, so we // ignore it as well @@ -1226,6 +1475,46 @@ static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) { Str->getString())); } +static void handleColdAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Check the attribute arguments. + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isa<FunctionDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunction; + return; + } + + if (D->hasAttr<HotAttr>()) { + S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) + << Attr.getName() << "hot"; + return; + } + + D->addAttr(::new (S.Context) ColdAttr(Attr.getRange(), S.Context)); +} + +static void handleHotAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Check the attribute arguments. + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isa<FunctionDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunction; + return; + } + + if (D->hasAttr<ColdAttr>()) { + S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) + << Attr.getName() << "cold"; + return; + } + + D->addAttr(::new (S.Context) HotAttr(Attr.getRange(), S.Context)); +} + static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. if (!checkAttributeNumArgs(S, Attr, 0)) @@ -1257,6 +1546,42 @@ static void handleAlwaysInlineAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) AlwaysInlineAttr(Attr.getRange(), S.Context)); } +static void handleTLSModelAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + // Check the attribute arguments. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + return; + } + + Expr *Arg = Attr.getArg(0); + Arg = Arg->IgnoreParenCasts(); + StringLiteral *Str = dyn_cast<StringLiteral>(Arg); + + // Check that it is a string. + if (!Str) { + S.Diag(Attr.getLoc(), diag::err_attribute_not_string) << "tls_model"; + return; + } + + if (!isa<VarDecl>(D) || !cast<VarDecl>(D)->isThreadSpecified()) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedTLSVar; + return; + } + + // Check that the value. + StringRef Model = Str->getString(); + if (Model != "global-dynamic" && Model != "local-dynamic" + && Model != "initial-exec" && Model != "local-exec") { + S.Diag(Attr.getLoc(), diag::err_attr_tlsmodel_arg); + return; + } + + D->addAttr(::new (S.Context) TLSModelAttr(Attr.getRange(), S.Context, + Model)); +} + static void handleMallocAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. if (Attr.hasParameterOrArguments()) { @@ -1427,7 +1752,7 @@ static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } if (!isa<VarDecl>(D) && !isa<ObjCIvarDecl>(D) && !isFunctionOrMethod(D) && - !isa<TypeDecl>(D) && !isa<LabelDecl>(D)) { + !isa<TypeDecl>(D) && !isa<LabelDecl>(D) && !isa<FieldDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableFunctionOrLabel; return; @@ -1534,47 +1859,28 @@ static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { priority)); } -static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) { +template <typename AttrTy> +static void handleAttrWithMessage(Sema &S, Decl *D, const AttributeList &Attr, + const char *Name) { unsigned NumArgs = Attr.getNumArgs(); if (NumArgs > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; return; } - - // Handle the case where deprecated attribute has a text message. + + // Handle the case where the attribute has a text message. StringRef Str; if (NumArgs == 1) { StringLiteral *SE = dyn_cast<StringLiteral>(Attr.getArg(0)); if (!SE) { S.Diag(Attr.getArg(0)->getLocStart(), diag::err_attribute_not_string) - << "deprecated"; + << Name; return; } Str = SE->getString(); } - D->addAttr(::new (S.Context) DeprecatedAttr(Attr.getRange(), S.Context, Str)); -} - -static void handleUnavailableAttr(Sema &S, Decl *D, const AttributeList &Attr) { - unsigned NumArgs = Attr.getNumArgs(); - if (NumArgs > 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; - return; - } - - // Handle the case where unavailable attribute has a text message. - StringRef Str; - if (NumArgs == 1) { - StringLiteral *SE = dyn_cast<StringLiteral>(Attr.getArg(0)); - if (!SE) { - S.Diag(Attr.getArg(0)->getLocStart(), - diag::err_attribute_not_string) << "unavailable"; - return; - } - Str = SE->getString(); - } - D->addAttr(::new (S.Context) UnavailableAttr(Attr.getRange(), S.Context, Str)); + D->addAttr(::new (S.Context) AttrTy(Attr.getRange(), S.Context, Str)); } static void handleArcWeakrefUnavailableAttr(Sema &S, Decl *D, @@ -1622,64 +1928,180 @@ static void handleObjCRequiresPropertyDefsAttr(Sema &S, Decl *D, Attr.getRange(), S.Context)); } -static void handleAvailabilityAttr(Sema &S, Decl *D, - const AttributeList &Attr) { - IdentifierInfo *Platform = Attr.getParameterName(); - SourceLocation PlatformLoc = Attr.getParameterLoc(); - +static bool checkAvailabilityAttr(Sema &S, SourceRange Range, + IdentifierInfo *Platform, + VersionTuple Introduced, + VersionTuple Deprecated, + VersionTuple Obsoleted) { StringRef PlatformName = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); - if (PlatformName.empty()) { - S.Diag(PlatformLoc, diag::warn_availability_unknown_platform) - << Platform; - + if (PlatformName.empty()) PlatformName = Platform->getName(); - } - - AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); - AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); - AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); - bool IsUnavailable = Attr.getUnavailableLoc().isValid(); // Ensure that Introduced <= Deprecated <= Obsoleted (although not all // of these steps are needed). - if (Introduced.isValid() && Deprecated.isValid() && - !(Introduced.Version <= Deprecated.Version)) { - S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) - << 1 << PlatformName << Deprecated.Version.getAsString() - << 0 << Introduced.Version.getAsString(); - return; + if (!Introduced.empty() && !Deprecated.empty() && + !(Introduced <= Deprecated)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 1 << PlatformName << Deprecated.getAsString() + << 0 << Introduced.getAsString(); + return true; } - if (Introduced.isValid() && Obsoleted.isValid() && - !(Introduced.Version <= Obsoleted.Version)) { - S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) - << 2 << PlatformName << Obsoleted.Version.getAsString() - << 0 << Introduced.Version.getAsString(); - return; + if (!Introduced.empty() && !Obsoleted.empty() && + !(Introduced <= Obsoleted)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.getAsString() + << 0 << Introduced.getAsString(); + return true; } - if (Deprecated.isValid() && Obsoleted.isValid() && - !(Deprecated.Version <= Obsoleted.Version)) { - S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering) - << 2 << PlatformName << Obsoleted.Version.getAsString() - << 1 << Deprecated.Version.getAsString(); - return; + if (!Deprecated.empty() && !Obsoleted.empty() && + !(Deprecated <= Obsoleted)) { + S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.getAsString() + << 1 << Deprecated.getAsString(); + return true; } + return false; +} + +AvailabilityAttr *Sema::mergeAvailabilityAttr(Decl *D, SourceRange Range, + IdentifierInfo *Platform, + VersionTuple Introduced, + VersionTuple Deprecated, + VersionTuple Obsoleted, + bool IsUnavailable, + StringRef Message) { + VersionTuple MergedIntroduced = Introduced; + VersionTuple MergedDeprecated = Deprecated; + VersionTuple MergedObsoleted = Obsoleted; + bool FoundAny = false; + + if (D->hasAttrs()) { + AttrVec &Attrs = D->getAttrs(); + for (unsigned i = 0, e = Attrs.size(); i != e;) { + const AvailabilityAttr *OldAA = dyn_cast<AvailabilityAttr>(Attrs[i]); + if (!OldAA) { + ++i; + continue; + } + + IdentifierInfo *OldPlatform = OldAA->getPlatform(); + if (OldPlatform != Platform) { + ++i; + continue; + } + + FoundAny = true; + VersionTuple OldIntroduced = OldAA->getIntroduced(); + VersionTuple OldDeprecated = OldAA->getDeprecated(); + VersionTuple OldObsoleted = OldAA->getObsoleted(); + bool OldIsUnavailable = OldAA->getUnavailable(); + StringRef OldMessage = OldAA->getMessage(); + + if ((!OldIntroduced.empty() && !Introduced.empty() && + OldIntroduced != Introduced) || + (!OldDeprecated.empty() && !Deprecated.empty() && + OldDeprecated != Deprecated) || + (!OldObsoleted.empty() && !Obsoleted.empty() && + OldObsoleted != Obsoleted) || + (OldIsUnavailable != IsUnavailable) || + (OldMessage != Message)) { + Diag(OldAA->getLocation(), diag::warn_mismatched_availability); + Diag(Range.getBegin(), diag::note_previous_attribute); + Attrs.erase(Attrs.begin() + i); + --e; + continue; + } + + VersionTuple MergedIntroduced2 = MergedIntroduced; + VersionTuple MergedDeprecated2 = MergedDeprecated; + VersionTuple MergedObsoleted2 = MergedObsoleted; + + if (MergedIntroduced2.empty()) + MergedIntroduced2 = OldIntroduced; + if (MergedDeprecated2.empty()) + MergedDeprecated2 = OldDeprecated; + if (MergedObsoleted2.empty()) + MergedObsoleted2 = OldObsoleted; + + if (checkAvailabilityAttr(*this, OldAA->getRange(), Platform, + MergedIntroduced2, MergedDeprecated2, + MergedObsoleted2)) { + Attrs.erase(Attrs.begin() + i); + --e; + continue; + } + + MergedIntroduced = MergedIntroduced2; + MergedDeprecated = MergedDeprecated2; + MergedObsoleted = MergedObsoleted2; + ++i; + } + } + + if (FoundAny && + MergedIntroduced == Introduced && + MergedDeprecated == Deprecated && + MergedObsoleted == Obsoleted) + return NULL; + + if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced, + MergedDeprecated, MergedObsoleted)) { + return ::new (Context) AvailabilityAttr(Range, Context, Platform, + Introduced, Deprecated, + Obsoleted, IsUnavailable, Message); + } + return NULL; +} + +static void handleAvailabilityAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + IdentifierInfo *Platform = Attr.getParameterName(); + SourceLocation PlatformLoc = Attr.getParameterLoc(); + + if (AvailabilityAttr::getPrettyPlatformName(Platform->getName()).empty()) + S.Diag(PlatformLoc, diag::warn_availability_unknown_platform) + << Platform; + + AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); + AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); + AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); + bool IsUnavailable = Attr.getUnavailableLoc().isValid(); StringRef Str; const StringLiteral *SE = dyn_cast_or_null<const StringLiteral>(Attr.getMessageExpr()); if (SE) Str = SE->getString(); - - D->addAttr(::new (S.Context) AvailabilityAttr(Attr.getRange(), S.Context, - Platform, - Introduced.Version, - Deprecated.Version, - Obsoleted.Version, - IsUnavailable, - Str)); + + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(D, Attr.getRange(), + Platform, + Introduced.Version, + Deprecated.Version, + Obsoleted.Version, + IsUnavailable, Str); + if (NewAttr) + D->addAttr(NewAttr); +} + +VisibilityAttr *Sema::mergeVisibilityAttr(Decl *D, SourceRange Range, + VisibilityAttr::VisibilityType Vis) { + if (isa<TypedefNameDecl>(D)) { + Diag(Range.getBegin(), diag::warn_attribute_ignored) << "visibility"; + return NULL; + } + VisibilityAttr *ExistingAttr = D->getAttr<VisibilityAttr>(); + if (ExistingAttr) { + VisibilityAttr::VisibilityType ExistingVis = ExistingAttr->getVisibility(); + if (ExistingVis == Vis) + return NULL; + Diag(ExistingAttr->getLocation(), diag::err_mismatched_visibility); + Diag(Range.getBegin(), diag::note_previous_attribute); + D->dropAttr<VisibilityAttr>(); + } + return ::new (Context) VisibilityAttr(Range, Context, Vis); } static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -1720,7 +2142,9 @@ static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - D->addAttr(::new (S.Context) VisibilityAttr(Attr.getRange(), S.Context, type)); + VisibilityAttr *NewAttr = S.mergeVisibilityAttr(D, Attr.getRange(), type); + if (NewAttr) + D->addAttr(NewAttr); } static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl, @@ -1803,7 +2227,15 @@ static void handleObjCNSObject(Sema &S, Decl *D, const AttributeList &Attr) { return; } } - else if (!isa<ObjCPropertyDecl>(D)) { + else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) { + QualType T = PD->getType(); + if (!T->isPointerType() || + !T->getAs<PointerType>()->getPointeeType()->isRecordType()) { + S.Diag(PD->getLocation(), diag::err_nsobject_attribute); + return; + } + } + else { // It is okay to include this attribute on properties, e.g.: // // @property (retain, nonatomic) struct Bork *Q __attribute__((NSObject)); @@ -2028,11 +2460,14 @@ static void handleWeakImportAttr(Sema &S, Decl *D, const AttributeList &Attr) { D->addAttr(::new (S.Context) WeakImportAttr(Attr.getRange(), S.Context)); } -static void handleReqdWorkGroupSize(Sema &S, Decl *D, - const AttributeList &Attr) { +// Handles reqd_work_group_size and work_group_size_hint. +static void handleWorkGroupSize(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize + || Attr.getKind() == AttributeList::AT_WorkGroupSizeHint); + // Attribute has 3 arguments. - if (!checkAttributeNumArgs(S, Attr, 3)) - return; + if (!checkAttributeNumArgs(S, Attr, 3)) return; unsigned WGSize[3]; for (unsigned i = 0; i < 3; ++i) { @@ -2041,14 +2476,54 @@ static void handleReqdWorkGroupSize(Sema &S, Decl *D, if (E->isTypeDependent() || E->isValueDependent() || !E->isIntegerConstantExpr(ArgNum, S.Context)) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int) - << "reqd_work_group_size" << E->getSourceRange(); + << Attr.getName()->getName() << E->getSourceRange(); return; } WGSize[i] = (unsigned) ArgNum.getZExtValue(); } - D->addAttr(::new (S.Context) ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context, - WGSize[0], WGSize[1], - WGSize[2])); + + if (Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize + && D->hasAttr<ReqdWorkGroupSizeAttr>()) { + ReqdWorkGroupSizeAttr *A = D->getAttr<ReqdWorkGroupSizeAttr>(); + if (!(A->getXDim() == WGSize[0] && + A->getYDim() == WGSize[1] && + A->getZDim() == WGSize[2])) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << + Attr.getName(); + } + } + + if (Attr.getKind() == AttributeList::AT_WorkGroupSizeHint + && D->hasAttr<WorkGroupSizeHintAttr>()) { + WorkGroupSizeHintAttr *A = D->getAttr<WorkGroupSizeHintAttr>(); + if (!(A->getXDim() == WGSize[0] && + A->getYDim() == WGSize[1] && + A->getZDim() == WGSize[2])) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << + Attr.getName(); + } + } + + if (Attr.getKind() == AttributeList::AT_ReqdWorkGroupSize) + D->addAttr(::new (S.Context) + ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context, + WGSize[0], WGSize[1], WGSize[2])); + else + D->addAttr(::new (S.Context) + WorkGroupSizeHintAttr(Attr.getRange(), S.Context, + WGSize[0], WGSize[1], WGSize[2])); +} + +SectionAttr *Sema::mergeSectionAttr(Decl *D, SourceRange Range, + StringRef Name) { + if (SectionAttr *ExistingAttr = D->getAttr<SectionAttr>()) { + if (ExistingAttr->getName() == Name) + return NULL; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_section); + Diag(Range.getBegin(), diag::note_previous_attribute); + return NULL; + } + return ::new (Context) SectionAttr(Range, Context, Name); } static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -2078,9 +2553,10 @@ static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) { S.Diag(SE->getLocStart(), diag::err_attribute_section_local_variable); return; } - - D->addAttr(::new (S.Context) SectionAttr(Attr.getRange(), S.Context, - SE->getString())); + SectionAttr *NewAttr = S.mergeSectionAttr(D, Attr.getRange(), + SE->getString()); + if (NewAttr) + D->addAttr(NewAttr); } @@ -2269,26 +2745,19 @@ enum FormatAttrKind { /// getFormatAttrKind - Map from format attribute names to supported format /// types. static FormatAttrKind getFormatAttrKind(StringRef Format) { - // Check for formats that get handled specially. - if (Format == "NSString") - return NSStringFormat; - if (Format == "CFString") - return CFStringFormat; - if (Format == "strftime") - return StrftimeFormat; - - // Otherwise, check for supported formats. - if (Format == "scanf" || Format == "printf" || Format == "printf0" || - Format == "strfmon" || Format == "cmn_err" || Format == "vcmn_err" || - Format == "zcmn_err" || - Format == "kprintf") // OpenBSD. - return SupportedFormat; - - if (Format == "gcc_diag" || Format == "gcc_cdiag" || - Format == "gcc_cxxdiag" || Format == "gcc_tdiag") - return IgnoredFormat; - - return InvalidFormat; + return llvm::StringSwitch<FormatAttrKind>(Format) + // Check for formats that get handled specially. + .Case("NSString", NSStringFormat) + .Case("CFString", CFStringFormat) + .Case("strftime", StrftimeFormat) + + // Otherwise, check for supported formats. + .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat) + .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat) + .Case("kprintf", SupportedFormat) // OpenBSD. + + .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat) + .Default(InvalidFormat); } /// Handle __attribute__((init_priority(priority))) attributes based on @@ -2340,6 +2809,29 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, prioritynum)); } +FormatAttr *Sema::mergeFormatAttr(Decl *D, SourceRange Range, StringRef Format, + int FormatIdx, int FirstArg) { + // Check whether we already have an equivalent format attribute. + for (specific_attr_iterator<FormatAttr> + i = D->specific_attr_begin<FormatAttr>(), + e = D->specific_attr_end<FormatAttr>(); + i != e ; ++i) { + FormatAttr *f = *i; + if (f->getType() == Format && + f->getFormatIdx() == FormatIdx && + f->getFirstArg() == FirstArg) { + // If we don't have a valid location for this attribute, adopt the + // location. + if (f->getLocation().isInvalid()) + f->setRange(Range); + return NULL; + } + } + + return ::new (Context) FormatAttr(Range, Context, Format, FormatIdx, + FirstArg); +} + /// Handle __attribute__((format(type,idx,firstarg))) attributes based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -2475,26 +2967,11 @@ static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - // Check whether we already have an equivalent format attribute. - for (specific_attr_iterator<FormatAttr> - i = D->specific_attr_begin<FormatAttr>(), - e = D->specific_attr_end<FormatAttr>(); - i != e ; ++i) { - FormatAttr *f = *i; - if (f->getType() == Format && - f->getFormatIdx() == (int)Idx.getZExtValue() && - f->getFirstArg() == (int)FirstArg.getZExtValue()) { - // If we don't have a valid location for this attribute, adopt the - // location. - if (f->getLocation().isInvalid()) - f->setRange(Attr.getRange()); - return; - } - } - - D->addAttr(::new (S.Context) FormatAttr(Attr.getRange(), S.Context, Format, + FormatAttr *NewAttr = S.mergeFormatAttr(D, Attr.getRange(), Format, Idx.getZExtValue(), - FirstArg.getZExtValue())); + FirstArg.getZExtValue()); + if (NewAttr) + D->addAttr(NewAttr); } static void handleTransparentUnionAttr(Sema &S, Decl *D, @@ -2596,37 +3073,41 @@ static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; return; } - + //FIXME: The C++0x version of this attribute has more limited applicabilty // than GNU's, and should error out when it is used to specify a // weaker alignment, rather than being silently ignored. if (Attr.getNumArgs() == 0) { - D->addAttr(::new (S.Context) AlignedAttr(Attr.getRange(), S.Context, true, 0)); + D->addAttr(::new (S.Context) AlignedAttr(Attr.getRange(), S.Context, + true, 0, Attr.isDeclspecAttribute())); return; } - S.AddAlignedAttr(Attr.getRange(), D, Attr.getArg(0)); + S.AddAlignedAttr(Attr.getRange(), D, Attr.getArg(0), + Attr.isDeclspecAttribute()); } -void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E) { +void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, + bool isDeclSpec) { // FIXME: Handle pack-expansions here. if (DiagnoseUnexpandedParameterPack(E)) return; if (E->isTypeDependent() || E->isValueDependent()) { // Save dependent expressions in the AST to be instantiated. - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, E)); + D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, E, + isDeclSpec)); return; } - + SourceLocation AttrLoc = AttrRange.getBegin(); // FIXME: Cache the number on the Attr object? llvm::APSInt Alignment(32); - ExprResult ICE = - VerifyIntegerConstantExpression(E, &Alignment, - PDiag(diag::err_attribute_argument_not_int) << "aligned", - /*AllowFold*/ false); + ExprResult ICE + = VerifyIntegerConstantExpression(E, &Alignment, + diag::err_aligned_attribute_argument_not_int, + /*AllowFold*/ false); if (ICE.isInvalid()) return; if (!llvm::isPowerOf2_64(Alignment.getZExtValue())) { @@ -2634,14 +3115,26 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E) { << E->getSourceRange(); return; } + if (isDeclSpec) { + // We've already verified it's a power of 2, now let's make sure it's + // 8192 or less. + if (Alignment.getZExtValue() > 8192) { + Diag(AttrLoc, diag::err_attribute_aligned_greater_than_8192) + << E->getSourceRange(); + return; + } + } - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, ICE.take())); + D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, true, ICE.take(), + isDeclSpec)); } -void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS) { +void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *TS, + bool isDeclSpec) { // FIXME: Cache the number on the Attr object if non-dependent? // FIXME: Perform checking of type validity - D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, false, TS)); + D->addAttr(::new (Context) AlignedAttr(AttrRange, Context, false, TS, + isDeclSpec)); return; } @@ -2820,9 +3313,15 @@ static void handleNoDebugAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!checkAttributeNumArgs(S, Attr, 0)) return; - if (!isFunctionOrMethod(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedFunction; + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!VD->hasGlobalStorage()) + S.Diag(Attr.getLoc(), + diag::warn_attribute_requires_functions_or_static_globals) + << Attr.getName(); + } else if (!isFunctionOrMethod(D)) { + S.Diag(Attr.getLoc(), + diag::warn_attribute_requires_functions_or_static_globals) + << Attr.getName(); return; } @@ -3008,22 +3507,22 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { } switch (Attr.getKind()) { - case AttributeList::AT_fastcall: + case AttributeList::AT_FastCall: D->addAttr(::new (S.Context) FastCallAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_stdcall: + case AttributeList::AT_StdCall: D->addAttr(::new (S.Context) StdCallAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_thiscall: + case AttributeList::AT_ThisCall: D->addAttr(::new (S.Context) ThisCallAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_cdecl: + case AttributeList::AT_CDecl: D->addAttr(::new (S.Context) CDeclAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_pascal: + case AttributeList::AT_Pascal: D->addAttr(::new (S.Context) PascalAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_pcs: { + case AttributeList::AT_Pcs: { Expr *Arg = Attr.getArg(0); StringLiteral *Str = dyn_cast<StringLiteral>(Arg); if (!Str || !Str->isAscii()) { @@ -3062,7 +3561,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { return true; if ((attr.getNumArgs() != 0 && - !(attr.getKind() == AttributeList::AT_pcs && attr.getNumArgs() == 1)) || + !(attr.getKind() == AttributeList::AT_Pcs && attr.getNumArgs() == 1)) || attr.getParameterName()) { Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; attr.setInvalid(); @@ -3072,12 +3571,12 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { // TODO: diagnose uses of these conventions on the wrong target. Or, better // move to TargetAttributesSema one day. switch (attr.getKind()) { - case AttributeList::AT_cdecl: CC = CC_C; break; - case AttributeList::AT_fastcall: CC = CC_X86FastCall; break; - case AttributeList::AT_stdcall: CC = CC_X86StdCall; break; - case AttributeList::AT_thiscall: CC = CC_X86ThisCall; break; - case AttributeList::AT_pascal: CC = CC_X86Pascal; break; - case AttributeList::AT_pcs: { + case AttributeList::AT_CDecl: CC = CC_C; break; + case AttributeList::AT_FastCall: CC = CC_X86FastCall; break; + case AttributeList::AT_StdCall: CC = CC_X86StdCall; break; + case AttributeList::AT_ThisCall: CC = CC_X86ThisCall; break; + case AttributeList::AT_Pascal: CC = CC_X86Pascal; break; + case AttributeList::AT_Pcs: { Expr *Arg = attr.getArg(0); StringLiteral *Str = dyn_cast<StringLiteral>(Arg); if (!Str || !Str->isAscii()) { @@ -3228,7 +3727,7 @@ static void handleNSConsumedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } bool typeOK, cf; - if (Attr.getKind() == AttributeList::AT_ns_consumed) { + if (Attr.getKind() == AttributeList::AT_NSConsumed) { typeOK = isValidSubjectOfNSAttribute(S, param->getType()); cf = false; } else { @@ -3269,7 +3768,7 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) returnType = PD->getType(); else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && - (Attr.getKind() == AttributeList::AT_ns_returns_retained)) + (Attr.getKind() == AttributeList::AT_NSReturnsRetained)) return; // ignore: was handled as a type attribute else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) returnType = FD->getResultType(); @@ -3284,15 +3783,15 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, bool cf; switch (Attr.getKind()) { default: llvm_unreachable("invalid ownership attribute"); - case AttributeList::AT_ns_returns_autoreleased: - case AttributeList::AT_ns_returns_retained: - case AttributeList::AT_ns_returns_not_retained: + case AttributeList::AT_NSReturnsAutoreleased: + case AttributeList::AT_NSReturnsRetained: + case AttributeList::AT_NSReturnsNotRetained: typeOK = isValidSubjectOfNSAttribute(S, returnType); cf = false; break; - case AttributeList::AT_cf_returns_retained: - case AttributeList::AT_cf_returns_not_retained: + case AttributeList::AT_CFReturnsRetained: + case AttributeList::AT_CFReturnsNotRetained: typeOK = isValidSubjectOfCFAttribute(S, returnType); cf = true; break; @@ -3307,23 +3806,23 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, switch (Attr.getKind()) { default: llvm_unreachable("invalid ownership attribute"); - case AttributeList::AT_ns_returns_autoreleased: + case AttributeList::AT_NSReturnsAutoreleased: D->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_cf_returns_not_retained: + case AttributeList::AT_CFReturnsNotRetained: D->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_ns_returns_not_retained: + case AttributeList::AT_NSReturnsNotRetained: D->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_cf_returns_retained: + case AttributeList::AT_CFReturnsRetained: D->addAttr(::new (S.Context) CFReturnsRetainedAttr(Attr.getRange(), S.Context)); return; - case AttributeList::AT_ns_returns_retained: + case AttributeList::AT_NSReturnsRetained: D->addAttr(::new (S.Context) NSReturnsRetainedAttr(Attr.getRange(), S.Context)); return; @@ -3336,8 +3835,8 @@ static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D, ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(D); - if (!isa<ObjCMethodDecl>(method)) { - S.Diag(method->getLocStart(), diag::err_attribute_wrong_decl_type) + if (!method) { + S.Diag(D->getLocStart(), diag::err_attribute_wrong_decl_type) << SourceRange(loc, loc) << attr.getName() << ExpectedMethod; return; } @@ -3367,7 +3866,7 @@ static void handleCFTransferAttr(Sema &S, Decl *D, const AttributeList &A) { return; } - bool IsAudited = (A.getKind() == AttributeList::AT_cf_audited_transfer); + bool IsAudited = (A.getKind() == AttributeList::AT_CFAuditedTransfer); // Check whether there's a conflicting attribute already present. Attr *Existing; @@ -3478,22 +3977,6 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, ObjCPreciseLifetimeAttr(Attr.getRange(), S.Context)); } -static bool isKnownDeclSpecAttr(const AttributeList &Attr) { - switch (Attr.getKind()) { - default: - return false; - case AttributeList::AT_dllimport: - case AttributeList::AT_dllexport: - case AttributeList::AT_uuid: - case AttributeList::AT_deprecated: - case AttributeList::AT_noreturn: - case AttributeList::AT_nothrow: - case AttributeList::AT_naked: - case AttributeList::AT_noinline: - return true; - } -} - //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -3552,6 +4035,45 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "uuid"; } +static void handleInheritanceAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (S.LangOpts.MicrosoftExt) { + AttributeList::Kind Kind = Attr.getKind(); + if (Kind == AttributeList::AT_SingleInheritance) + D->addAttr( + ::new (S.Context) SingleInheritanceAttr(Attr.getRange(), S.Context)); + else if (Kind == AttributeList::AT_MultipleInheritance) + D->addAttr( + ::new (S.Context) MultipleInheritanceAttr(Attr.getRange(), S.Context)); + else if (Kind == AttributeList::AT_VirtualInheritance) + D->addAttr( + ::new (S.Context) VirtualInheritanceAttr(Attr.getRange(), S.Context)); + } else + S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); +} + +static void handlePortabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (S.LangOpts.MicrosoftExt) { + AttributeList::Kind Kind = Attr.getKind(); + if (Kind == AttributeList::AT_Ptr32) + D->addAttr( + ::new (S.Context) Ptr32Attr(Attr.getRange(), S.Context)); + else if (Kind == AttributeList::AT_Ptr64) + D->addAttr( + ::new (S.Context) Ptr64Attr(Attr.getRange(), S.Context)); + else if (Kind == AttributeList::AT_Win64) + D->addAttr( + ::new (S.Context) Win64Attr(Attr.getRange(), S.Context)); + } else + S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); +} + +static void handleForceInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (S.LangOpts.MicrosoftExt) + D->addAttr(::new (S.Context) ForceInlineAttr(Attr.getRange(), S.Context)); + else + S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -3559,9 +4081,9 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) { static void ProcessNonInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { switch (Attr.getKind()) { - case AttributeList::AT_device: handleDeviceAttr (S, D, Attr); break; - case AttributeList::AT_host: handleHostAttr (S, D, Attr); break; - case AttributeList::AT_overloadable:handleOverloadableAttr(S, D, Attr); break; + case AttributeList::AT_CUDADevice: handleDeviceAttr (S, D, Attr); break; + case AttributeList::AT_CUDAHost: handleHostAttr (S, D, Attr); break; + case AttributeList::AT_Overloadable:handleOverloadableAttr(S, D, Attr); break; default: break; } @@ -3570,227 +4092,254 @@ static void ProcessNonInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { switch (Attr.getKind()) { - case AttributeList::AT_ibaction: handleIBAction(S, D, Attr); break; - case AttributeList::AT_iboutlet: handleIBOutlet(S, D, Attr); break; - case AttributeList::AT_iboutletcollection: + case AttributeList::AT_IBAction: handleIBAction(S, D, Attr); break; + case AttributeList::AT_IBOutlet: handleIBOutlet(S, D, Attr); break; + case AttributeList::AT_IBOutletCollection: handleIBOutletCollection(S, D, Attr); break; - case AttributeList::AT_address_space: - case AttributeList::AT_opencl_image_access: - case AttributeList::AT_objc_gc: - case AttributeList::AT_vector_size: - case AttributeList::AT_neon_vector_type: - case AttributeList::AT_neon_polyvector_type: + case AttributeList::AT_AddressSpace: + case AttributeList::AT_OpenCLImageAccess: + case AttributeList::AT_ObjCGC: + case AttributeList::AT_VectorSize: + case AttributeList::AT_NeonVectorType: + case AttributeList::AT_NeonPolyVectorType: // Ignore these, these are type attributes, handled by // ProcessTypeAttributes. break; - case AttributeList::AT_device: - case AttributeList::AT_host: - case AttributeList::AT_overloadable: + case AttributeList::AT_CUDADevice: + case AttributeList::AT_CUDAHost: + case AttributeList::AT_Overloadable: // Ignore, this is a non-inheritable attribute, handled // by ProcessNonInheritableDeclAttr. break; - case AttributeList::AT_alias: handleAliasAttr (S, D, Attr); break; - case AttributeList::AT_aligned: handleAlignedAttr (S, D, Attr); break; - case AttributeList::AT_always_inline: + case AttributeList::AT_Alias: handleAliasAttr (S, D, Attr); break; + case AttributeList::AT_Aligned: handleAlignedAttr (S, D, Attr); break; + case AttributeList::AT_AllocSize: handleAllocSizeAttr (S, D, Attr); break; + case AttributeList::AT_AlwaysInline: handleAlwaysInlineAttr (S, D, Attr); break; - case AttributeList::AT_analyzer_noreturn: + case AttributeList::AT_AnalyzerNoReturn: handleAnalyzerNoReturnAttr (S, D, Attr); break; - case AttributeList::AT_annotate: handleAnnotateAttr (S, D, Attr); break; - case AttributeList::AT_availability:handleAvailabilityAttr(S, D, Attr); break; - case AttributeList::AT_carries_dependency: + case AttributeList::AT_TLSModel: handleTLSModelAttr (S, D, Attr); break; + case AttributeList::AT_Annotate: handleAnnotateAttr (S, D, Attr); break; + case AttributeList::AT_Availability:handleAvailabilityAttr(S, D, Attr); break; + case AttributeList::AT_CarriesDependency: handleDependencyAttr (S, D, Attr); break; - case AttributeList::AT_common: handleCommonAttr (S, D, Attr); break; - case AttributeList::AT_constant: handleConstantAttr (S, D, Attr); break; - case AttributeList::AT_constructor: handleConstructorAttr (S, D, Attr); break; - case AttributeList::AT_deprecated: handleDeprecatedAttr (S, D, Attr); break; - case AttributeList::AT_destructor: handleDestructorAttr (S, D, Attr); break; - case AttributeList::AT_ext_vector_type: + case AttributeList::AT_Common: handleCommonAttr (S, D, Attr); break; + case AttributeList::AT_CUDAConstant:handleConstantAttr (S, D, Attr); break; + case AttributeList::AT_Constructor: handleConstructorAttr (S, D, Attr); break; + case AttributeList::AT_Deprecated: + handleAttrWithMessage<DeprecatedAttr>(S, D, Attr, "deprecated"); + break; + case AttributeList::AT_Destructor: handleDestructorAttr (S, D, Attr); break; + case AttributeList::AT_ExtVectorType: handleExtVectorTypeAttr(S, scope, D, Attr); break; - case AttributeList::AT_format: handleFormatAttr (S, D, Attr); break; - case AttributeList::AT_format_arg: handleFormatArgAttr (S, D, Attr); break; - case AttributeList::AT_global: handleGlobalAttr (S, D, Attr); break; - case AttributeList::AT_gnu_inline: handleGNUInlineAttr (S, D, Attr); break; - case AttributeList::AT_launch_bounds: + case AttributeList::AT_Format: handleFormatAttr (S, D, Attr); break; + case AttributeList::AT_FormatArg: handleFormatArgAttr (S, D, Attr); break; + case AttributeList::AT_CUDAGlobal: handleGlobalAttr (S, D, Attr); break; + case AttributeList::AT_GNUInline: handleGNUInlineAttr (S, D, Attr); break; + case AttributeList::AT_CUDALaunchBounds: handleLaunchBoundsAttr(S, D, Attr); break; - case AttributeList::AT_mode: handleModeAttr (S, D, Attr); break; - case AttributeList::AT_malloc: handleMallocAttr (S, D, Attr); break; - case AttributeList::AT_may_alias: handleMayAliasAttr (S, D, Attr); break; - case AttributeList::AT_nocommon: handleNoCommonAttr (S, D, Attr); break; - case AttributeList::AT_nonnull: handleNonNullAttr (S, D, Attr); break; + case AttributeList::AT_Mode: handleModeAttr (S, D, Attr); break; + case AttributeList::AT_Malloc: handleMallocAttr (S, D, Attr); break; + case AttributeList::AT_MayAlias: handleMayAliasAttr (S, D, Attr); break; + case AttributeList::AT_NoCommon: handleNoCommonAttr (S, D, Attr); break; + case AttributeList::AT_NonNull: handleNonNullAttr (S, D, Attr); break; case AttributeList::AT_ownership_returns: case AttributeList::AT_ownership_takes: case AttributeList::AT_ownership_holds: handleOwnershipAttr (S, D, Attr); break; - case AttributeList::AT_naked: handleNakedAttr (S, D, Attr); break; - case AttributeList::AT_noreturn: handleNoReturnAttr (S, D, Attr); break; - case AttributeList::AT_nothrow: handleNothrowAttr (S, D, Attr); break; - case AttributeList::AT_shared: handleSharedAttr (S, D, Attr); break; - case AttributeList::AT_vecreturn: handleVecReturnAttr (S, D, Attr); break; - - case AttributeList::AT_objc_ownership: + case AttributeList::AT_Cold: handleColdAttr (S, D, Attr); break; + case AttributeList::AT_Hot: handleHotAttr (S, D, Attr); break; + case AttributeList::AT_Naked: handleNakedAttr (S, D, Attr); break; + case AttributeList::AT_NoReturn: handleNoReturnAttr (S, D, Attr); break; + case AttributeList::AT_NoThrow: handleNothrowAttr (S, D, Attr); break; + case AttributeList::AT_CUDAShared: handleSharedAttr (S, D, Attr); break; + case AttributeList::AT_VecReturn: handleVecReturnAttr (S, D, Attr); break; + + case AttributeList::AT_ObjCOwnership: handleObjCOwnershipAttr(S, D, Attr); break; - case AttributeList::AT_objc_precise_lifetime: + case AttributeList::AT_ObjCPreciseLifetime: handleObjCPreciseLifetimeAttr(S, D, Attr); break; - case AttributeList::AT_objc_returns_inner_pointer: + case AttributeList::AT_ObjCReturnsInnerPointer: handleObjCReturnsInnerPointerAttr(S, D, Attr); break; - case AttributeList::AT_ns_bridged: + case AttributeList::AT_NSBridged: handleNSBridgedAttr(S, scope, D, Attr); break; - case AttributeList::AT_cf_audited_transfer: - case AttributeList::AT_cf_unknown_transfer: + case AttributeList::AT_CFAuditedTransfer: + case AttributeList::AT_CFUnknownTransfer: handleCFTransferAttr(S, D, Attr); break; // Checker-specific. - case AttributeList::AT_cf_consumed: - case AttributeList::AT_ns_consumed: handleNSConsumedAttr (S, D, Attr); break; - case AttributeList::AT_ns_consumes_self: + case AttributeList::AT_CFConsumed: + case AttributeList::AT_NSConsumed: handleNSConsumedAttr (S, D, Attr); break; + case AttributeList::AT_NSConsumesSelf: handleNSConsumesSelfAttr(S, D, Attr); break; - case AttributeList::AT_ns_returns_autoreleased: - case AttributeList::AT_ns_returns_not_retained: - case AttributeList::AT_cf_returns_not_retained: - case AttributeList::AT_ns_returns_retained: - case AttributeList::AT_cf_returns_retained: + case AttributeList::AT_NSReturnsAutoreleased: + case AttributeList::AT_NSReturnsNotRetained: + case AttributeList::AT_CFReturnsNotRetained: + case AttributeList::AT_NSReturnsRetained: + case AttributeList::AT_CFReturnsRetained: handleNSReturnsRetainedAttr(S, D, Attr); break; - case AttributeList::AT_reqd_work_group_size: - handleReqdWorkGroupSize(S, D, Attr); break; + case AttributeList::AT_WorkGroupSizeHint: + case AttributeList::AT_ReqdWorkGroupSize: + handleWorkGroupSize(S, D, Attr); break; - case AttributeList::AT_init_priority: + case AttributeList::AT_InitPriority: handleInitPriorityAttr(S, D, Attr); break; - case AttributeList::AT_packed: handlePackedAttr (S, D, Attr); break; - case AttributeList::AT_ms_struct: handleMsStructAttr (S, D, Attr); break; - case AttributeList::AT_section: handleSectionAttr (S, D, Attr); break; - case AttributeList::AT_unavailable: handleUnavailableAttr (S, D, Attr); break; - case AttributeList::AT_objc_arc_weak_reference_unavailable: + case AttributeList::AT_Packed: handlePackedAttr (S, D, Attr); break; + case AttributeList::AT_Section: handleSectionAttr (S, D, Attr); break; + case AttributeList::AT_Unavailable: + handleAttrWithMessage<UnavailableAttr>(S, D, Attr, "unavailable"); + break; + case AttributeList::AT_ArcWeakrefUnavailable: handleArcWeakrefUnavailableAttr (S, D, Attr); break; - case AttributeList::AT_objc_root_class: + case AttributeList::AT_ObjCRootClass: handleObjCRootClassAttr(S, D, Attr); break; - case AttributeList::AT_objc_requires_property_definitions: + case AttributeList::AT_ObjCRequiresPropertyDefs: handleObjCRequiresPropertyDefsAttr (S, D, Attr); break; - case AttributeList::AT_unused: handleUnusedAttr (S, D, Attr); break; - case AttributeList::AT_returns_twice: + case AttributeList::AT_Unused: handleUnusedAttr (S, D, Attr); break; + case AttributeList::AT_ReturnsTwice: handleReturnsTwiceAttr(S, D, Attr); break; - case AttributeList::AT_used: handleUsedAttr (S, D, Attr); break; - case AttributeList::AT_visibility: handleVisibilityAttr (S, D, Attr); break; - case AttributeList::AT_warn_unused_result: handleWarnUnusedResult(S, D, Attr); + case AttributeList::AT_Used: handleUsedAttr (S, D, Attr); break; + case AttributeList::AT_Visibility: handleVisibilityAttr (S, D, Attr); break; + case AttributeList::AT_WarnUnusedResult: handleWarnUnusedResult(S, D, Attr); break; - case AttributeList::AT_weak: handleWeakAttr (S, D, Attr); break; - case AttributeList::AT_weakref: handleWeakRefAttr (S, D, Attr); break; - case AttributeList::AT_weak_import: handleWeakImportAttr (S, D, Attr); break; - case AttributeList::AT_transparent_union: + case AttributeList::AT_Weak: handleWeakAttr (S, D, Attr); break; + case AttributeList::AT_WeakRef: handleWeakRefAttr (S, D, Attr); break; + case AttributeList::AT_WeakImport: handleWeakImportAttr (S, D, Attr); break; + case AttributeList::AT_TransparentUnion: handleTransparentUnionAttr(S, D, Attr); break; - case AttributeList::AT_objc_exception: + case AttributeList::AT_ObjCException: handleObjCExceptionAttr(S, D, Attr); break; - case AttributeList::AT_objc_method_family: + case AttributeList::AT_ObjCMethodFamily: handleObjCMethodFamilyAttr(S, D, Attr); break; - case AttributeList::AT_NSObject: handleObjCNSObject (S, D, Attr); break; - case AttributeList::AT_blocks: handleBlocksAttr (S, D, Attr); break; - case AttributeList::AT_sentinel: handleSentinelAttr (S, D, Attr); break; - case AttributeList::AT_const: handleConstAttr (S, D, Attr); break; - case AttributeList::AT_pure: handlePureAttr (S, D, Attr); break; - case AttributeList::AT_cleanup: handleCleanupAttr (S, D, Attr); break; - case AttributeList::AT_nodebug: handleNoDebugAttr (S, D, Attr); break; - case AttributeList::AT_noinline: handleNoInlineAttr (S, D, Attr); break; - case AttributeList::AT_regparm: handleRegparmAttr (S, D, Attr); break; + case AttributeList::AT_ObjCNSObject:handleObjCNSObject (S, D, Attr); break; + case AttributeList::AT_Blocks: handleBlocksAttr (S, D, Attr); break; + case AttributeList::AT_Sentinel: handleSentinelAttr (S, D, Attr); break; + case AttributeList::AT_Const: handleConstAttr (S, D, Attr); break; + case AttributeList::AT_Pure: handlePureAttr (S, D, Attr); break; + case AttributeList::AT_Cleanup: handleCleanupAttr (S, D, Attr); break; + case AttributeList::AT_NoDebug: handleNoDebugAttr (S, D, Attr); break; + case AttributeList::AT_NoInline: handleNoInlineAttr (S, D, Attr); break; + case AttributeList::AT_Regparm: handleRegparmAttr (S, D, Attr); break; case AttributeList::IgnoredAttribute: // Just ignore break; - case AttributeList::AT_no_instrument_function: // Interacts with -pg. + case AttributeList::AT_NoInstrumentFunction: // Interacts with -pg. handleNoInstrumentFunctionAttr(S, D, Attr); break; - case AttributeList::AT_stdcall: - case AttributeList::AT_cdecl: - case AttributeList::AT_fastcall: - case AttributeList::AT_thiscall: - case AttributeList::AT_pascal: - case AttributeList::AT_pcs: + case AttributeList::AT_StdCall: + case AttributeList::AT_CDecl: + case AttributeList::AT_FastCall: + case AttributeList::AT_ThisCall: + case AttributeList::AT_Pascal: + case AttributeList::AT_Pcs: handleCallConvAttr(S, D, Attr); break; - case AttributeList::AT_opencl_kernel_function: + case AttributeList::AT_OpenCLKernel: handleOpenCLKernelAttr(S, D, Attr); break; - case AttributeList::AT_uuid: + + // Microsoft attributes: + case AttributeList::AT_MsStruct: + handleMsStructAttr(S, D, Attr); + break; + case AttributeList::AT_Uuid: handleUuidAttr(S, D, Attr); break; + case AttributeList::AT_SingleInheritance: + case AttributeList::AT_MultipleInheritance: + case AttributeList::AT_VirtualInheritance: + handleInheritanceAttr(S, D, Attr); + break; + case AttributeList::AT_Win64: + case AttributeList::AT_Ptr32: + case AttributeList::AT_Ptr64: + handlePortabilityAttr(S, D, Attr); + break; + case AttributeList::AT_ForceInline: + handleForceInlineAttr(S, D, Attr); + break; // Thread safety attributes: - case AttributeList::AT_guarded_var: + case AttributeList::AT_GuardedVar: handleGuardedVarAttr(S, D, Attr); break; - case AttributeList::AT_pt_guarded_var: - handleGuardedVarAttr(S, D, Attr, /*pointer = */true); + case AttributeList::AT_PtGuardedVar: + handlePtGuardedVarAttr(S, D, Attr); break; - case AttributeList::AT_scoped_lockable: - handleLockableAttr(S, D, Attr, /*scoped = */true); + case AttributeList::AT_ScopedLockable: + handleScopedLockableAttr(S, D, Attr); break; - case AttributeList::AT_no_address_safety_analysis: + case AttributeList::AT_NoAddressSafetyAnalysis: handleNoAddressSafetyAttr(S, D, Attr); break; - case AttributeList::AT_no_thread_safety_analysis: + case AttributeList::AT_NoThreadSafetyAnalysis: handleNoThreadSafetyAttr(S, D, Attr); break; - case AttributeList::AT_lockable: + case AttributeList::AT_Lockable: handleLockableAttr(S, D, Attr); break; - case AttributeList::AT_guarded_by: + case AttributeList::AT_GuardedBy: handleGuardedByAttr(S, D, Attr); break; - case AttributeList::AT_pt_guarded_by: - handleGuardedByAttr(S, D, Attr, /*pointer = */true); + case AttributeList::AT_PtGuardedBy: + handlePtGuardedByAttr(S, D, Attr); break; - case AttributeList::AT_exclusive_lock_function: - handleLockFunAttr(S, D, Attr, /*exclusive = */true); + case AttributeList::AT_ExclusiveLockFunction: + handleExclusiveLockFunctionAttr(S, D, Attr); break; - case AttributeList::AT_exclusive_locks_required: - handleLocksRequiredAttr(S, D, Attr, /*exclusive = */true); + case AttributeList::AT_ExclusiveLocksRequired: + handleExclusiveLocksRequiredAttr(S, D, Attr); break; - case AttributeList::AT_exclusive_trylock_function: - handleTrylockFunAttr(S, D, Attr, /*exclusive = */true); + case AttributeList::AT_ExclusiveTrylockFunction: + handleExclusiveTrylockFunctionAttr(S, D, Attr); break; - case AttributeList::AT_lock_returned: + case AttributeList::AT_LockReturned: handleLockReturnedAttr(S, D, Attr); break; - case AttributeList::AT_locks_excluded: + case AttributeList::AT_LocksExcluded: handleLocksExcludedAttr(S, D, Attr); break; - case AttributeList::AT_shared_lock_function: - handleLockFunAttr(S, D, Attr); + case AttributeList::AT_SharedLockFunction: + handleSharedLockFunctionAttr(S, D, Attr); break; - case AttributeList::AT_shared_locks_required: - handleLocksRequiredAttr(S, D, Attr); + case AttributeList::AT_SharedLocksRequired: + handleSharedLocksRequiredAttr(S, D, Attr); break; - case AttributeList::AT_shared_trylock_function: - handleTrylockFunAttr(S, D, Attr); + case AttributeList::AT_SharedTrylockFunction: + handleSharedTrylockFunctionAttr(S, D, Attr); break; - case AttributeList::AT_unlock_function: + case AttributeList::AT_UnlockFunction: handleUnlockFunAttr(S, D, Attr); break; - case AttributeList::AT_acquired_before: - handleAcquireOrderAttr(S, D, Attr, /*before = */true); + case AttributeList::AT_AcquiredBefore: + handleAcquiredBeforeAttr(S, D, Attr); break; - case AttributeList::AT_acquired_after: - handleAcquireOrderAttr(S, D, Attr, /*before = */false); + case AttributeList::AT_AcquiredAfter: + handleAcquiredAfterAttr(S, D, Attr); break; default: // Ask target about the attribute. const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema(); if (!TargetAttrs.ProcessDeclAttribute(scope, D, Attr, S)) - S.Diag(Attr.getLoc(), diag::warn_unknown_attribute_ignored) - << Attr.getName(); + S.Diag(Attr.getLoc(), Attr.isDeclspecAttribute() ? + diag::warn_unhandled_ms_attribute_ignored : + diag::warn_unknown_attribute_ignored) << Attr.getName(); break; } } @@ -3805,8 +4354,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, if (Attr.isInvalid()) return; - if (Attr.isDeclspecAttribute() && !isKnownDeclSpecAttr(Attr)) - // FIXME: Try to deal with other __declspec attributes! + // Type attributes are still treated as declaration attributes by + // ParseMicrosoftTypeAttributes and ParseBorlandTypeAttributes. We don't + // want to process them, however, because we will simply warn about ignoring + // them. So instead, we will bail out early. + if (Attr.isMSTypespecAttribute()) return; if (NonInheritable) @@ -3840,7 +4392,7 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, bool Sema::ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl, const AttributeList *AttrList) { for (const AttributeList* l = AttrList; l; l = l->getNext()) { - if (l->getKind() == AttributeList::AT_annotate) { + if (l->getKind() == AttributeList::AT_Annotate) { handleAnnotateAttr(*this, ASDecl, *l); } else { Diag(l->getLoc(), diag::err_only_annotate_after_access_spec); @@ -3880,7 +4432,7 @@ void Sema::checkUnusedDeclAttributes(Declarator &D) { } /// DeclClonePragmaWeak - clone existing decl (maybe definition), -/// #pragma weak needs a non-definition decl and source may not have one +/// \#pragma weak needs a non-definition decl and source may not have one. NamedDecl * Sema::DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II, SourceLocation Loc) { assert(isa<FunctionDecl>(ND) || isa<VarDecl>(ND)); @@ -3930,7 +4482,7 @@ NamedDecl * Sema::DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II, return NewD; } -/// DeclApplyPragmaWeak - A declaration (maybe definition) needs #pragma weak +/// DeclApplyPragmaWeak - A declaration (maybe definition) needs \#pragma weak /// applied to it, possibly with an alias. void Sema::DeclApplyPragmaWeak(Scope *S, NamedDecl *ND, WeakInfo &W) { if (W.getUsed()) return; // only do this once @@ -4018,7 +4570,7 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag, } if (S.getLangOpts().ObjCAutoRefCount) if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(decl)) { - // FIXME. we may want to supress diagnostics for all + // FIXME: we may want to suppress diagnostics for all // kind of forbidden type messages on unavailable functions. if (FD->hasAttr<UnavailableAttr>() && diag.getForbiddenTypeDiagnostic() == @@ -4033,57 +4585,29 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag, diag.Triggered = true; } -// This duplicates a vector push_back but hides the need to know the -// size of the type. -void Sema::DelayedDiagnostics::add(const DelayedDiagnostic &diag) { - assert(StackSize <= StackCapacity); - - // Grow the stack if necessary. - if (StackSize == StackCapacity) { - unsigned newCapacity = 2 * StackCapacity + 2; - char *newBuffer = new char[newCapacity * sizeof(DelayedDiagnostic)]; - const char *oldBuffer = (const char*) Stack; - - if (StackCapacity) - memcpy(newBuffer, oldBuffer, StackCapacity * sizeof(DelayedDiagnostic)); - - delete[] oldBuffer; - Stack = reinterpret_cast<sema::DelayedDiagnostic*>(newBuffer); - StackCapacity = newCapacity; - } - - assert(StackSize < StackCapacity); - new (&Stack[StackSize++]) DelayedDiagnostic(diag); -} - -void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, - Decl *decl) { - DelayedDiagnostics &DD = S.DelayedDiagnostics; - - // Check the invariants. - assert(DD.StackSize >= state.SavedStackSize); - assert(state.SavedStackSize >= DD.ActiveStackBase); - assert(DD.ParsingDepth > 0); - - // Drop the parsing depth. - DD.ParsingDepth--; - - // If there are no active diagnostics, we're done. - if (DD.StackSize == DD.ActiveStackBase) - return; - - // We only want to actually emit delayed diagnostics when we - // successfully parsed a decl. - if (decl) { - // We emit all the active diagnostics, not just those starting - // from the saved state. The idea is this: we get one push for a - // decl spec and another for each declarator; in a decl group like: - // deprecated_typedef foo, *bar, baz(); - // only the declarator pops will be passed decls. This is correct; - // we really do need to consider delayed diagnostics from the decl spec - // for each of the different declarations. - for (unsigned i = DD.ActiveStackBase, e = DD.StackSize; i != e; ++i) { - DelayedDiagnostic &diag = DD.Stack[i]; +void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { + assert(DelayedDiagnostics.getCurrentPool()); + DelayedDiagnosticPool &poppedPool = *DelayedDiagnostics.getCurrentPool(); + DelayedDiagnostics.popWithoutEmitting(state); + + // When delaying diagnostics to run in the context of a parsed + // declaration, we only want to actually emit anything if parsing + // succeeds. + if (!decl) return; + + // We emit all the active diagnostics in this pool or any of its + // parents. In general, we'll get one pool for the decl spec + // and a child pool for each declarator; in a decl group like: + // deprecated_typedef foo, *bar, baz(); + // only the declarator pops will be passed decls. This is correct; + // we really do need to consider delayed diagnostics from the decl spec + // for each of the different declarations. + const DelayedDiagnosticPool *pool = &poppedPool; + do { + for (DelayedDiagnosticPool::pool_iterator + i = pool->pool_begin(), e = pool->pool_end(); i != e; ++i) { + // This const_cast is a bit lame. Really, Triggered should be mutable. + DelayedDiagnostic &diag = const_cast<DelayedDiagnostic&>(*i); if (diag.Triggered) continue; @@ -4091,25 +4615,28 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, case DelayedDiagnostic::Deprecation: // Don't bother giving deprecation diagnostics if the decl is invalid. if (!decl->isInvalidDecl()) - S.HandleDelayedDeprecationCheck(diag, decl); + HandleDelayedDeprecationCheck(diag, decl); break; case DelayedDiagnostic::Access: - S.HandleDelayedAccessCheck(diag, decl); + HandleDelayedAccessCheck(diag, decl); break; case DelayedDiagnostic::ForbiddenType: - handleDelayedForbiddenType(S, diag, decl); + handleDelayedForbiddenType(*this, diag, decl); break; } } - } - - // Destroy all the delayed diagnostics we're about to pop off. - for (unsigned i = state.SavedStackSize, e = DD.StackSize; i != e; ++i) - DD.Stack[i].Destroy(); + } while ((pool = pool->getParent())); +} - DD.StackSize = state.SavedStackSize; +/// Given a set of delayed diagnostics, re-emit them as if they had +/// been delayed in the current context instead of in the given pool. +/// Essentially, this just moves them to the current pool. +void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) { + DelayedDiagnosticPool *curPool = DelayedDiagnostics.getCurrentPool(); + assert(curPool && "re-emitting in undelayed context not supported"); + curPool->steal(pool); } static bool isDeclDeprecated(Decl *D) { @@ -4123,24 +4650,36 @@ static bool isDeclDeprecated(Decl *D) { return false; } +static void +DoEmitDeprecationWarning(Sema &S, const NamedDecl *D, StringRef Message, + SourceLocation Loc, + const ObjCInterfaceDecl *UnknownObjCClass) { + DeclarationName Name = D->getDeclName(); + if (!Message.empty()) { + S.Diag(Loc, diag::warn_deprecated_message) << Name << Message; + S.Diag(D->getLocation(), + isa<ObjCMethodDecl>(D) ? diag::note_method_declared_at + : diag::note_previous_decl) << Name; + } else if (!UnknownObjCClass) { + S.Diag(Loc, diag::warn_deprecated) << D->getDeclName(); + S.Diag(D->getLocation(), + isa<ObjCMethodDecl>(D) ? diag::note_method_declared_at + : diag::note_previous_decl) << Name; + } else { + S.Diag(Loc, diag::warn_deprecated_fwdclass_message) << Name; + S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); + } +} + void Sema::HandleDelayedDeprecationCheck(DelayedDiagnostic &DD, Decl *Ctx) { if (isDeclDeprecated(Ctx)) return; DD.Triggered = true; - if (!DD.getDeprecationMessage().empty()) - Diag(DD.Loc, diag::warn_deprecated_message) - << DD.getDeprecationDecl()->getDeclName() - << DD.getDeprecationMessage(); - else if (DD.getUnknownObjCClass()) { - Diag(DD.Loc, diag::warn_deprecated_fwdclass_message) - << DD.getDeprecationDecl()->getDeclName(); - Diag(DD.getUnknownObjCClass()->getLocation(), diag::note_forward_class); - } - else - Diag(DD.Loc, diag::warn_deprecated) - << DD.getDeprecationDecl()->getDeclName(); + DoEmitDeprecationWarning(*this, DD.getDeprecationDecl(), + DD.getDeprecationMessage(), DD.Loc, + DD.getUnknownObjCClass()); } void Sema::EmitDeprecationWarning(NamedDecl *D, StringRef Message, @@ -4157,15 +4696,5 @@ void Sema::EmitDeprecationWarning(NamedDecl *D, StringRef Message, // Otherwise, don't warn if our current context is deprecated. if (isDeclDeprecated(cast<Decl>(getCurLexicalContext()))) return; - if (!Message.empty()) - Diag(Loc, diag::warn_deprecated_message) << D->getDeclName() - << Message; - else { - if (!UnknownObjCClass) - Diag(Loc, diag::warn_deprecated) << D->getDeclName(); - else { - Diag(Loc, diag::warn_deprecated_fwdclass_message) << D->getDeclName(); - Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); - } - } + DoEmitDeprecationWarning(*this, D, Message, Loc, UnknownObjCClass); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index c861072ad749..1d45a6823da1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -23,6 +23,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclVisitor.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -127,8 +128,8 @@ namespace { void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, CXXMethodDecl *Method) { - // If we have an MSAny or unknown spec already, don't bother. - if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) + // If we have an MSAny spec already, don't bother. + if (!Method || ComputedEST == EST_MSAny) return; const FunctionProtoType *Proto @@ -140,7 +141,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, ExceptionSpecificationType EST = Proto->getExceptionSpecType(); // If this function can throw any exceptions, make a note of that. - if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) { + if (EST == EST_MSAny || EST == EST_None) { ClearExceptions(); ComputedEST = EST; return; @@ -197,7 +198,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, } void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { - if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) + if (!E || ComputedEST == EST_MSAny) return; // FIXME: @@ -667,9 +668,9 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef, SourceLocation ParamLoc = PD->getLocation(); if (!(*i)->isDependentType() && SemaRef.RequireLiteralType(ParamLoc, *i, - SemaRef.PDiag(diag::err_constexpr_non_literal_param) - << ArgIndex+1 << PD->getSourceRange() - << isa<CXXConstructorDecl>(FD))) + diag::err_constexpr_non_literal_param, + ArgIndex+1, PD->getSourceRange(), + isa<CXXConstructorDecl>(FD))) return false; } return true; @@ -725,7 +726,7 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { QualType RT = NewFD->getResultType(); if (!RT->isDependentType() && RequireLiteralType(NewFD->getLocation(), RT, - PDiag(diag::err_constexpr_non_literal_return))) + diag::err_constexpr_non_literal_return)) return false; } @@ -920,7 +921,7 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { unsigned Fields = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++Fields) { - if ((*I)->isAnonymousStructOrUnion()) { + if (I->isAnonymousStructOrUnion()) { AnyAnonStructUnionMembers = true; break; } @@ -1055,8 +1056,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class, // The class-name in a base-specifier shall not be an incompletely // defined class. if (RequireCompleteType(BaseLoc, BaseType, - PDiag(diag::err_incomplete_base_class) - << SpecifierRange)) { + diag::err_incomplete_base_class, SpecifierRange)) { Class->setInvalidDecl(); return 0; } @@ -1119,6 +1119,8 @@ Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange, Virtual, Access, TInfo, EllipsisLoc)) return BaseSpec; + else + Class->setInvalidDecl(); return true; } @@ -1403,32 +1405,50 @@ bool Sema::ActOnAccessSpecifier(AccessSpecifier Access, return ProcessAccessDeclAttributeList(ASDecl, Attrs); } -/// CheckOverrideControl - Check C++0x override control semantics. -void Sema::CheckOverrideControl(const Decl *D) { +/// CheckOverrideControl - Check C++11 override control semantics. +void Sema::CheckOverrideControl(Decl *D) { const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D); - if (!MD || !MD->isVirtual()) + + // Do we know which functions this declaration might be overriding? + bool OverridesAreKnown = !MD || + (!MD->getParent()->hasAnyDependentBases() && + !MD->getType()->isDependentType()); + + if (!MD || !MD->isVirtual()) { + if (OverridesAreKnown) { + if (OverrideAttr *OA = D->getAttr<OverrideAttr>()) { + Diag(OA->getLocation(), + diag::override_keyword_only_allowed_on_virtual_member_functions) + << "override" << FixItHint::CreateRemoval(OA->getLocation()); + D->dropAttr<OverrideAttr>(); + } + if (FinalAttr *FA = D->getAttr<FinalAttr>()) { + Diag(FA->getLocation(), + diag::override_keyword_only_allowed_on_virtual_member_functions) + << "final" << FixItHint::CreateRemoval(FA->getLocation()); + D->dropAttr<FinalAttr>(); + } + } return; + } - if (MD->isDependentContext()) + if (!OverridesAreKnown) return; - // C++0x [class.virtual]p3: - // If a virtual function is marked with the virt-specifier override and does - // not override a member function of a base class, - // the program is ill-formed. - bool HasOverriddenMethods = + // C++11 [class.virtual]p5: + // If a virtual function is marked with the virt-specifier override and + // does not override a member function of a base class, the program is + // ill-formed. + bool HasOverriddenMethods = MD->begin_overridden_methods() != MD->end_overridden_methods(); - if (MD->hasAttr<OverrideAttr>() && !HasOverriddenMethods) { - Diag(MD->getLocation(), - diag::err_function_marked_override_not_overriding) + if (MD->hasAttr<OverrideAttr>() && !HasOverriddenMethods) + Diag(MD->getLocation(), diag::err_function_marked_override_not_overriding) << MD->getDeclName(); - return; - } } -/// CheckIfOverriddenFunctionIsMarkedFinal - Checks whether a virtual member +/// CheckIfOverriddenFunctionIsMarkedFinal - Checks whether a virtual member /// function overrides a virtual member function marked 'final', according to -/// C++0x [class.virtual]p3. +/// C++11 [class.virtual]p4. bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New, const CXXMethodDecl *Old) { if (!Old->hasAttr<FinalAttr>()) @@ -1440,16 +1460,26 @@ bool Sema::CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New, return true; } +static bool InitializationHasSideEffects(const FieldDecl &FD) { + const Type *T = FD.getType()->getBaseElementTypeUnsafe(); + // FIXME: Destruction of ObjC lifetime types has side-effects. + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + return !RD->isCompleteDefinition() || + !RD->hasTrivialDefaultConstructor() || + !RD->hasTrivialDestructor(); + return false; +} + /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member /// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the /// bitfield width if there is one, 'InitExpr' specifies the initializer if -/// one has been parsed, and 'HasDeferredInit' is true if an initializer is -/// present but parsing it has been deferred. +/// one has been parsed, and 'InitStyle' is set if an in-class initializer is +/// present (but parsing it has been deferred). Decl * Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, MultiTemplateParamsArg TemplateParameterLists, Expr *BW, const VirtSpecifiers &VS, - bool HasDeferredInit) { + InClassInitStyle InitStyle) { const DeclSpec &DS = D.getDeclSpec(); DeclarationNameInfo NameInfo = GetNameForDeclarator(D); DeclarationName Name = NameInfo.getName(); @@ -1507,12 +1537,12 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, CXXScopeSpec &SS = D.getCXXScopeSpec(); // Data members must have identifiers for names. - if (Name.getNameKind() != DeclarationName::Identifier) { + if (!Name.isIdentifier()) { Diag(Loc, diag::err_bad_variable_name) << Name; return 0; } - + IdentifierInfo *II = Name.getAsIdentifierInfo(); // Member field could not be with "template" keyword. @@ -1553,10 +1583,10 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } Member = HandleField(S, cast<CXXRecordDecl>(CurContext), Loc, D, BitWidth, - HasDeferredInit, AS); + InitStyle, AS); assert(Member && "HandleField never returns null"); } else { - assert(!HasDeferredInit); + assert(InitStyle == ICIS_NoInit); Member = HandleDeclarator(S, D, move(TemplateParameterLists)); if (!Member) { @@ -1596,37 +1626,39 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, FunTmpl->getTemplatedDecl()->setAccess(AS); } - if (VS.isOverrideSpecified()) { - CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member); - if (!MD || !MD->isVirtual()) { - Diag(Member->getLocStart(), - diag::override_keyword_only_allowed_on_virtual_member_functions) - << "override" << FixItHint::CreateRemoval(VS.getOverrideLoc()); - } else - MD->addAttr(new (Context) OverrideAttr(VS.getOverrideLoc(), Context)); - } - if (VS.isFinalSpecified()) { - CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member); - if (!MD || !MD->isVirtual()) { - Diag(Member->getLocStart(), - diag::override_keyword_only_allowed_on_virtual_member_functions) - << "final" << FixItHint::CreateRemoval(VS.getFinalLoc()); - } else - MD->addAttr(new (Context) FinalAttr(VS.getFinalLoc(), Context)); - } + if (VS.isOverrideSpecified()) + Member->addAttr(new (Context) OverrideAttr(VS.getOverrideLoc(), Context)); + if (VS.isFinalSpecified()) + Member->addAttr(new (Context) FinalAttr(VS.getFinalLoc(), Context)); if (VS.getLastLocation().isValid()) { // Update the end location of a method that has a virt-specifiers. if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Member)) MD->setRangeEnd(VS.getLastLocation()); } - + CheckOverrideControl(Member); assert((Name || isInstField) && "No identifier for non-field ?"); - if (isInstField) - FieldCollector->Add(cast<FieldDecl>(Member)); + if (isInstField) { + FieldDecl *FD = cast<FieldDecl>(Member); + FieldCollector->Add(FD); + + if (Diags.getDiagnosticLevel(diag::warn_unused_private_field, + FD->getLocation()) + != DiagnosticsEngine::Ignored) { + // Remember all explicit private FieldDecls that have a name, no side + // effects and are not part of a dependent type declaration. + if (!FD->isImplicit() && FD->getDeclName() && + FD->getAccess() == AS_private && + !FD->hasAttr<UnusedAttr>() && + !FD->getParent()->isDependentContext() && + !InitializationHasSideEffects(*FD)) + UnusedPrivateFields.insert(FD); + } + } + return Member; } @@ -1635,9 +1667,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, /// instantiating an in-class initializer in a class template. Such actions /// are deferred until the class is complete. void -Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc, +Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation InitLoc, Expr *InitExpr) { FieldDecl *FD = cast<FieldDecl>(D); + assert(FD->getInClassInitStyle() != ICIS_NoInit && + "must set init style when field is created"); if (!InitExpr) { FD->setInvalidDecl(); @@ -1660,9 +1694,9 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc, Expr **Inits = &InitExpr; unsigned NumInits = 1; InitializedEntity Entity = InitializedEntity::InitializeMember(FD); - InitializationKind Kind = EqualLoc.isInvalid() + InitializationKind Kind = FD->getInClassInitStyle() == ICIS_ListInit ? InitializationKind::CreateDirectList(InitExpr->getLocStart()) - : InitializationKind::CreateCopy(InitExpr->getLocStart(), EqualLoc); + : InitializationKind::CreateCopy(InitExpr->getLocStart(), InitLoc); InitializationSequence Seq(*this, Entity, Kind, Inits, NumInits); Init = Seq.Perform(*this, Entity, Kind, MultiExprArg(Inits, NumInits)); if (Init.isInvalid()) { @@ -1670,7 +1704,7 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc, return; } - CheckImplicitConversions(Init.get(), EqualLoc); + CheckImplicitConversions(Init.get(), InitLoc); } // C++0x [class.base.init]p7: @@ -2010,73 +2044,95 @@ static void CheckForDanglingReferenceOrPointer(Sema &S, ValueDecl *Member, << (unsigned)IsPointer; } -/// Checks an initializer expression for use of uninitialized fields, such as -/// containing the field that is being initialized. Returns true if there is an -/// uninitialized field was used an updates the SourceLocation parameter; false -/// otherwise. -static bool InitExprContainsUninitializedFields(const Stmt *S, - const ValueDecl *LhsField, - SourceLocation *L) { - assert(isa<FieldDecl>(LhsField) || isa<IndirectFieldDecl>(LhsField)); - - if (isa<CallExpr>(S)) { - // Do not descend into function calls or constructors, as the use - // of an uninitialized field may be valid. One would have to inspect - // the contents of the function/ctor to determine if it is safe or not. - // i.e. Pass-by-value is never safe, but pass-by-reference and pointers - // may be safe, depending on what the function/ctor does. - return false; - } - if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { - const NamedDecl *RhsField = ME->getMemberDecl(); - - if (const VarDecl *VD = dyn_cast<VarDecl>(RhsField)) { - // The member expression points to a static data member. - assert(VD->isStaticDataMember() && - "Member points to non-static data member!"); - (void)VD; - return false; +namespace { + class UninitializedFieldVisitor + : public EvaluatedExprVisitor<UninitializedFieldVisitor> { + Sema &S; + ValueDecl *VD; + public: + typedef EvaluatedExprVisitor<UninitializedFieldVisitor> Inherited; + UninitializedFieldVisitor(Sema &S, ValueDecl *VD) : Inherited(S.Context), + S(S), VD(VD) { } - - if (isa<EnumConstantDecl>(RhsField)) { - // The member expression points to an enum. - return false; + + void HandleExpr(Expr *E) { + if (!E) return; + + // Expressions like x(x) sometimes lack the surrounding expressions + // but need to be checked anyways. + HandleValue(E); + Visit(E); } - if (RhsField == LhsField) { - // Initializing a field with itself. Throw a warning. - // But wait; there are exceptions! - // Exception #1: The field may not belong to this record. - // e.g. Foo(const Foo& rhs) : A(rhs.A) {} - const Expr *base = ME->getBase(); - if (base != NULL && !isa<CXXThisExpr>(base->IgnoreParenCasts())) { - // Even though the field matches, it does not belong to this record. - return false; + void HandleValue(Expr *E) { + E = E->IgnoreParens(); + + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) { + if (isa<EnumConstantDecl>(ME->getMemberDecl())) + return; + Expr *Base = E; + while (isa<MemberExpr>(Base)) { + ME = dyn_cast<MemberExpr>(Base); + if (VarDecl *VarD = dyn_cast<VarDecl>(ME->getMemberDecl())) + if (VarD->hasGlobalStorage()) + return; + Base = ME->getBase(); + } + + if (VD == ME->getMemberDecl() && isa<CXXThisExpr>(Base)) { + S.Diag(ME->getExprLoc(), diag::warn_field_is_uninit); + return; + } + } + + if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) { + HandleValue(CO->getTrueExpr()); + HandleValue(CO->getFalseExpr()); + return; + } + + if (BinaryConditionalOperator *BCO = + dyn_cast<BinaryConditionalOperator>(E)) { + HandleValue(BCO->getCommon()); + HandleValue(BCO->getFalseExpr()); + return; + } + + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { + switch (BO->getOpcode()) { + default: + return; + case(BO_PtrMemD): + case(BO_PtrMemI): + HandleValue(BO->getLHS()); + return; + case(BO_Comma): + HandleValue(BO->getRHS()); + return; + } } - // None of the exceptions triggered; return true to indicate an - // uninitialized field was used. - *L = ME->getMemberLoc(); - return true; } - } else if (isa<UnaryExprOrTypeTraitExpr>(S)) { - // sizeof/alignof doesn't reference contents, do not warn. - return false; - } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(S)) { - // address-of doesn't reference contents (the pointer may be dereferenced - // in the same expression but it would be rare; and weird). - if (UOE->getOpcode() == UO_AddrOf) - return false; - } - for (Stmt::const_child_range it = S->children(); it; ++it) { - if (!*it) { - // An expression such as 'member(arg ?: "")' may trigger this. - continue; + + void VisitImplicitCastExpr(ImplicitCastExpr *E) { + if (E->getCastKind() == CK_LValueToRValue) + HandleValue(E->getSubExpr()); + + Inherited::VisitImplicitCastExpr(E); } - if (InitExprContainsUninitializedFields(*it, LhsField, L)) - return true; + + void VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + Expr *Callee = E->getCallee(); + if (isa<MemberExpr>(Callee)) + HandleValue(Callee); + + Inherited::VisitCXXMemberCallExpr(E); + } + }; + static void CheckInitExprContainsUninitializedFields(Sema &S, Expr *E, + ValueDecl *VD) { + UninitializedFieldVisitor(S, VD).HandleExpr(E); } - return false; -} +} // namespace MemInitResult Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init, @@ -2106,18 +2162,17 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init, Args = InitList->getInits(); NumArgs = InitList->getNumInits(); } - for (unsigned i = 0; i < NumArgs; ++i) { - SourceLocation L; - if (InitExprContainsUninitializedFields(Args[i], Member, &L)) { - // FIXME: Return true in the case when other fields are used before being + + if (getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit, IdLoc) + != DiagnosticsEngine::Ignored) + for (unsigned i = 0; i < NumArgs; ++i) + // FIXME: Warn about the case when other fields are used before being // uninitialized. For example, let this field be the i'th field. When // initializing the i'th field, throw a warning if any of the >= i'th // fields are used, as they are not yet initialized. // Right now we are only handling the case where the i'th field uses // itself in its initializer. - Diag(L, diag::warn_field_is_uninit); - } - } + CheckInitExprContainsUninitializedFields(*this, Args[i], Member); SourceRange InitRange = Init->getSourceRange(); @@ -2235,6 +2290,16 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init, if (DelegationInit.isInvalid()) return true; + // If we are in a dependent context, template instantiation will + // perform this type-checking again. Just save the arguments that we + // received in a ParenListExpr. + // FIXME: This isn't quite ideal, since our ASTs don't capture all + // of the information that we have about the base + // initializer. However, deconstructing the ASTs is a dicey process, + // and this approach is far more likely to get the corner cases right. + if (CurContext->isDependentContext()) + DelegationInit = Owned(Init); + return new (Context) CXXCtorInitializer(Context, TInfo, InitRange.getBegin(), DelegationInit.takeAs<Expr>(), InitRange.getEnd()); @@ -2694,7 +2759,7 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, FieldBaseElementType->isObjCRetainableType() && FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None && FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { - // Instant objects: + // ARC: // Default-initialize Objective-C pointers to NULL. CXXMemberInit = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, @@ -2741,6 +2806,16 @@ struct BaseAndFieldInfo { llvm_unreachable("Invalid ImplicitInitializerKind!"); } + + bool addFieldInitializer(CXXCtorInitializer *Init) { + AllToInit.push_back(Init); + + // Check whether this initializer makes the field "used". + if (Init->getInit() && Init->getInit()->HasSideEffects(S.Context)) + S.UnusedPrivateFields.remove(Init->getAnyMember()); + + return false; + } }; } @@ -2778,12 +2853,10 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, IndirectFieldDecl *Indirect = 0) { // Overwhelmingly common case: we have a direct initializer for this field. - if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field)) { - Info.AllToInit.push_back(Init); - return false; - } + if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field)) + return Info.addFieldInitializer(Init); - // C++0x [class.base.init]p8: if the entity is a non-static data member that + // C++11 [class.base.init]p8: if the entity is a non-static data member that // has a brace-or-equal-initializer, the entity is initialized as specified // in [dcl.init]. if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) { @@ -2798,8 +2871,7 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, SourceLocation(), SourceLocation(), 0, SourceLocation()); - Info.AllToInit.push_back(Init); - return false; + return Info.addFieldInitializer(Init); } // Don't build an implicit initializer for union members if none was @@ -2823,10 +2895,10 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, Indirect, Init)) return true; - if (Init) - Info.AllToInit.push_back(Init); + if (!Init) + return false; - return false; + return Info.addFieldInitializer(Init); } bool @@ -3397,19 +3469,33 @@ void Sema::ActOnDefaultCtorInitializers(Decl *CDtorDecl) { bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, unsigned DiagID, AbstractDiagSelID SelID) { - if (SelID == -1) - return RequireNonAbstractType(Loc, T, PDiag(DiagID)); - else - return RequireNonAbstractType(Loc, T, PDiag(DiagID) << SelID); + class NonAbstractTypeDiagnoser : public TypeDiagnoser { + unsigned DiagID; + AbstractDiagSelID SelID; + + public: + NonAbstractTypeDiagnoser(unsigned DiagID, AbstractDiagSelID SelID) + : TypeDiagnoser(DiagID == 0), DiagID(DiagID), SelID(SelID) { } + + virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) { + if (Suppressed) return; + if (SelID == -1) + S.Diag(Loc, DiagID) << T; + else + S.Diag(Loc, DiagID) << SelID << T; + } + } Diagnoser(DiagID, SelID); + + return RequireNonAbstractType(Loc, T, Diagnoser); } bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, - const PartialDiagnostic &PD) { + TypeDiagnoser &Diagnoser) { if (!getLangOpts().CPlusPlus) return false; if (const ArrayType *AT = Context.getAsArrayType(T)) - return RequireNonAbstractType(Loc, AT->getElementType(), PD); + return RequireNonAbstractType(Loc, AT->getElementType(), Diagnoser); if (const PointerType *PT = T->getAs<PointerType>()) { // Find the innermost pointer type. @@ -3417,7 +3503,7 @@ bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, PT = T; if (const ArrayType *AT = Context.getAsArrayType(PT->getPointeeType())) - return RequireNonAbstractType(Loc, AT->getElementType(), PD); + return RequireNonAbstractType(Loc, AT->getElementType(), Diagnoser); } const RecordType *RT = T->getAs<RecordType>(); @@ -3436,7 +3522,7 @@ bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, if (!RD->isAbstract()) return false; - Diag(Loc, PD) << RD->getDeclName(); + Diagnoser.diagnose(*this, Loc, T); DiagnoseAbstractType(RD); return true; @@ -3732,7 +3818,7 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { for (CXXRecordDecl::method_iterator M = Record->method_begin(), MEnd = Record->method_end(); M != MEnd; ++M) { - if (!(*M)->isStatic()) + if (!M->isStatic()) DiagnoseHiddenVirtualMethods(Record, *M); } } @@ -3762,8 +3848,8 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { case TSK_Undeclared: case TSK_ExplicitSpecialization: - RequireLiteralType((*M)->getLocation(), Context.getRecordType(Record), - PDiag(diag::err_constexpr_method_non_literal)); + RequireLiteralType(M->getLocation(), Context.getRecordType(Record), + diag::err_constexpr_method_non_literal); break; } @@ -3781,558 +3867,354 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { // instantiated (e.g. meta-functions). This doesn't apply to classes that // have inherited constructors. DeclareInheritedConstructors(Record); - - if (!Record->isDependentType()) - CheckExplicitlyDefaultedMethods(Record); } void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) { for (CXXRecordDecl::method_iterator MI = Record->method_begin(), ME = Record->method_end(); - MI != ME; ++MI) { - if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted()) { - switch (getSpecialMember(*MI)) { - case CXXDefaultConstructor: - CheckExplicitlyDefaultedDefaultConstructor( - cast<CXXConstructorDecl>(*MI)); - break; - - case CXXDestructor: - CheckExplicitlyDefaultedDestructor(cast<CXXDestructorDecl>(*MI)); - break; - - case CXXCopyConstructor: - CheckExplicitlyDefaultedCopyConstructor(cast<CXXConstructorDecl>(*MI)); - break; - - case CXXCopyAssignment: - CheckExplicitlyDefaultedCopyAssignment(*MI); - break; - - case CXXMoveConstructor: - CheckExplicitlyDefaultedMoveConstructor(cast<CXXConstructorDecl>(*MI)); - break; - - case CXXMoveAssignment: - CheckExplicitlyDefaultedMoveAssignment(*MI); - break; - - case CXXInvalid: - llvm_unreachable("non-special member explicitly defaulted!"); - } - } - } - + MI != ME; ++MI) + if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted()) + CheckExplicitlyDefaultedSpecialMember(*MI); +} + +/// Is the special member function which would be selected to perform the +/// specified operation on the specified class type a constexpr constructor? +static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, + Sema::CXXSpecialMember CSM, + bool ConstArg) { + Sema::SpecialMemberOverloadResult *SMOR = + S.LookupSpecialMember(ClassDecl, CSM, ConstArg, + false, false, false, false); + if (!SMOR || !SMOR->getMethod()) + // A constructor we wouldn't select can't be "involved in initializing" + // anything. + return true; + return SMOR->getMethod()->isConstexpr(); } -void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { - assert(CD->isExplicitlyDefaulted() && CD->isDefaultConstructor()); - - // Whether this was the first-declared instance of the constructor. - // This affects whether we implicitly add an exception spec (and, eventually, - // constexpr). It is also ill-formed to explicitly default a constructor such - // that it would be deleted. (C++0x [decl.fct.def.default]) - bool First = CD == CD->getCanonicalDecl(); +/// Determine whether the specified special member function would be constexpr +/// if it were implicitly defined. +static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, + Sema::CXXSpecialMember CSM, + bool ConstArg) { + if (!S.getLangOpts().CPlusPlus0x) + return false; - bool HadError = false; - if (CD->getNumParams() != 0) { - Diag(CD->getLocation(), diag::err_defaulted_default_ctor_params) - << CD->getSourceRange(); - HadError = true; - } + // C++11 [dcl.constexpr]p4: + // In the definition of a constexpr constructor [...] + switch (CSM) { + case Sema::CXXDefaultConstructor: + // Since default constructor lookup is essentially trivial (and cannot + // involve, for instance, template instantiation), we compute whether a + // defaulted default constructor is constexpr directly within CXXRecordDecl. + // + // This is important for performance; we need to know whether the default + // constructor is constexpr to determine whether the type is a literal type. + return ClassDecl->defaultedDefaultConstructorIsConstexpr(); - ImplicitExceptionSpecification Spec - = ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent()); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - if (EPI.ExceptionSpecType == EST_Delayed) { - // Exception specification depends on some deferred part of the class. We'll - // try again when the class's definition has been fully processed. - return; - } - const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); + case Sema::CXXCopyConstructor: + case Sema::CXXMoveConstructor: + // For copy or move constructors, we need to perform overload resolution. + break; - // C++11 [dcl.fct.def.default]p2: - // An explicitly-defaulted function may be declared constexpr only if it - // would have been implicitly declared as constexpr, - // Do not apply this rule to templates, since core issue 1358 makes such - // functions always instantiate to constexpr functions. - if (CD->isConstexpr() && - CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { - if (!CD->getParent()->defaultedDefaultConstructorIsConstexpr()) { - Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr) - << CXXDefaultConstructor; - HadError = true; - } - } - // and may have an explicit exception-specification only if it is compatible - // with the exception-specification on the implicit declaration. - if (CtorType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXDefaultConstructor, - PDiag(), - ExceptionType, SourceLocation(), - CtorType, CD->getLocation())) { - HadError = true; - } + case Sema::CXXCopyAssignment: + case Sema::CXXMoveAssignment: + case Sema::CXXDestructor: + case Sema::CXXInvalid: + return false; } - // If a function is explicitly defaulted on its first declaration, - if (First) { - // -- it is implicitly considered to be constexpr if the implicit - // definition would be, - CD->setConstexpr(CD->getParent()->defaultedDefaultConstructorIsConstexpr()); + // -- if the class is a non-empty union, or for each non-empty anonymous + // union member of a non-union class, exactly one non-static data member + // shall be initialized; [DR1359] + // + // If we squint, this is guaranteed, since exactly one non-static data member + // will be initialized (if the constructor isn't deleted), we just don't know + // which one. + if (ClassDecl->isUnion()) + return true; - // -- it is implicitly considered to have the same - // exception-specification as if it had been implicitly declared - // - // FIXME: a compatible, but different, explicit exception specification - // will be silently overridden. We should issue a warning if this happens. - EPI.ExtInfo = CtorType->getExtInfo(); + // -- the class shall not have any virtual base classes; + if (ClassDecl->getNumVBases()) + return false; - // Such a function is also trivial if the implicitly-declared function - // would have been. - CD->setTrivial(CD->getParent()->hasTrivialDefaultConstructor()); - } + // -- every constructor involved in initializing [...] base class + // sub-objects shall be a constexpr constructor; + for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), + BEnd = ClassDecl->bases_end(); + B != BEnd; ++B) { + const RecordType *BaseType = B->getType()->getAs<RecordType>(); + if (!BaseType) continue; - if (HadError) { - CD->setInvalidDecl(); - return; + CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl()); + if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, ConstArg)) + return false; } - if (ShouldDeleteSpecialMember(CD, CXXDefaultConstructor)) { - if (First) { - CD->setDeletedAsWritten(); - } else { - Diag(CD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXDefaultConstructor; - CD->setInvalidDecl(); + // -- every constructor involved in initializing non-static data members + // [...] shall be a constexpr constructor; + // -- every non-static data member and base class sub-object shall be + // initialized + for (RecordDecl::field_iterator F = ClassDecl->field_begin(), + FEnd = ClassDecl->field_end(); + F != FEnd; ++F) { + if (F->isInvalidDecl()) + continue; + if (const RecordType *RecordTy = + S.Context.getBaseElementType(F->getType())->getAs<RecordType>()) { + CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl()); + if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM, ConstArg)) + return false; } } -} - -void Sema::CheckExplicitlyDefaultedCopyConstructor(CXXConstructorDecl *CD) { - assert(CD->isExplicitlyDefaulted() && CD->isCopyConstructor()); - - // Whether this was the first-declared instance of the constructor. - bool First = CD == CD->getCanonicalDecl(); - - bool HadError = false; - if (CD->getNumParams() != 1) { - Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_params) - << CD->getSourceRange(); - HadError = true; - } - - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyCtorExceptionSpecAndConst(CD->getParent()); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); - // Check for parameter type matching. - // This is a copy ctor so we know it's a cv-qualified reference to T. - QualType ArgType = CtorType->getArgType(0); - if (ArgType->getPointeeType().isVolatileQualified()) { - Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_volatile_param); - HadError = true; - } - if (ArgType->getPointeeType().isConstQualified() && !Const) { - Diag(CD->getLocation(), diag::err_defaulted_copy_ctor_const_param); - HadError = true; - } + // All OK, it's constexpr! + return true; +} - // C++11 [dcl.fct.def.default]p2: - // An explicitly-defaulted function may be declared constexpr only if it - // would have been implicitly declared as constexpr, - // Do not apply this rule to templates, since core issue 1358 makes such - // functions always instantiate to constexpr functions. - if (CD->isConstexpr() && - CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { - if (!CD->getParent()->defaultedCopyConstructorIsConstexpr()) { - Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr) - << CXXCopyConstructor; - HadError = true; - } - } - // and may have an explicit exception-specification only if it is compatible - // with the exception-specification on the implicit declaration. - if (CtorType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXCopyConstructor, - PDiag(), - ExceptionType, SourceLocation(), - CtorType, CD->getLocation())) { - HadError = true; - } +static Sema::ImplicitExceptionSpecification +computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) { + switch (S.getSpecialMember(MD)) { + case Sema::CXXDefaultConstructor: + return S.ComputeDefaultedDefaultCtorExceptionSpec(Loc, MD); + case Sema::CXXCopyConstructor: + return S.ComputeDefaultedCopyCtorExceptionSpec(MD); + case Sema::CXXCopyAssignment: + return S.ComputeDefaultedCopyAssignmentExceptionSpec(MD); + case Sema::CXXMoveConstructor: + return S.ComputeDefaultedMoveCtorExceptionSpec(MD); + case Sema::CXXMoveAssignment: + return S.ComputeDefaultedMoveAssignmentExceptionSpec(MD); + case Sema::CXXDestructor: + return S.ComputeDefaultedDtorExceptionSpec(MD); + case Sema::CXXInvalid: + break; } + llvm_unreachable("only special members have implicit exception specs"); +} - // If a function is explicitly defaulted on its first declaration, - if (First) { - // -- it is implicitly considered to be constexpr if the implicit - // definition would be, - CD->setConstexpr(CD->getParent()->defaultedCopyConstructorIsConstexpr()); +static void +updateExceptionSpec(Sema &S, FunctionDecl *FD, const FunctionProtoType *FPT, + const Sema::ImplicitExceptionSpecification &ExceptSpec) { + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + ExceptSpec.getEPI(EPI); + const FunctionProtoType *NewFPT = cast<FunctionProtoType>( + S.Context.getFunctionType(FPT->getResultType(), FPT->arg_type_begin(), + FPT->getNumArgs(), EPI)); + FD->setType(QualType(NewFPT, 0)); +} + +void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) { + const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); + if (FPT->getExceptionSpecType() != EST_Unevaluated) + return; - // -- it is implicitly considered to have the same - // exception-specification as if it had been implicitly declared, and - // - // FIXME: a compatible, but different, explicit exception specification - // will be silently overridden. We should issue a warning if this happens. - EPI.ExtInfo = CtorType->getExtInfo(); + // Evaluate the exception specification. + ImplicitExceptionSpecification ExceptSpec = + computeImplicitExceptionSpec(*this, Loc, MD); - // -- [...] it shall have the same parameter type as if it had been - // implicitly declared. - CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // Update the type of the special member to use it. + updateExceptionSpec(*this, MD, FPT, ExceptSpec); - // Such a function is also trivial if the implicitly-declared function - // would have been. - CD->setTrivial(CD->getParent()->hasTrivialCopyConstructor()); - } + // A user-provided destructor can be defined outside the class. When that + // happens, be sure to update the exception specification on both + // declarations. + const FunctionProtoType *CanonicalFPT = + MD->getCanonicalDecl()->getType()->castAs<FunctionProtoType>(); + if (CanonicalFPT->getExceptionSpecType() == EST_Unevaluated) + updateExceptionSpec(*this, MD->getCanonicalDecl(), + CanonicalFPT, ExceptSpec); +} - if (HadError) { - CD->setInvalidDecl(); - return; - } +static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl); +static bool isImplicitCopyAssignmentArgConst(Sema &S, CXXRecordDecl *ClassDecl); - if (ShouldDeleteSpecialMember(CD, CXXCopyConstructor)) { - if (First) { - CD->setDeletedAsWritten(); - } else { - Diag(CD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXCopyConstructor; - CD->setInvalidDecl(); - } - } -} +void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { + CXXRecordDecl *RD = MD->getParent(); + CXXSpecialMember CSM = getSpecialMember(MD); -void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) { - assert(MD->isExplicitlyDefaulted()); + assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid && + "not an explicitly-defaulted special member"); - // Whether this was the first-declared instance of the operator + // Whether this was the first-declared instance of the constructor. + // This affects whether we implicitly add an exception spec and constexpr. bool First = MD == MD->getCanonicalDecl(); bool HadError = false; - if (MD->getNumParams() != 1) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_params) - << MD->getSourceRange(); - HadError = true; - } - QualType ReturnType = - MD->getType()->getAs<FunctionType>()->getResultType(); - if (!ReturnType->isLValueReferenceType() || - !Context.hasSameType( - Context.getCanonicalType(ReturnType->getPointeeType()), - Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_return_type); + // C++11 [dcl.fct.def.default]p1: + // A function that is explicitly defaulted shall + // -- be a special member function (checked elsewhere), + // -- have the same type (except for ref-qualifiers, and except that a + // copy operation can take a non-const reference) as an implicit + // declaration, and + // -- not have default arguments. + unsigned ExpectedParams = 1; + if (CSM == CXXDefaultConstructor || CSM == CXXDestructor) + ExpectedParams = 0; + if (MD->getNumParams() != ExpectedParams) { + // This also checks for default arguments: a copy or move constructor with a + // default argument is classified as a default constructor, and assignment + // operations and destructors can't have default arguments. + Diag(MD->getLocation(), diag::err_defaulted_special_member_params) + << CSM << MD->getSourceRange(); HadError = true; } - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyCtorExceptionSpecAndConst(MD->getParent()); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - const FunctionProtoType *OperType = MD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); + const FunctionProtoType *Type = MD->getType()->getAs<FunctionProtoType>(); - QualType ArgType = OperType->getArgType(0); - if (!ArgType->isLValueReferenceType()) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref); - HadError = true; - } else { - if (ArgType->getPointeeType().isVolatileQualified()) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_volatile_param); + // Compute argument constness, constexpr, and triviality. + bool CanHaveConstParam = false; + bool Trivial; + switch (CSM) { + case CXXDefaultConstructor: + Trivial = RD->hasTrivialDefaultConstructor(); + break; + case CXXCopyConstructor: + CanHaveConstParam = isImplicitCopyCtorArgConst(*this, RD); + Trivial = RD->hasTrivialCopyConstructor(); + break; + case CXXCopyAssignment: + CanHaveConstParam = isImplicitCopyAssignmentArgConst(*this, RD); + Trivial = RD->hasTrivialCopyAssignment(); + break; + case CXXMoveConstructor: + Trivial = RD->hasTrivialMoveConstructor(); + break; + case CXXMoveAssignment: + Trivial = RD->hasTrivialMoveAssignment(); + break; + case CXXDestructor: + Trivial = RD->hasTrivialDestructor(); + break; + case CXXInvalid: + llvm_unreachable("non-special member explicitly defaulted!"); + } + + QualType ReturnType = Context.VoidTy; + if (CSM == CXXCopyAssignment || CSM == CXXMoveAssignment) { + // Check for return type matching. + ReturnType = Type->getResultType(); + QualType ExpectedReturnType = + Context.getLValueReferenceType(Context.getTypeDeclType(RD)); + if (!Context.hasSameType(ReturnType, ExpectedReturnType)) { + Diag(MD->getLocation(), diag::err_defaulted_special_member_return_type) + << (CSM == CXXMoveAssignment) << ExpectedReturnType; HadError = true; } - if (ArgType->getPointeeType().isConstQualified() && !Const) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_const_param); + + // A defaulted special member cannot have cv-qualifiers. + if (Type->getTypeQuals()) { + Diag(MD->getLocation(), diag::err_defaulted_special_member_quals) + << (CSM == CXXMoveAssignment); HadError = true; } } - if (OperType->getTypeQuals()) { - Diag(MD->getLocation(), diag::err_defaulted_copy_assign_quals); - HadError = true; - } - - if (OperType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXCopyAssignment, - PDiag(), - ExceptionType, SourceLocation(), - OperType, MD->getLocation())) { + // Check for parameter type matching. + QualType ArgType = ExpectedParams ? Type->getArgType(0) : QualType(); + bool HasConstParam = false; + if (ExpectedParams && ArgType->isReferenceType()) { + // Argument must be reference to possibly-const T. + QualType ReferentType = ArgType->getPointeeType(); + HasConstParam = ReferentType.isConstQualified(); + + if (ReferentType.isVolatileQualified()) { + Diag(MD->getLocation(), + diag::err_defaulted_special_member_volatile_param) << CSM; HadError = true; } - } - if (First) { - // We set the declaration to have the computed exception spec here. - // We duplicate the one parameter type. - EPI.RefQualifier = OperType->getRefQualifier(); - EPI.ExtInfo = OperType->getExtInfo(); - MD->setType(Context.getFunctionType(ReturnType, &ArgType, 1, EPI)); - // Such a function is also trivial if the implicitly-declared function - // would have been. - MD->setTrivial(MD->getParent()->hasTrivialCopyAssignment()); - } - - if (HadError) { - MD->setInvalidDecl(); - return; - } - - if (ShouldDeleteSpecialMember(MD, CXXCopyAssignment)) { - if (First) { - MD->setDeletedAsWritten(); - } else { - Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXCopyAssignment; - MD->setInvalidDecl(); + if (HasConstParam && !CanHaveConstParam) { + if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) { + Diag(MD->getLocation(), + diag::err_defaulted_special_member_copy_const_param) + << (CSM == CXXCopyAssignment); + // FIXME: Explain why this special member can't be const. + } else { + Diag(MD->getLocation(), + diag::err_defaulted_special_member_move_const_param) + << (CSM == CXXMoveAssignment); + } + HadError = true; } - } -} - -void Sema::CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *CD) { - assert(CD->isExplicitlyDefaulted() && CD->isMoveConstructor()); - // Whether this was the first-declared instance of the constructor. - bool First = CD == CD->getCanonicalDecl(); - - bool HadError = false; - if (CD->getNumParams() != 1) { - Diag(CD->getLocation(), diag::err_defaulted_move_ctor_params) - << CD->getSourceRange(); + // If a function is explicitly defaulted on its first declaration, it shall + // have the same parameter type as if it had been implicitly declared. + // (Presumably this is to prevent it from being trivial?) + if (!HasConstParam && CanHaveConstParam && First) + Diag(MD->getLocation(), + diag::err_defaulted_special_member_copy_non_const_param) + << (CSM == CXXCopyAssignment); + } else if (ExpectedParams) { + // A copy assignment operator can take its argument by value, but a + // defaulted one cannot. + assert(CSM == CXXCopyAssignment && "unexpected non-ref argument"); + Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref); HadError = true; } - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveCtorExceptionSpec(CD->getParent())); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); - - // Check for parameter type matching. - // This is a move ctor so we know it's a cv-qualified rvalue reference to T. - QualType ArgType = CtorType->getArgType(0); - if (ArgType->getPointeeType().isVolatileQualified()) { - Diag(CD->getLocation(), diag::err_defaulted_move_ctor_volatile_param); - HadError = true; - } - if (ArgType->getPointeeType().isConstQualified()) { - Diag(CD->getLocation(), diag::err_defaulted_move_ctor_const_param); - HadError = true; + // Rebuild the type with the implicit exception specification added, if we + // are going to need it. + const FunctionProtoType *ImplicitType = 0; + if (First || Type->hasExceptionSpec()) { + FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo(); + computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI); + ImplicitType = cast<FunctionProtoType>( + Context.getFunctionType(ReturnType, &ArgType, ExpectedParams, EPI)); } // C++11 [dcl.fct.def.default]p2: // An explicitly-defaulted function may be declared constexpr only if it // would have been implicitly declared as constexpr, - // Do not apply this rule to templates, since core issue 1358 makes such - // functions always instantiate to constexpr functions. - if (CD->isConstexpr() && - CD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { - if (!CD->getParent()->defaultedMoveConstructorIsConstexpr()) { - Diag(CD->getLocStart(), diag::err_incorrect_defaulted_constexpr) - << CXXMoveConstructor; - HadError = true; - } + // Do not apply this rule to members of class templates, since core issue 1358 + // makes such functions always instantiate to constexpr functions. For + // non-constructors, this is checked elsewhere. + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, + HasConstParam); + if (isa<CXXConstructorDecl>(MD) && MD->isConstexpr() && !Constexpr && + MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { + Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM; + // FIXME: Explain why the constructor can't be constexpr. + HadError = true; } // and may have an explicit exception-specification only if it is compatible // with the exception-specification on the implicit declaration. - if (CtorType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXMoveConstructor, - PDiag(), - ExceptionType, SourceLocation(), - CtorType, CD->getLocation())) { - HadError = true; - } - } + if (Type->hasExceptionSpec() && + CheckEquivalentExceptionSpec( + PDiag(diag::err_incorrect_defaulted_exception_spec) << CSM, + PDiag(), ImplicitType, SourceLocation(), Type, MD->getLocation())) + HadError = true; // If a function is explicitly defaulted on its first declaration, if (First) { // -- it is implicitly considered to be constexpr if the implicit // definition would be, - CD->setConstexpr(CD->getParent()->defaultedMoveConstructorIsConstexpr()); - - // -- it is implicitly considered to have the same - // exception-specification as if it had been implicitly declared, and - // - // FIXME: a compatible, but different, explicit exception specification - // will be silently overridden. We should issue a warning if this happens. - EPI.ExtInfo = CtorType->getExtInfo(); + MD->setConstexpr(Constexpr); - // -- [...] it shall have the same parameter type as if it had been - // implicitly declared. - CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // -- it is implicitly considered to have the same exception-specification + // as if it had been implicitly declared, + MD->setType(QualType(ImplicitType, 0)); // Such a function is also trivial if the implicitly-declared function // would have been. - CD->setTrivial(CD->getParent()->hasTrivialMoveConstructor()); + MD->setTrivial(Trivial); } - if (HadError) { - CD->setInvalidDecl(); - return; - } - - if (ShouldDeleteSpecialMember(CD, CXXMoveConstructor)) { + if (ShouldDeleteSpecialMember(MD, CSM)) { if (First) { - CD->setDeletedAsWritten(); + MD->setDeletedAsWritten(); } else { - Diag(CD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXMoveConstructor; - CD->setInvalidDecl(); - } - } -} - -void Sema::CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *MD) { - assert(MD->isExplicitlyDefaulted()); - - // Whether this was the first-declared instance of the operator - bool First = MD == MD->getCanonicalDecl(); - - bool HadError = false; - if (MD->getNumParams() != 1) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_params) - << MD->getSourceRange(); - HadError = true; - } - - QualType ReturnType = - MD->getType()->getAs<FunctionType>()->getResultType(); - if (!ReturnType->isLValueReferenceType() || - !Context.hasSameType( - Context.getCanonicalType(ReturnType->getPointeeType()), - Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_return_type); - HadError = true; - } - - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveCtorExceptionSpec(MD->getParent())); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - const FunctionProtoType *OperType = MD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); - - QualType ArgType = OperType->getArgType(0); - if (!ArgType->isRValueReferenceType()) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_not_ref); - HadError = true; - } else { - if (ArgType->getPointeeType().isVolatileQualified()) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_volatile_param); - HadError = true; - } - if (ArgType->getPointeeType().isConstQualified()) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_const_param); + // C++11 [dcl.fct.def.default]p4: + // [For a] user-provided explicitly-defaulted function [...] if such a + // function is implicitly defined as deleted, the program is ill-formed. + Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) << CSM; HadError = true; } } - if (OperType->getTypeQuals()) { - Diag(MD->getLocation(), diag::err_defaulted_move_assign_quals); - HadError = true; - } - - if (OperType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXMoveAssignment, - PDiag(), - ExceptionType, SourceLocation(), - OperType, MD->getLocation())) { - HadError = true; - } - } - if (First) { - // We set the declaration to have the computed exception spec here. - // We duplicate the one parameter type. - EPI.RefQualifier = OperType->getRefQualifier(); - EPI.ExtInfo = OperType->getExtInfo(); - MD->setType(Context.getFunctionType(ReturnType, &ArgType, 1, EPI)); - - // Such a function is also trivial if the implicitly-declared function - // would have been. - MD->setTrivial(MD->getParent()->hasTrivialMoveAssignment()); - } - - if (HadError) { + if (HadError) MD->setInvalidDecl(); - return; - } - - if (ShouldDeleteSpecialMember(MD, CXXMoveAssignment)) { - if (First) { - MD->setDeletedAsWritten(); - } else { - Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXMoveAssignment; - MD->setInvalidDecl(); - } - } -} - -void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) { - assert(DD->isExplicitlyDefaulted()); - - // Whether this was the first-declared instance of the destructor. - bool First = DD == DD->getCanonicalDecl(); - - ImplicitExceptionSpecification Spec - = ComputeDefaultedDtorExceptionSpec(DD->getParent()); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - const FunctionProtoType *DtorType = DD->getType()->getAs<FunctionProtoType>(), - *ExceptionType = Context.getFunctionType( - Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>(); - - if (DtorType->hasExceptionSpec()) { - if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec) - << CXXDestructor, - PDiag(), - ExceptionType, SourceLocation(), - DtorType, DD->getLocation())) { - DD->setInvalidDecl(); - return; - } - } - if (First) { - // We set the declaration to have the computed exception spec here. - // There are no parameters. - EPI.ExtInfo = DtorType->getExtInfo(); - DD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); - - // Such a function is also trivial if the implicitly-declared function - // would have been. - DD->setTrivial(DD->getParent()->hasTrivialDestructor()); - } - - if (ShouldDeleteSpecialMember(DD, CXXDestructor)) { - if (First) { - DD->setDeletedAsWritten(); - } else { - Diag(DD->getLocation(), diag::err_out_of_line_default_deletes) - << CXXDestructor; - DD->setInvalidDecl(); - } - } } namespace { @@ -4385,9 +4267,15 @@ struct SpecialMemberDeletionInfo { bool inUnion() const { return MD->getParent()->isUnion(); } /// Look up the corresponding special member in the given class. - Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class) { + Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class, + unsigned Quals) { unsigned TQ = MD->getTypeQualifiers(); - return S.LookupSpecialMember(Class, CSM, ConstArg, VolatileArg, + // cv-qualifiers on class members don't affect default ctor / dtor calls. + if (CSM == Sema::CXXDefaultConstructor || CSM == Sema::CXXDestructor) + Quals = 0; + return S.LookupSpecialMember(Class, CSM, + ConstArg || (Quals & Qualifiers::Const), + VolatileArg || (Quals & Qualifiers::Volatile), MD->getRefQualifier() == RQ_RValue, TQ & Qualifiers::Const, TQ & Qualifiers::Volatile); @@ -4399,7 +4287,8 @@ struct SpecialMemberDeletionInfo { bool shouldDeleteForField(FieldDecl *FD); bool shouldDeleteForAllConstMembers(); - bool shouldDeleteForClassSubobject(CXXRecordDecl *Class, Subobject Subobj); + bool shouldDeleteForClassSubobject(CXXRecordDecl *Class, Subobject Subobj, + unsigned Quals); bool shouldDeleteForSubobjectCall(Subobject Subobj, Sema::SpecialMemberOverloadResult *SMOR, bool IsDtorCallInCtor); @@ -4480,9 +4369,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( } /// Check whether we should delete a special member function due to having a -/// direct or virtual base class or static data member of class type M. +/// direct or virtual base class or non-static data member of class type M. bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject( - CXXRecordDecl *Class, Subobject Subobj) { + CXXRecordDecl *Class, Subobject Subobj, unsigned Quals) { FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>(); // C++11 [class.ctor]p5: @@ -4501,7 +4390,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject( // that is deleted or inaccessible if (!(CSM == Sema::CXXDefaultConstructor && Field && Field->hasInClassInitializer()) && - shouldDeleteForSubobjectCall(Subobj, lookupIn(Class), false)) + shouldDeleteForSubobjectCall(Subobj, lookupIn(Class, Quals), false)) return true; // C++11 [class.ctor]p5, C++11 [class.copy]p11: @@ -4522,7 +4411,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject( /// having a particular direct or virtual base class. bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) { CXXRecordDecl *BaseClass = Base->getType()->getAsCXXRecordDecl(); - return shouldDeleteForClassSubobject(BaseClass, Base); + return shouldDeleteForClassSubobject(BaseClass, Base, 0); } /// Check whether we should delete a special member function due to the class @@ -4549,7 +4438,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { (!FieldRecord || !FieldRecord->hasUserProvidedDefaultConstructor())) { if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_default_ctor_uninit_field) - << MD->getParent() << FD << FieldType << /*Const*/1; + << MD->getParent() << FD << FD->getType() << /*Const*/1; return true; } @@ -4577,7 +4466,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { // -- a non-static data member of const non-class type (or array thereof) if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_assign_field) - << IsMove << MD->getParent() << FD << FieldType << /*Const*/1; + << IsMove << MD->getParent() << FD << FD->getType() << /*Const*/1; return true; } } @@ -4599,7 +4488,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { CXXRecordDecl *UnionFieldRecord = UnionFieldType->getAsCXXRecordDecl(); if (UnionFieldRecord && - shouldDeleteForClassSubobject(UnionFieldRecord, *UI)) + shouldDeleteForClassSubobject(UnionFieldRecord, *UI, + UnionFieldType.getCVRQualifiers())) return true; } @@ -4618,7 +4508,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { return false; } - if (shouldDeleteForClassSubobject(FieldRecord, FD)) + if (shouldDeleteForClassSubobject(FieldRecord, FD, + FieldType.getCVRQualifiers())) return true; } @@ -4647,7 +4538,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() { /// C++11 [class.copy]p23, and C++11 [class.dtor]p5. bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, bool Diagnose) { - assert(!MD->isInvalidDecl()); + if (MD->isInvalidDecl()) + return false; CXXRecordDecl *RD = MD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) @@ -4803,7 +4695,7 @@ void Sema::DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { if (Diags.getDiagnosticLevel(diag::warn_overloaded_virtual, MD->getLocation()) == DiagnosticsEngine::Ignored) return; - if (MD->getDeclName().getNameKind() != DeclarationName::Identifier) + if (!MD->getDeclName().isIdentifier()) return; CXXBasePaths Paths(/*FindAmbiguities=*/true, // true to look in all bases. @@ -4850,6 +4742,14 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, AdjustDeclIfTemplate(TagDecl); + for (const AttributeList* l = AttrList; l; l = l->getNext()) { + if (l->getKind() != AttributeList::AT_Visibility) + continue; + l->setInvalid(); + Diag(l->getLoc(), diag::warn_attribute_after_definition_ignored) << + l->getName(); + } + ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef( // strict aliasing violation! reinterpret_cast<Decl**>(FieldCollector->getCurFields()), @@ -5572,6 +5472,8 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, } } + ActOnDocumentableDecl(Namespc); + // Although we could have an invalid decl (i.e. the namespace name is a // redefinition), push it as current DeclContext and try to continue parsing. // FIXME: We should be able to push Namespc here, so that the each DeclContext @@ -6734,6 +6636,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, if (!Redeclaration) PushOnScopeChains(NewND, S); + ActOnDocumentableDecl(NewND); return NewND; } @@ -6816,7 +6719,10 @@ namespace { } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc, + CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] @@ -6863,7 +6769,21 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (Expr *E = F->getInClassInitializer()) ExceptSpec.CalledExpr(E); else if (!F->isInvalidDecl()) - ExceptSpec.SetDelayed(); + // DR1351: + // If the brace-or-equal-initializer of a non-static data member + // invokes a defaulted default constructor of its class or of an + // enclosing class in a potentially evaluated subexpression, the + // program is ill-formed. + // + // This resolution is unworkable: the exception specification of the + // default constructor can be needed in an unevaluated context, in + // particular, in the operand of a noexcept-expression, and we can be + // unable to compute an exception specification for an enclosed class. + // + // We do not allow an in-class initializer to require the evaluation + // of the exception specification for any in-class initializer whose + // definition is not lexically complete. + Diag(Loc, diag::err_in_class_initializer_references_def_ctor) << MD; } else if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs<RecordType>()) { CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl()); @@ -6892,9 +6812,9 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( assert(!ClassDecl->hasUserDeclaredConstructor() && "Should not build implicit default constructor!"); - ImplicitExceptionSpecification Spec = - ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, + CXXDefaultConstructor, + false); // Create the actual constructor declaration. CanQualType ClassType @@ -6904,16 +6824,20 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( = Context.DeclarationNames.getCXXConstructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, 0, 0, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, /*Type*/QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, - /*isConstexpr=*/ClassDecl->defaultedDefaultConstructorIsConstexpr() && - getLangOpts().CPlusPlus0x); + Constexpr); DefaultCon->setAccess(AS_public); DefaultCon->setDefaulted(); DefaultCon->setImplicit(); DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor()); - + + // Build an exception specification pointing back at this constructor. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = DefaultCon; + DefaultCon->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); + // Note that we have declared this constructor. ++ASTContext::NumImplicitDefaultConstructorsDeclared; @@ -6948,7 +6872,7 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, } SourceLocation Loc = Constructor->getLocation(); - Constructor->setBody(new (Context) CompoundStmt(Context, 0, 0, Loc, Loc)); + Constructor->setBody(new (Context) CompoundStmt(Loc)); Constructor->setUsed(); MarkVTableUsed(CurrentLocation, ClassDecl); @@ -6958,58 +6882,14 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, } } -/// Get any existing defaulted default constructor for the given class. Do not -/// implicitly define one if it does not exist. -static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self, - CXXRecordDecl *D) { - ASTContext &Context = Self.Context; - QualType ClassType = Context.getTypeDeclType(D); - DeclarationName ConstructorName - = Context.DeclarationNames.getCXXConstructorName( - Context.getCanonicalType(ClassType.getUnqualifiedType())); - - DeclContext::lookup_const_iterator Con, ConEnd; - for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName); - Con != ConEnd; ++Con) { - // A function template cannot be defaulted. - if (isa<FunctionTemplateDecl>(*Con)) - continue; - - CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con); - if (Constructor->isDefaultConstructor()) - return Constructor->isDefaulted() ? Constructor : 0; - } - return 0; -} - void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { if (!D) return; AdjustDeclIfTemplate(D); CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D); - CXXConstructorDecl *CtorDecl - = getDefaultedDefaultConstructorUnsafe(*this, ClassDecl); - - if (!CtorDecl) return; - - // Compute the exception specification for the default constructor. - const FunctionProtoType *CtorTy = - CtorDecl->getType()->castAs<FunctionProtoType>(); - if (CtorTy->getExceptionSpecType() == EST_Delayed) { - // FIXME: Don't do this unless the exception spec is needed. - ImplicitExceptionSpecification Spec = - ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - assert(EPI.ExceptionSpecType != EST_Delayed); - CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); - } - - // If the default constructor is explicitly defaulted, checking the exception - // specification is deferred until now. - if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() && - !ClassDecl->isDependentType()) - CheckExplicitlyDefaultedDefaultConstructor(CtorDecl); + if (!ClassDecl->isDependentType()) + CheckExplicitlyDefaultedMethods(ClassDecl); } void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { @@ -7193,7 +7073,9 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have // an exception-specification. @@ -7240,14 +7122,8 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // If a class has no user-declared destructor, a destructor is // declared implicitly. An implicitly-declared destructor is an // inline public member of its class. - - ImplicitExceptionSpecification Spec = - ComputeDefaultedDtorExceptionSpec(ClassDecl); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); // Create the actual destructor declaration. - QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI); - CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); SourceLocation ClassLoc = ClassDecl->getLocation(); @@ -7255,24 +7131,27 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { = Context.DeclarationNames.getCXXDestructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXDestructorDecl *Destructor - = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0, - /*isInline=*/true, + = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + QualType(), 0, /*isInline=*/true, /*isImplicitlyDeclared=*/true); Destructor->setAccess(AS_public); Destructor->setDefaulted(); Destructor->setImplicit(); Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); - + + // Build an exception specification pointing back at this destructor. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = Destructor; + Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); + // Note that we have declared this destructor. ++ASTContext::NumImplicitDestructorsDeclared; - + // Introduce this destructor into its scope. if (Scope *S = getScopeForContext(ClassDecl)) PushOnScopeChains(Destructor, S, false); ClassDecl->addDecl(Destructor); - - // This could be uniqued if it ever proves significant. - Destructor->setTypeSourceInfo(Context.getTrivialTypeSourceInfo(Ty)); AddOverriddenMethods(ClassDecl, Destructor); @@ -7309,7 +7188,7 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, } SourceLocation Loc = Destructor->getLocation(); - Destructor->setBody(new (Context) CompoundStmt(Context, 0, 0, Loc, Loc)); + Destructor->setBody(new (Context) CompoundStmt(Loc)); Destructor->setImplicitlyDefined(true); Destructor->setUsed(); MarkVTableUsed(CurrentLocation, ClassDecl); @@ -7322,15 +7201,6 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, /// \brief Perform any semantic analysis which needs to be delayed until all /// pending class member declarations have been parsed. void Sema::ActOnFinishCXXMemberDecls() { - // Now we have parsed all exception specifications, determine the implicit - // exception specifications for destructors. - for (unsigned i = 0, e = DelayedDestructorExceptionSpecs.size(); - i != e; ++i) { - CXXDestructorDecl *Dtor = DelayedDestructorExceptionSpecs[i]; - AdjustDestructorExceptionSpec(Dtor->getParent(), Dtor, true); - } - DelayedDestructorExceptionSpecs.clear(); - // Perform any deferred checking of exception specifications for virtual // destructors. for (unsigned i = 0, e = DelayedDestructorExceptionSpecChecks.size(); @@ -7345,44 +7215,33 @@ void Sema::ActOnFinishCXXMemberDecls() { DelayedDestructorExceptionSpecChecks.clear(); } -void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *classDecl, - CXXDestructorDecl *destructor, - bool WasDelayed) { +void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, + CXXDestructorDecl *Destructor) { + assert(getLangOpts().CPlusPlus0x && + "adjusting dtor exception specs was introduced in c++11"); + // C++11 [class.dtor]p3: // A declaration of a destructor that does not have an exception- // specification is implicitly considered to have the same exception- // specification as an implicit declaration. - const FunctionProtoType *dtorType = destructor->getType()-> + const FunctionProtoType *DtorType = Destructor->getType()-> getAs<FunctionProtoType>(); - if (!WasDelayed && dtorType->hasExceptionSpec()) + if (DtorType->hasExceptionSpec()) return; - ImplicitExceptionSpecification exceptSpec = - ComputeDefaultedDtorExceptionSpec(classDecl); - // Replace the destructor's type, building off the existing one. Fortunately, // the only thing of interest in the destructor type is its extended info. // The return and arguments are fixed. - FunctionProtoType::ExtProtoInfo epi = dtorType->getExtProtoInfo(); - epi.ExceptionSpecType = exceptSpec.getExceptionSpecType(); - epi.NumExceptions = exceptSpec.size(); - epi.Exceptions = exceptSpec.data(); - QualType ty = Context.getFunctionType(Context.VoidTy, 0, 0, epi); - - destructor->setType(ty); - - // If we can't compute the exception specification for this destructor yet - // (because it depends on an exception specification which we have not parsed - // yet), make a note that we need to try again when the class is complete. - if (epi.ExceptionSpecType == EST_Delayed) { - assert(!WasDelayed && "couldn't compute destructor exception spec"); - DelayedDestructorExceptionSpecs.push_back(destructor); - } + FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = Destructor; + Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); // FIXME: If the destructor has a body that could throw, and the newly created // spec doesn't allow exceptions, we should emit a warning, because this // change in behavior can break conforming C++03 programs at runtime. - // However, we don't have a body yet, so it needs to be done somewhere else. + // However, we don't have a body or an exception specification yet, so it + // needs to be done somewhere else. } /// \brief Builds a statement that copies/moves the given entity from \p From to @@ -7584,11 +7443,13 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, Loc, Copy.take()); } -std::pair<Sema::ImplicitExceptionSpecification, bool> -Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( - CXXRecordDecl *ClassDecl) { +/// Determine whether an implicit copy assignment operator for ClassDecl has a +/// const argument. +/// FIXME: It ought to be possible to store this on the record. +static bool isImplicitCopyAssignmentArgConst(Sema &S, + CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(*this), false); + return true; // C++ [class.copy]p10: // If the class definition does not explicitly declare a copy @@ -7599,37 +7460,34 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // X& X::operator=(const X&) // // if - bool HasConstCopyAssignment = true; - // -- each direct base class B of X has a copy assignment operator // whose parameter is of type const B&, const volatile B& or B, // and for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); - HasConstCopyAssignment && Base != BaseEnd; ++Base) { + Base != BaseEnd; ++Base) { // We'll handle this below - if (LangOpts.CPlusPlus0x && Base->isVirtual()) + if (S.getLangOpts().CPlusPlus0x && Base->isVirtual()) continue; assert(!Base->getType()->isDependentType() && "Cannot generate implicit members for class with dependent bases."); CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, - false, 0); + if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0)) + return false; } // In C++11, the above citation has "or virtual" added - if (LangOpts.CPlusPlus0x) { + if (S.getLangOpts().CPlusPlus0x) { for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); - HasConstCopyAssignment && Base != BaseEnd; ++Base) { + Base != BaseEnd; ++Base) { assert(!Base->getType()->isDependentType() && "Cannot generate implicit members for class with dependent bases."); CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, - false, 0); + if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, + false, 0)) + return false; } } @@ -7639,23 +7497,36 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // const volatile M& or M. for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); - HasConstCopyAssignment && Field != FieldEnd; - ++Field) { - QualType FieldType = Context.getBaseElementType((*Field)->getType()); - if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const, - false, 0); - } + Field != FieldEnd; ++Field) { + QualType FieldType = S.Context.getBaseElementType(Field->getType()); + if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) + if (!S.LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const, + false, 0)) + return false; } // Otherwise, the implicitly declared copy assignment operator will // have the form // // X& X::operator=(X&) - + + return true; +} + +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + + ImplicitExceptionSpecification ExceptSpec(*this); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>(); + assert(T->getNumArgs() == 1 && "not a copy assignment op"); + unsigned ArgQuals = T->getArgType(0).getNonReferenceType().getCVRQualifiers(); + // C++ [except.spec]p14: - // An implicitly declared special member function (Clause 12) shall have an + // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] // It is unspecified whether or not an implicit copy assignment operator @@ -7664,8 +7535,6 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // Based on a similar decision made for constness in C++0x, we're erring on // the side of assuming such calls to be made regardless of whether they // actually happen. - ImplicitExceptionSpecification ExceptSpec(*this); - unsigned ArgQuals = HasConstCopyAssignment ? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); Base != BaseEnd; ++Base) { @@ -7693,15 +7562,17 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( FieldEnd = ClassDecl->field_end(); Field != FieldEnd; ++Field) { - QualType FieldType = Context.getBaseElementType((*Field)->getType()); + QualType FieldType = Context.getBaseElementType(Field->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXMethodDecl *CopyAssign = - LookupCopyingAssignment(FieldClassDecl, ArgQuals, false, 0)) + LookupCopyingAssignment(FieldClassDecl, + ArgQuals | FieldType.getCVRQualifiers(), + false, 0)) ExceptSpec.CalledDecl(Field->getLocation(), CopyAssign); } } - return std::make_pair(ExceptSpec, HasConstCopyAssignment); + return ExceptSpec; } CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { @@ -7710,26 +7581,19 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { // for determining the argument type of the operator. Note also that // operators taking an object instead of a reference are allowed. - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyAssignmentExceptionSpecAndConst(ClassDecl); - QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType RetType = Context.getLValueReferenceType(ArgType); - if (Const) + if (isImplicitCopyAssignmentArgConst(*this, ClassDecl)) ArgType = ArgType.withConst(); ArgType = Context.getLValueReferenceType(ArgType); // An implicitly-declared copy assignment operator is an inline public // member of its class. - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXMethodDecl *CopyAssignment - = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(RetType, &ArgType, 1, EPI), + = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isStatic=*/false, /*StorageClassAsWritten=*/SC_None, /*isInline=*/true, /*isConstexpr=*/false, @@ -7738,7 +7602,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { CopyAssignment->setDefaulted(); CopyAssignment->setImplicit(); CopyAssignment->setTrivial(ClassDecl->hasTrivialCopyAssignment()); - + + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = CopyAssignment; + CopyAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI)); + // Add the parameter to the operator. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment, ClassLoc, ClassLoc, /*Id=*/0, @@ -8076,9 +7946,10 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { - ImplicitExceptionSpecification ExceptSpec(*this); +Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -8103,7 +7974,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, - false, 0)) + 0, false, 0)) ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign); } @@ -8113,7 +7984,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, - false, 0)) + 0, false, 0)) ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign); } @@ -8121,10 +7992,12 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { FieldEnd = ClassDecl->field_end(); Field != FieldEnd; ++Field) { - QualType FieldType = Context.getBaseElementType((*Field)->getType()); + QualType FieldType = Context.getBaseElementType(Field->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(FieldClassDecl, - false, 0)) + if (CXXMethodDecl *MoveAssign = + LookupMovingAssignment(FieldClassDecl, + FieldType.getCVRQualifiers(), + false, 0)) ExceptSpec.CalledDecl(Field->getLocation(), MoveAssign); } } @@ -8167,7 +8040,7 @@ hasMoveOrIsTriviallyCopyable(Sema &S, QualType Type, bool IsConstructor) { // reference types, are supposed to return false here, but that appears // to be a standard defect. CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl(); - if (!ClassDecl) + if (!ClassDecl || !ClassDecl->getDefinition()) return true; if (Type.isTriviallyCopyableType(S.Context)) @@ -8209,7 +8082,7 @@ static bool subobjectsHaveMoveOrTrivialCopy(Sema &S, CXXRecordDecl *ClassDecl, for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); Field != FieldEnd; ++Field) { - if (!hasMoveOrIsTriviallyCopyable(S, (*Field)->getType(), IsConstructor)) + if (!hasMoveOrIsTriviallyCopyable(S, Field->getType(), IsConstructor)) return false; } @@ -8244,22 +8117,17 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { // Note: The following rules are largely analoguous to the move // constructor rules. - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveAssignmentExceptionSpec(ClassDecl)); - QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType RetType = Context.getLValueReferenceType(ArgType); ArgType = Context.getRValueReferenceType(ArgType); // An implicitly-declared move assignment operator is an inline public // member of its class. - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXMethodDecl *MoveAssignment - = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(RetType, &ArgType, 1, EPI), + = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isStatic=*/false, /*StorageClassAsWritten=*/SC_None, /*isInline=*/true, @@ -8270,6 +8138,12 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { MoveAssignment->setImplicit(); MoveAssignment->setTrivial(ClassDecl->hasTrivialMoveAssignment()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = MoveAssignment; + MoveAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI)); + // Add the parameter to the operator. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveAssignment, ClassLoc, ClassLoc, /*Id=*/0, @@ -8620,10 +8494,12 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, } } -std::pair<Sema::ImplicitExceptionSpecification, bool> -Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { +/// Determine whether an implicit copy constructor for ClassDecl has a const +/// argument. +/// FIXME: It ought to be possible to store this on the record. +static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(*this), false); + return true; // C++ [class.copy]p5: // The implicitly-declared copy constructor for a class X will @@ -8632,60 +8508,71 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { // X::X(const X&) // // if - // FIXME: It ought to be possible to store this on the record. - bool HasConstCopyConstructor = true; - // -- each direct or virtual base class B of X has a copy // constructor whose first parameter is of type const B& or // const volatile B&, and for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { + Base != BaseEnd; ++Base) { // Virtual bases are handled below. if (Base->isVirtual()) continue; - + CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const); + // FIXME: This lookup is wrong. If the copy ctor for a member or base is + // ambiguous, we should still produce a constructor with a const-qualified + // parameter. + if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const)) + return false; } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { + Base != BaseEnd; ++Base) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const); + if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const)) + return false; } - + // -- for all the nonstatic data members of X that are of a // class type M (or array thereof), each such class type // has a copy constructor whose first parameter is of type // const M& or const volatile M&. for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); - HasConstCopyConstructor && Field != FieldEnd; - ++Field) { - QualType FieldType = Context.getBaseElementType((*Field)->getType()); + Field != FieldEnd; ++Field) { + QualType FieldType = S.Context.getBaseElementType(Field->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const); + if (!S.LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const)) + return false; } } + // Otherwise, the implicitly declared copy constructor will have // the form // // X::X(X&) - + + return true; +} + +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + + ImplicitExceptionSpecification ExceptSpec(*this); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>(); + assert(T->getNumArgs() >= 1 && "not a copy ctor"); + unsigned Quals = T->getArgType(0).getNonReferenceType().getCVRQualifiers(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - ImplicitExceptionSpecification ExceptSpec(*this); - unsigned Quals = HasConstCopyConstructor? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); Base != BaseEnd; @@ -8714,15 +8601,16 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { FieldEnd = ClassDecl->field_end(); Field != FieldEnd; ++Field) { - QualType FieldType = Context.getBaseElementType((*Field)->getType()); + QualType FieldType = Context.getBaseElementType(Field->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXConstructorDecl *CopyConstructor = - LookupCopyingConstructor(FieldClassDecl, Quals)) + LookupCopyingConstructor(FieldClassDecl, + Quals | FieldType.getCVRQualifiers())) ExceptSpec.CalledDecl(Field->getLocation(), CopyConstructor); } } - return std::make_pair(ExceptSpec, HasConstCopyConstructor); + return ExceptSpec; } CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( @@ -8731,18 +8619,16 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // If the class definition does not explicitly declare a copy // constructor, one is declared implicitly. - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyCtorExceptionSpecAndConst(ClassDecl); - QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = ClassType; + bool Const = isImplicitCopyCtorArgConst(*this, ClassDecl); if (Const) ArgType = ArgType.withConst(); ArgType = Context.getLValueReferenceType(ArgType); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, + CXXCopyConstructor, + Const); DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( @@ -8753,15 +8639,20 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // An implicitly-declared copy constructor is an inline public // member of its class. CXXConstructorDecl *CopyConstructor = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, - /*isConstexpr=*/ClassDecl->defaultedCopyConstructorIsConstexpr() && - getLangOpts().CPlusPlus0x); + Constexpr); CopyConstructor->setAccess(AS_public); CopyConstructor->setDefaulted(); CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = CopyConstructor; + CopyConstructor->setType( + Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // Note that we have declared this constructor. ++ASTContext::NumImplicitCopyConstructorsDeclared; @@ -8825,7 +8716,9 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] @@ -8842,7 +8735,8 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *BaseType = B->getType()->getAs<RecordType>()) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl()); - CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl); + CXXConstructorDecl *Constructor = + LookupMovingConstructor(BaseClassDecl, 0); // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) @@ -8856,7 +8750,8 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { B != BEnd; ++B) { if (const RecordType *BaseType = B->getType()->getAs<RecordType>()) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl()); - CXXConstructorDecl *Constructor = LookupMovingConstructor(BaseClassDecl); + CXXConstructorDecl *Constructor = + LookupMovingConstructor(BaseClassDecl, 0); // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) @@ -8868,10 +8763,10 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { for (RecordDecl::field_iterator F = ClassDecl->field_begin(), FEnd = ClassDecl->field_end(); F != FEnd; ++F) { - if (const RecordType *RecordTy - = Context.getBaseElementType(F->getType())->getAs<RecordType>()) { - CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl()); - CXXConstructorDecl *Constructor = LookupMovingConstructor(FieldRecDecl); + QualType FieldType = Context.getBaseElementType(F->getType()); + if (CXXRecordDecl *FieldRecDecl = FieldType->getAsCXXRecordDecl()) { + CXXConstructorDecl *Constructor = + LookupMovingConstructor(FieldRecDecl, FieldType.getCVRQualifiers()); // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. // In particular, the problem is that this function never gets called. It @@ -8906,13 +8801,12 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( return 0; } - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveCtorExceptionSpec(ClassDecl)); - QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = Context.getRValueReferenceType(ClassType); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, + CXXMoveConstructor, + false); DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( @@ -8924,15 +8818,20 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( // An implicitly-declared copy/move constructor is an inline public // member of its class. CXXConstructorDecl *MoveConstructor = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, - /*isConstexpr=*/ClassDecl->defaultedMoveConstructorIsConstexpr() && - getLangOpts().CPlusPlus0x); + Constexpr); MoveConstructor->setAccess(AS_public); MoveConstructor->setDefaulted(); MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = MoveConstructor; + MoveConstructor->setType( + Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // Add the parameter to the constructor. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveConstructor, ClassLoc, ClassLoc, @@ -9046,8 +8945,7 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion( // will fill in the actual details. Invoke->setUsed(); Invoke->setReferenced(); - Invoke->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(), - Conv->getLocation())); + Invoke->setBody(new (Context) CompoundStmt(Conv->getLocation())); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Conv); @@ -9171,13 +9069,6 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, unsigned NumExprs = ExprArgs.size(); Expr **Exprs = (Expr **)ExprArgs.release(); - for (specific_attr_iterator<NonNullAttr> - i = Constructor->specific_attr_begin<NonNullAttr>(), - e = Constructor->specific_attr_end<NonNullAttr>(); i != e; ++i) { - const NonNullAttr *NonNull = *i; - CheckNonNullArguments(NonNull, ExprArgs.get(), ConstructLoc); - } - MarkFunctionReferenced(ConstructLoc, Constructor); return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc, Constructor, Elidable, Exprs, NumExprs, @@ -9243,7 +9134,7 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) { bool Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, - SourceLocation Loc, + SourceLocation Loc, ASTOwningVector<Expr*> &ConvertedArgs, bool AllowExplicit) { // FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall. @@ -9271,7 +9162,8 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor, DiagnoseSentinelCalls(Constructor, Loc, AllArgs.data(), AllArgs.size()); - // FIXME: Missing call to CheckFunctionCall or equivalent + CheckConstructorCall(Constructor, AllArgs.data(), AllArgs.size(), + Proto, Loc); return Invalid; } @@ -9329,7 +9221,7 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl, diag::err_operator_new_delete_too_few_parameters) << FnDecl->getDeclName(); - // Check the the first parameter type is not dependent. + // Check the first parameter type is not dependent. QualType FirstParamType = FnDecl->getParamDecl(0)->getType(); if (FirstParamType->isDependentType()) return SemaRef.Diag(FnDecl->getLocation(), DependentParamTypeDiag) @@ -9568,7 +9460,7 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) { TemplateParameterList *Params = TpDecl->getTemplateParameters(); if (Params->size() == 1) { NonTypeTemplateParmDecl *PmDecl = - cast<NonTypeTemplateParmDecl>(Params->getParam(0)); + dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(0)); // The template parameter must be a char parameter pack. if (PmDecl && PmDecl->isTemplateParameterPack() && @@ -9769,7 +9661,8 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, Diag(Loc, diag::err_objc_object_catch); Invalid = true; } else if (T->isObjCObjectPointerType()) { - if (!getLangOpts().ObjCNonFragileABI) + // FIXME: should this be a test for macosx-fragile specifically? + if (getLangOpts().ObjCRuntime.isFragile()) Diag(Loc, diag::warn_objc_pointer_cxx_catch_fragile); } } @@ -9881,37 +9774,49 @@ Decl *Sema::ActOnExceptionDeclarator(Scope *S, Declarator &D) { Decl *Sema::ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, - Expr *AssertMessageExpr_, + Expr *AssertMessageExpr, SourceLocation RParenLoc) { - StringLiteral *AssertMessage = cast<StringLiteral>(AssertMessageExpr_); + StringLiteral *AssertMessage = cast<StringLiteral>(AssertMessageExpr); + + if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression)) + return 0; - if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent()) { + return BuildStaticAssertDeclaration(StaticAssertLoc, AssertExpr, + AssertMessage, RParenLoc, false); +} + +Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, + Expr *AssertExpr, + StringLiteral *AssertMessage, + SourceLocation RParenLoc, + bool Failed) { + if (!AssertExpr->isTypeDependent() && !AssertExpr->isValueDependent() && + !Failed) { // In a static_assert-declaration, the constant-expression shall be a // constant expression that can be contextually converted to bool. ExprResult Converted = PerformContextuallyConvertToBool(AssertExpr); if (Converted.isInvalid()) - return 0; + Failed = true; llvm::APSInt Cond; - if (VerifyIntegerConstantExpression(Converted.get(), &Cond, - PDiag(diag::err_static_assert_expression_is_not_constant), + if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond, + diag::err_static_assert_expression_is_not_constant, /*AllowFold=*/false).isInvalid()) - return 0; + Failed = true; - if (!Cond) { + if (!Failed && !Cond) { llvm::SmallString<256> MsgBuffer; llvm::raw_svector_ostream Msg(MsgBuffer); AssertMessage->printPretty(Msg, Context, 0, getPrintingPolicy()); Diag(StaticAssertLoc, diag::err_static_assert_failed) << Msg.str() << AssertExpr->getSourceRange(); + Failed = true; } } - if (DiagnoseUnexpandedParameterPack(AssertExpr, UPPC_StaticAssertExpression)) - return 0; - Decl *Decl = StaticAssertDecl::Create(Context, CurContext, StaticAssertLoc, - AssertExpr, AssertMessage, RParenLoc); + AssertExpr, AssertMessage, RParenLoc, + Failed); CurContext->addDecl(Decl); return Decl; @@ -10116,7 +10021,7 @@ Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, /// friend class A<T>::B<unsigned>; /// We permit this as a special case; if there are any template /// parameters present at all, require proper matching, i.e. -/// template <> template <class T> friend class A<int>::B; +/// template <> template \<class T> friend class A<int>::B; Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TempParams) { SourceLocation Loc = DS.getLocStart(); @@ -10438,9 +10343,11 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, FrD->setAccess(AS_public); CurContext->addDecl(FrD); - if (ND->isInvalidDecl()) + if (ND->isInvalidDecl()) { FrD->setInvalidDecl(); - else { + } else { + if (DC->isRecord()) CheckFriendAccess(ND); + FunctionDecl *FD; if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND)) FD = FTD->getTemplatedDecl(); @@ -10464,8 +10371,13 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) { return; } if (const FunctionDecl *Prev = Fn->getPreviousDecl()) { - Diag(DelLoc, diag::err_deleted_decl_not_first); - Diag(Prev->getLocation(), diag::note_previous_declaration); + // Don't consider the implicit declaration we generate for explicit + // specializations. FIXME: Do not generate these implicit declarations. + if ((Prev->getTemplateSpecializationKind() != TSK_ExplicitSpecialization + || Prev->getPreviousDecl()) && !Prev->isDefined()) { + Diag(DelLoc, diag::err_deleted_decl_not_first); + Diag(Prev->getLocation(), diag::note_previous_declaration); + } // If the declaration wasn't the first, we delete the function anyway for // recovery. } @@ -10531,10 +10443,11 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { if (Primary == Primary->getCanonicalDecl()) return; + CheckExplicitlyDefaultedSpecialMember(MD); + switch (Member) { case CXXDefaultConstructor: { CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD); - CheckExplicitlyDefaultedDefaultConstructor(CD); if (!CD->isInvalidDecl()) DefineImplicitDefaultConstructor(DefaultLoc, CD); break; @@ -10542,14 +10455,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { case CXXCopyConstructor: { CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD); - CheckExplicitlyDefaultedCopyConstructor(CD); if (!CD->isInvalidDecl()) DefineImplicitCopyConstructor(DefaultLoc, CD); break; } case CXXCopyAssignment: { - CheckExplicitlyDefaultedCopyAssignment(MD); if (!MD->isInvalidDecl()) DefineImplicitCopyAssignment(DefaultLoc, MD); break; @@ -10557,7 +10468,6 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { case CXXDestructor: { CXXDestructorDecl *DD = cast<CXXDestructorDecl>(MD); - CheckExplicitlyDefaultedDestructor(DD); if (!DD->isInvalidDecl()) DefineImplicitDestructor(DefaultLoc, DD); break; @@ -10565,14 +10475,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { case CXXMoveConstructor: { CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD); - CheckExplicitlyDefaultedMoveConstructor(CD); if (!CD->isInvalidDecl()) DefineImplicitMoveConstructor(DefaultLoc, CD); break; } case CXXMoveAssignment: { - CheckExplicitlyDefaultedMoveAssignment(MD); if (!MD->isInvalidDecl()) DefineImplicitMoveAssignment(DefaultLoc, MD); break; @@ -10650,8 +10558,8 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New, if (const RecordType *RT = NewClassTy->getAs<RecordType>()) { if (!RT->isBeingDefined() && RequireCompleteType(New->getLocation(), NewClassTy, - PDiag(diag::err_covariant_return_incomplete) - << New->getDeclName())) + diag::err_covariant_return_incomplete, + New->getDeclName())) return true; } @@ -10857,7 +10765,7 @@ bool Sema::DefineUsedVTables() { // Note: The VTableUses vector could grow as a result of marking // the members of a class as "used", so we check the size each - // time through the loop and prefer indices (with are stable) to + // time through the loop and prefer indices (which are stable) to // iterators (which are not). bool DefinedAnything = false; for (unsigned I = 0; I != VTableUses.size(); ++I) { @@ -10867,6 +10775,8 @@ bool Sema::DefineUsedVTables() { SourceLocation Loc = VTableUses[I].second; + bool DefineVTable = true; + // If this class has a key function, but that key function is // defined in another translation unit, we don't need to emit the // vtable even though we're using it. @@ -10877,7 +10787,8 @@ bool Sema::DefineUsedVTables() { case TSK_ExplicitSpecialization: case TSK_ExplicitInstantiationDeclaration: // The key function is in another translation unit. - continue; + DefineVTable = false; + break; case TSK_ExplicitInstantiationDefinition: case TSK_ImplicitInstantiation: @@ -10906,7 +10817,15 @@ bool Sema::DefineUsedVTables() { } if (IsExplicitInstantiationDeclaration) - continue; + DefineVTable = false; + } + + // The exception specifications for all virtual members may be needed even + // if we are not providing an authoritative form of the vtable in this TU. + // We may choose to emit it available_externally anyway. + if (!DefineVTable) { + MarkVirtualMemberExceptionSpecsNeeded(Loc, Class); + continue; } // Mark all of the virtual members of this class as referenced, so @@ -10935,16 +10854,33 @@ bool Sema::DefineUsedVTables() { return DefinedAnything; } +void Sema::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc, + const CXXRecordDecl *RD) { + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) + if ((*I)->isVirtual() && !(*I)->isPure()) + ResolveExceptionSpec(Loc, (*I)->getType()->castAs<FunctionProtoType>()); +} + void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, const CXXRecordDecl *RD) { - for (CXXRecordDecl::method_iterator i = RD->method_begin(), - e = RD->method_end(); i != e; ++i) { - CXXMethodDecl *MD = *i; + // Mark all functions which will appear in RD's vtable as used. + CXXFinalOverriderMap FinalOverriders; + RD->getFinalOverriders(FinalOverriders); + for (CXXFinalOverriderMap::const_iterator I = FinalOverriders.begin(), + E = FinalOverriders.end(); + I != E; ++I) { + for (OverridingMethods::const_iterator OI = I->second.begin(), + OE = I->second.end(); + OI != OE; ++OI) { + assert(OI->second.size() > 0 && "no final overrider"); + CXXMethodDecl *Overrider = OI->second.front().Method; - // C++ [basic.def.odr]p2: - // [...] A virtual member function is used if it is not pure. [...] - if (MD->isVirtual() && !MD->isPure()) - MarkFunctionReferenced(Loc, MD); + // C++ [basic.def.odr]p2: + // [...] A virtual member function is used if it is not pure. [...] + if (!Overrider->isPure()) + MarkFunctionReferenced(Loc, Overrider); + } } // Only classes that have virtual bases need a VTT. @@ -11162,8 +11098,8 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) { switch (Proto->getExceptionSpecType()) { case EST_Uninstantiated: + case EST_Unevaluated: case EST_BasicNoexcept: - case EST_Delayed: case EST_DynamicNone: case EST_MSAny: case EST_None: @@ -11290,7 +11226,7 @@ Sema::checkExceptionSpecification(ExceptionSpecificationType EST, if (!NoexceptExpr->isValueDependent()) NoexceptExpr = VerifyIntegerConstantExpression(NoexceptExpr, 0, - PDiag(diag::err_noexcept_needs_constant_expression), + diag::err_noexcept_needs_constant_expression, /*AllowFold*/ false).take(); EPI.NoexceptExpr = NoexceptExpr; } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index a942d4979d73..9da4d69382ef 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -173,10 +173,11 @@ void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, Diag(Overridden->getLocation(), diag::note_previous_decl) << "method"; } - ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin(); + ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin(), + oe = Overridden->param_end(); for (ObjCMethodDecl::param_iterator ni = NewMethod->param_begin(), ne = NewMethod->param_end(); - ni != ne; ++ni, ++oi) { + ni != ne && oi != oe; ++ni, ++oi) { const ParmVarDecl *oldDecl = (*oi); ParmVarDecl *newDecl = (*ni); if (newDecl->hasAttr<NSConsumedAttr>() != @@ -196,7 +197,6 @@ static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) { ObjCMethodFamily family = method->getMethodFamily(); switch (family) { case OMF_None: - case OMF_dealloc: case OMF_finalize: case OMF_retain: case OMF_release: @@ -206,6 +206,24 @@ static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) { case OMF_performSelector: return false; + case OMF_dealloc: + if (!S.Context.hasSameType(method->getResultType(), S.Context.VoidTy)) { + SourceRange ResultTypeRange; + if (const TypeSourceInfo *ResultTypeInfo + = method->getResultTypeSourceInfo()) + ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); + if (ResultTypeRange.isInvalid()) + S.Diag(method->getLocation(), diag::error_dealloc_bad_result_type) + << method->getResultType() + << FixItHint::CreateInsertion(method->getSelectorLoc(0), "(void)"); + else + S.Diag(method->getLocation(), diag::error_dealloc_bad_result_type) + << method->getResultType() + << FixItHint::CreateReplacement(ResultTypeRange, "void"); + return true; + } + return false; + case OMF_init: // If the method doesn't obey the init rules, don't bother annotating it. if (S.checkInitMethod(method, QualType())) @@ -267,9 +285,9 @@ void Sema::AddAnyMethodToGlobalPool(Decl *D) { /// ActOnStartOfObjCMethodDef - This routine sets up parameters; invisible /// and user declared, in the method definition's AST. void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { - assert(getCurMethodDecl() == 0 && "Method parsing confused"); + assert((getCurMethodDecl() == 0) && "Methodparsing confused"); ObjCMethodDecl *MDecl = dyn_cast_or_null<ObjCMethodDecl>(D); - + // If we don't have a valid method decl, simply return. if (!MDecl) return; @@ -338,11 +356,11 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { // Finally, in ActOnFinishFunctionBody() (SemaDecl), warn if flag is set. // Only do this if the current class actually has a superclass. if (IC->getSuperClass()) { - ObjCShouldCallSuperDealloc = + getCurFunction()->ObjCShouldCallSuperDealloc = !(Context.getLangOpts().ObjCAutoRefCount || Context.getLangOpts().getGC() == LangOptions::GCOnly) && MDecl->getMethodFamily() == OMF_dealloc; - ObjCShouldCallSuperFinalize = + getCurFunction()->ObjCShouldCallSuperFinalize = Context.getLangOpts().getGC() != LangOptions::NonGC && MDecl->getMethodFamily() == OMF_finalize; } @@ -474,11 +492,11 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, Diag(SuperLoc, diag::err_undef_superclass) << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); else if (RequireCompleteType(SuperLoc, - Context.getObjCInterfaceType(SuperClassDecl), - PDiag(diag::err_forward_superclass) - << SuperClassDecl->getDeclName() - << ClassName - << SourceRange(AtInterfaceLoc, ClassLoc))) { + Context.getObjCInterfaceType(SuperClassDecl), + diag::err_forward_superclass, + SuperClassDecl->getDeclName(), + ClassName, + SourceRange(AtInterfaceLoc, ClassLoc))) { SuperClassDecl = 0; } } @@ -501,13 +519,13 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, return ActOnObjCContainerStartDefinition(IDecl); } -/// ActOnCompatiblityAlias - this action is called after complete parsing of -/// @compatibility_alias declaration. It sets up the alias relationships. -Decl *Sema::ActOnCompatiblityAlias(SourceLocation AtLoc, - IdentifierInfo *AliasName, - SourceLocation AliasLocation, - IdentifierInfo *ClassName, - SourceLocation ClassLocation) { +/// ActOnCompatibilityAlias - this action is called after complete parsing of +/// a \@compatibility_alias declaration. It sets up the alias relationships. +Decl *Sema::ActOnCompatibilityAlias(SourceLocation AtLoc, + IdentifierInfo *AliasName, + SourceLocation AliasLocation, + IdentifierInfo *ClassName, + SourceLocation ClassLocation) { // Look for previous declaration of alias name NamedDecl *ADecl = LookupSingleName(TUScope, AliasName, AliasLocation, LookupOrdinaryName, ForRedeclaration); @@ -712,7 +730,7 @@ void Sema::DiagnoseClassExtensionDupMethods(ObjCCategoryDecl *CAT, } } -/// ActOnForwardProtocolDeclaration - Handle @protocol foo; +/// ActOnForwardProtocolDeclaration - Handle \@protocol foo; Sema::DeclGroupPtrTy Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, const IdentifierLocPair *IdentList, @@ -759,8 +777,8 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, if (!IDecl || RequireCompleteType(ClassLoc, Context.getObjCInterfaceType(IDecl), - PDiag(diag::err_category_forward_interface) - << (CategoryName == 0))) { + diag::err_category_forward_interface, + CategoryName == 0)) { // Create an invalid ObjCCategoryDecl to serve as context for // the enclosing method declarations. We mark the decl invalid // to make it clear that this isn't a valid AST. @@ -1019,8 +1037,8 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, ObjCInterfaceDecl* IDecl = ImpDecl->getClassInterface(); if (!IDecl) return; - /// Check case of non-existing @interface decl. - /// (legacy objective-c @implementation decl without an @interface decl). + /// Check case of non-existing \@interface decl. + /// (legacy objective-c \@implementation decl without an \@interface decl). /// Add implementations's ivar to the synthesize class's ivar list. if (IDecl->isImplicitInterfaceDecl()) { IDecl->setEndOfDefinitionLoc(RBrace); @@ -1038,7 +1056,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, return; assert(ivars && "missing @implementation ivars"); - if (LangOpts.ObjCNonFragileABI2) { + if (LangOpts.ObjCRuntime.isNonFragile()) { if (ImpDecl->getSuperClass()) Diag(ImpDecl->getLocation(), diag::warn_on_superclass_use); for (unsigned i = 0; i < numIvars; i++) { @@ -1094,7 +1112,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, if (numIvars > 0) Diag(ivars[j]->getLocation(), diag::err_inconsistant_ivar_count); else if (IVI != IVE) - Diag((*IVI)->getLocation(), diag::err_inconsistant_ivar_count); + Diag(IVI->getLocation(), diag::err_inconsistant_ivar_count); } void Sema::WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method, @@ -1399,8 +1417,9 @@ void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl, true); for (ObjCMethodDecl::param_iterator IM = ImpMethodDecl->param_begin(), - IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(); - IM != EM; ++IM, ++IF) { + IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(), + EF = MethodDecl->param_end(); + IM != EM && IF != EF; ++IM, ++IF) { CheckMethodOverrideParam(*this, ImpMethodDecl, MethodDecl, *IM, *IF, IsProtocolMethodDecl, false, true); } @@ -1421,8 +1440,9 @@ void Sema::CheckConflictingOverridingMethod(ObjCMethodDecl *Method, true); for (ObjCMethodDecl::param_iterator IM = Method->param_begin(), - IF = Overridden->param_begin(), EM = Method->param_end(); - IM != EM; ++IM, ++IF) { + IF = Overridden->param_begin(), EM = Method->param_end(), + EF = Overridden->param_end(); + IM != EM && IF != EF; ++IM, ++IF) { CheckMethodOverrideParam(*this, Method, Overridden, *IM, *IF, IsProtocolMethodDecl, true, true); } @@ -1454,8 +1474,9 @@ void Sema::WarnExactTypedMethods(ObjCMethodDecl *ImpMethodDecl, IsProtocolMethodDecl, false, false); if (match) for (ObjCMethodDecl::param_iterator IM = ImpMethodDecl->param_begin(), - IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(); - IM != EM; ++IM, ++IF) { + IF = MethodDecl->param_begin(), EM = ImpMethodDecl->param_end(), + EF = MethodDecl->param_end(); + IM != EM && IF != EF; ++IM, ++IF) { match = CheckMethodOverrideParam(*this, ImpMethodDecl, MethodDecl, *IM, *IF, IsProtocolMethodDecl, false, false); @@ -1487,8 +1508,8 @@ void Sema::WarnExactTypedMethods(ObjCMethodDecl *ImpMethodDecl, void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, ObjCProtocolDecl *PDecl, bool& IncompleteImpl, - const llvm::DenseSet<Selector> &InsMap, - const llvm::DenseSet<Selector> &ClsMap, + const SelectorSet &InsMap, + const SelectorSet &ClsMap, ObjCContainerDecl *CDecl) { ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl); ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() @@ -1497,7 +1518,7 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, ObjCInterfaceDecl *Super = IDecl->getSuperClass(); ObjCInterfaceDecl *NSIDecl = 0; - if (getLangOpts().NeXTRuntime) { + if (getLangOpts().ObjCRuntime.isNeXTFamily()) { // check to see if class implements forwardInvocation method and objects // of this class are derived from 'NSProxy' so that to forward requests // from one object to another. @@ -1584,10 +1605,10 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, /// MatchAllMethodDeclarations - Check methods declared in interface /// or protocol against those declared in their implementations. /// -void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap, - const llvm::DenseSet<Selector> &ClsMap, - llvm::DenseSet<Selector> &InsMapSeen, - llvm::DenseSet<Selector> &ClsMapSeen, +void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, + const SelectorSet &ClsMap, + SelectorSet &InsMapSeen, + SelectorSet &ClsMapSeen, ObjCImplDecl* IMPDecl, ObjCContainerDecl* CDecl, bool &IncompleteImpl, @@ -1683,7 +1704,7 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap, /// warns each time an exact match is found. void Sema::CheckCategoryVsClassMethodMatches( ObjCCategoryImplDecl *CatIMPDecl) { - llvm::DenseSet<Selector> InsMap, ClsMap; + SelectorSet InsMap, ClsMap; for (ObjCImplementationDecl::instmeth_iterator I = CatIMPDecl->instmeth_begin(), @@ -1704,7 +1725,7 @@ void Sema::CheckCategoryVsClassMethodMatches( ObjCInterfaceDecl *IDecl = CatDecl->getClassInterface(); if (!IDecl) return; - llvm::DenseSet<Selector> InsMapSeen, ClsMapSeen; + SelectorSet InsMapSeen, ClsMapSeen; bool IncompleteImpl = false; MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, CatIMPDecl, IDecl, @@ -1715,7 +1736,7 @@ void Sema::CheckCategoryVsClassMethodMatches( void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl* CDecl, bool IncompleteImpl) { - llvm::DenseSet<Selector> InsMap; + SelectorSet InsMap; // Check and see if instance methods in class interface have been // implemented in the implementation class. for (ObjCImplementationDecl::instmeth_iterator @@ -1726,11 +1747,12 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, // an implementation or 2) there is a @synthesize/@dynamic implementation // of the property in the @implementation. if (const ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) - if (!(LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2) || - IDecl->isObjCRequiresPropertyDefs()) + if (!(LangOpts.ObjCDefaultSynthProperties && + LangOpts.ObjCRuntime.isNonFragile()) || + IDecl->isObjCRequiresPropertyDefs()) DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap); - llvm::DenseSet<Selector> ClsMap; + SelectorSet ClsMap; for (ObjCImplementationDecl::classmeth_iterator I = IMPDecl->classmeth_begin(), E = IMPDecl->classmeth_end(); I != E; ++I) @@ -1738,7 +1760,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, // Check for type conflict of methods declared in a class/protocol and // its implementation; if any. - llvm::DenseSet<Selector> InsMapSeen, ClsMapSeen; + SelectorSet InsMapSeen, ClsMapSeen; MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, IMPDecl, CDecl, IncompleteImpl, true); @@ -1954,9 +1976,10 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, return false; ObjCMethodDecl::param_const_iterator - li = left->param_begin(), le = left->param_end(), ri = right->param_begin(); + li = left->param_begin(), le = left->param_end(), ri = right->param_begin(), + re = right->param_end(); - for (; li != le; ++li, ++ri) { + for (; li != le && ri != re; ++li, ++ri) { assert(ri != right->param_end() && "Param mismatch"); const ParmVarDecl *lparm = *li, *rparm = *ri; @@ -2140,53 +2163,16 @@ ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) { return 0; } -/// CompareMethodParamsInBaseAndSuper - This routine compares methods with -/// identical selector names in current and its super classes and issues -/// a warning if any of their argument types are incompatible. -void Sema::CompareMethodParamsInBaseAndSuper(Decl *ClassDecl, - ObjCMethodDecl *Method, - bool IsInstance) { - ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ClassDecl); - if (ID == 0) return; - - while (ObjCInterfaceDecl *SD = ID->getSuperClass()) { - ObjCMethodDecl *SuperMethodDecl = - SD->lookupMethod(Method->getSelector(), IsInstance); - if (SuperMethodDecl == 0) { - ID = SD; - continue; - } - ObjCMethodDecl::param_iterator ParamI = Method->param_begin(), - E = Method->param_end(); - ObjCMethodDecl::param_iterator PrevI = SuperMethodDecl->param_begin(); - for (; ParamI != E; ++ParamI, ++PrevI) { - // Number of parameters are the same and is guaranteed by selector match. - assert(PrevI != SuperMethodDecl->param_end() && "Param mismatch"); - QualType T1 = Context.getCanonicalType((*ParamI)->getType()); - QualType T2 = Context.getCanonicalType((*PrevI)->getType()); - // If type of argument of method in this class does not match its - // respective argument type in the super class method, issue warning; - if (!Context.typesAreCompatible(T1, T2)) { - Diag((*ParamI)->getLocation(), diag::ext_typecheck_base_super) - << T1 << T2; - Diag(SuperMethodDecl->getLocation(), diag::note_previous_declaration); - return; - } - } - ID = SD; - } -} - /// DiagnoseDuplicateIvars - /// Check for duplicate ivars in the entire class at the start of -/// @implementation. This becomes necesssary because class extension can +/// \@implementation. This becomes necesssary because class extension can /// add ivars to a class in random order which will not be known until -/// class's @implementation is seen. +/// class's \@implementation is seen. void Sema::DiagnoseDuplicateIvars(ObjCInterfaceDecl *ID, ObjCInterfaceDecl *SID) { for (ObjCInterfaceDecl::ivar_iterator IVI = ID->ivar_begin(), IVE = ID->ivar_end(); IVI != IVE; ++IVI) { - ObjCIvarDecl* Ivar = (*IVI); + ObjCIvarDecl* Ivar = *IVI; if (Ivar->isInvalidDecl()) continue; if (IdentifierInfo *II = Ivar->getIdentifier()) { @@ -2273,9 +2259,6 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, InsMap[Method->getSelector()] = Method; /// The following allows us to typecheck messages to "id". AddInstanceMethodToGlobalPool(Method); - // verify that the instance method conforms to the same definition of - // parent methods if it shadows one. - CompareMethodParamsInBaseAndSuper(ClassDecl, Method, true); } } else { /// Check for class method of the same name with incompatible types @@ -2298,11 +2281,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, Diag(PrevMethod->getLocation(), diag::note_previous_declaration); } ClsMap[Method->getSelector()] = Method; - /// The following allows us to typecheck messages to "Class". AddFactoryMethodToGlobalPool(Method); - // verify that the class method conforms to the same definition of - // parent methods if it shadows one. - CompareMethodParamsInBaseAndSuper(ClassDecl, Method, false); } } } @@ -2347,7 +2326,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ClsExtDecl; ClsExtDecl = ClsExtDecl->getNextClassExtension()) { for (ObjCContainerDecl::prop_iterator I = ClsExtDecl->prop_begin(), E = ClsExtDecl->prop_end(); I != E; ++I) { - ObjCPropertyDecl *Property = (*I); + ObjCPropertyDecl *Property = *I; // Skip over properties declared @dynamic if (const ObjCPropertyImplDecl *PIDecl = IC->FindPropertyImplDecl(Property->getIdentifier())) @@ -2399,7 +2378,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, Diag(IDecl->getLocation(), diag::err_objc_root_class_subclass); } - if (LangOpts.ObjCNonFragileABI2) { + if (LangOpts.ObjCRuntime.isNonFragile()) { while (IDecl->getSuperClass()) { DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass()); IDecl = IDecl->getSuperClass(); @@ -2443,6 +2422,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, Consumer.HandleTopLevelDeclInObjCContainer(DG); } + ActOnDocumentableDecl(ClassDecl); return ClassDecl; } @@ -2488,19 +2468,10 @@ bool containsInvalidMethodImplAttribute(ObjCMethodDecl *IMD, return false; } -namespace { - /// \brief Describes the compatibility of a result type with its method. - enum ResultTypeCompatibilityKind { - RTC_Compatible, - RTC_Incompatible, - RTC_Unknown - }; -} - /// \brief Check whether the declared result type of the given Objective-C /// method declaration is compatible with the method's class. /// -static ResultTypeCompatibilityKind +static Sema::ResultTypeCompatibilityKind CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method, ObjCInterfaceDecl *CurrentClass) { QualType ResultType = Method->getResultType(); @@ -2513,27 +2484,27 @@ CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method, // - it is id or qualified id, or if (ResultObjectType->isObjCIdType() || ResultObjectType->isObjCQualifiedIdType()) - return RTC_Compatible; + return Sema::RTC_Compatible; if (CurrentClass) { if (ObjCInterfaceDecl *ResultClass = ResultObjectType->getInterfaceDecl()) { // - it is the same as the method's class type, or if (declaresSameEntity(CurrentClass, ResultClass)) - return RTC_Compatible; + return Sema::RTC_Compatible; // - it is a superclass of the method's class type if (ResultClass->isSuperClassOf(CurrentClass)) - return RTC_Compatible; + return Sema::RTC_Compatible; } } else { // Any Objective-C pointer type might be acceptable for a protocol // method; we just don't know. - return RTC_Unknown; + return Sema::RTC_Unknown; } } - return RTC_Incompatible; + return Sema::RTC_Incompatible; } namespace { @@ -2543,7 +2514,6 @@ class OverrideSearch { public: Sema &S; ObjCMethodDecl *Method; - llvm::SmallPtrSet<ObjCContainerDecl*, 128> Searched; llvm::SmallPtrSet<ObjCMethodDecl*, 4> Overridden; bool Recursive; @@ -2572,8 +2542,13 @@ public: // Prevent the search from reaching this container again. This is // important with categories, which override methods from the // interface and each other. - Searched.insert(container); - searchFromContainer(container); + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(container)) { + searchFromContainer(container); + if (ObjCInterfaceDecl *Interface = Category->getClassInterface()) + searchFromContainer(Interface); + } else { + searchFromContainer(container); + } } typedef llvm::SmallPtrSet<ObjCMethodDecl*, 128>::iterator iterator; @@ -2609,7 +2584,7 @@ private: void searchFrom(ObjCCategoryDecl *category) { // A method in a category declaration overrides declarations from // the main class and from protocols the category references. - search(category->getClassInterface()); + // The main class is handled in the constructor. search(category->getReferencedProtocols()); } @@ -2619,10 +2594,12 @@ private: // declaration. if (ObjCCategoryDecl *category = impl->getCategoryDecl()) { search(category); + if (ObjCInterfaceDecl *Interface = category->getClassInterface()) + search(Interface); // Otherwise it overrides declarations from the class. - } else { - search(impl->getClassInterface()); + } else if (ObjCInterfaceDecl *Interface = impl->getClassInterface()) { + search(Interface); } } @@ -2647,7 +2624,8 @@ private: void searchFrom(ObjCImplementationDecl *impl) { // A method in a class implementation overrides declarations from // the class interface. - search(impl->getClassInterface()); + if (ObjCInterfaceDecl *Interface = impl->getClassInterface()) + search(Interface); } @@ -2658,9 +2636,6 @@ private: } void search(ObjCContainerDecl *container) { - // Abort if we've already searched this container. - if (!Searched.insert(container)) return; - // Check for a method in this container which matches this selector. ObjCMethodDecl *meth = container->getMethod(Method->getSelector(), Method->isInstanceMethod()); @@ -2682,6 +2657,68 @@ private: }; } +void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, + ObjCInterfaceDecl *CurrentClass, + ResultTypeCompatibilityKind RTC) { + // Search for overridden methods and merge information down from them. + OverrideSearch overrides(*this, ObjCMethod); + // Keep track if the method overrides any method in the class's base classes, + // its protocols, or its categories' protocols; we will keep that info + // in the ObjCMethodDecl. + // For this info, a method in an implementation is not considered as + // overriding the same method in the interface or its categories. + bool hasOverriddenMethodsInBaseOrProtocol = false; + for (OverrideSearch::iterator + i = overrides.begin(), e = overrides.end(); i != e; ++i) { + ObjCMethodDecl *overridden = *i; + + if (isa<ObjCProtocolDecl>(overridden->getDeclContext()) || + CurrentClass != overridden->getClassInterface() || + overridden->isOverriding()) + hasOverriddenMethodsInBaseOrProtocol = true; + + // Propagate down the 'related result type' bit from overridden methods. + if (RTC != Sema::RTC_Incompatible && overridden->hasRelatedResultType()) + ObjCMethod->SetRelatedResultType(); + + // Then merge the declarations. + mergeObjCMethodDecls(ObjCMethod, overridden); + + if (ObjCMethod->isImplicit() && overridden->isImplicit()) + continue; // Conflicting properties are detected elsewhere. + + // Check for overriding methods + if (isa<ObjCInterfaceDecl>(ObjCMethod->getDeclContext()) || + isa<ObjCImplementationDecl>(ObjCMethod->getDeclContext())) + CheckConflictingOverridingMethod(ObjCMethod, overridden, + isa<ObjCProtocolDecl>(overridden->getDeclContext())); + + if (CurrentClass && overridden->getDeclContext() != CurrentClass && + isa<ObjCInterfaceDecl>(overridden->getDeclContext()) && + !overridden->isImplicit() /* not meant for properties */) { + ObjCMethodDecl::param_iterator ParamI = ObjCMethod->param_begin(), + E = ObjCMethod->param_end(); + ObjCMethodDecl::param_iterator PrevI = overridden->param_begin(), + PrevE = overridden->param_end(); + for (; ParamI != E && PrevI != PrevE; ++ParamI, ++PrevI) { + assert(PrevI != overridden->param_end() && "Param mismatch"); + QualType T1 = Context.getCanonicalType((*ParamI)->getType()); + QualType T2 = Context.getCanonicalType((*PrevI)->getType()); + // If type of argument of method in this class does not match its + // respective argument type in the super class method, issue warning; + if (!Context.typesAreCompatible(T1, T2)) { + Diag((*ParamI)->getLocation(), diag::ext_typecheck_base_super) + << T1 << T2; + Diag(overridden->getLocation(), diag::note_previous_declaration); + break; + } + } + } + } + + ObjCMethod->setOverriding(hasOverriddenMethodsInBaseOrProtocol); +} + Decl *Sema::ActOnMethodDeclaration( Scope *S, SourceLocation MethodLoc, SourceLocation EndLoc, @@ -2871,32 +2908,14 @@ Decl *Sema::ActOnMethodDeclaration( ResultTypeCompatibilityKind RTC = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass); - // Search for overridden methods and merge information down from them. - OverrideSearch overrides(*this, ObjCMethod); - for (OverrideSearch::iterator - i = overrides.begin(), e = overrides.end(); i != e; ++i) { - ObjCMethodDecl *overridden = *i; - - // Propagate down the 'related result type' bit from overridden methods. - if (RTC != RTC_Incompatible && overridden->hasRelatedResultType()) - ObjCMethod->SetRelatedResultType(); + CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC); - // Then merge the declarations. - mergeObjCMethodDecls(ObjCMethod, overridden); - - // Check for overriding methods - if (isa<ObjCInterfaceDecl>(ObjCMethod->getDeclContext()) || - isa<ObjCImplementationDecl>(ObjCMethod->getDeclContext())) - CheckConflictingOverridingMethod(ObjCMethod, overridden, - isa<ObjCProtocolDecl>(overridden->getDeclContext())); - } - bool ARCError = false; if (getLangOpts().ObjCAutoRefCount) ARCError = CheckARCMethodDecl(*this, ObjCMethod); // Infer the related result type when possible. - if (!ARCError && RTC == RTC_Compatible && + if (!ARCError && RTC == Sema::RTC_Compatible && !ObjCMethod->hasRelatedResultType() && LangOpts.ObjCInferRelatedResultType) { bool InferRelatedResultType = false; @@ -2927,7 +2946,9 @@ Decl *Sema::ActOnMethodDeclaration( if (InferRelatedResultType) ObjCMethod->SetRelatedResultType(); } - + + ActOnDocumentableDecl(ObjCMethod); + return ObjCMethod; } @@ -2948,7 +2969,7 @@ bool Sema::CheckObjCDeclScope(Decl *D) { return true; } -/// Called whenever @defs(ClassName) is encountered in the source. Inserts the +/// Called whenever \@defs(ClassName) is encountered in the source. Inserts the /// instance variables of ClassName into Decls. void Sema::ActOnDefs(Scope *S, Decl *TagD, SourceLocation DeclStart, IdentifierInfo *ClassName, @@ -2959,7 +2980,7 @@ void Sema::ActOnDefs(Scope *S, Decl *TagD, SourceLocation DeclStart, Diag(DeclStart, diag::err_undef_interface) << ClassName; return; } - if (LangOpts.ObjCNonFragileABI) { + if (LangOpts.ObjCRuntime.isNonFragile()) { Diag(DeclStart, diag::err_atdef_nonfragile_interface); return; } diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 14b24341d094..63bfa9dc2d9f 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -51,7 +51,8 @@ bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) { // C++ 15.4p2: A type denoted in an exception-specification shall not denote // an incomplete type. if (RequireCompleteType(Range.getBegin(), T, - PDiag(diag::err_incomplete_in_exception_spec) << /*direct*/0 << Range)) + diag::err_incomplete_in_exception_spec, + /*direct*/0, Range)) return true; // C++ 15.4p2: A type denoted in an exception-specification shall not denote @@ -71,8 +72,9 @@ bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) { if (T->isRecordType() && T->getAs<RecordType>()->isBeingDefined()) return false; - if (!T->isVoidType() && RequireCompleteType(Range.getBegin(), T, - PDiag(diag::err_incomplete_in_exception_spec) << kind << Range)) + if (!T->isVoidType() && + RequireCompleteType(Range.getBegin(), T, + diag::err_incomplete_in_exception_spec, kind, Range)) return true; return false; @@ -98,20 +100,22 @@ bool Sema::CheckDistantExceptionSpec(QualType T) { const FunctionProtoType * Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { - // FIXME: If FD is a special member, we should delay computing its exception - // specification until this point. - if (FPT->getExceptionSpecType() != EST_Uninstantiated) + if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) return FPT; FunctionDecl *SourceDecl = FPT->getExceptionSpecDecl(); const FunctionProtoType *SourceFPT = SourceDecl->getType()->castAs<FunctionProtoType>(); - if (SourceFPT->getExceptionSpecType() != EST_Uninstantiated) + // If the exception specification has already been resolved, just return it. + if (!isUnresolvedExceptionSpec(SourceFPT->getExceptionSpecType())) return SourceFPT; - // Instantiate the exception specification now. - InstantiateExceptionSpec(Loc, SourceDecl); + // Compute or instantiate the exception specification now. + if (FPT->getExceptionSpecType() == EST_Unevaluated) + EvaluateImplicitExceptionSpec(Loc, cast<CXXMethodDecl>(SourceDecl)); + else + InstantiateExceptionSpec(Loc, SourceDecl); return SourceDecl->getType()->castAs<FunctionProtoType>(); } @@ -344,8 +348,8 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, ExceptionSpecificationType OldEST = Old->getExceptionSpecType(); ExceptionSpecificationType NewEST = New->getExceptionSpecType(); - assert(OldEST != EST_Delayed && NewEST != EST_Delayed && - OldEST != EST_Uninstantiated && NewEST != EST_Uninstantiated && + assert(!isUnresolvedExceptionSpec(OldEST) && + !isUnresolvedExceptionSpec(NewEST) && "Shouldn't see unknown exception specifications here"); // Shortcut the case where both have no spec. @@ -542,8 +546,8 @@ bool Sema::CheckExceptionSpecSubset( ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); - assert(SuperEST != EST_Delayed && SubEST != EST_Delayed && - SuperEST != EST_Uninstantiated && SubEST != EST_Uninstantiated && + assert(!isUnresolvedExceptionSpec(SuperEST) && + !isUnresolvedExceptionSpec(SubEST) && "Shouldn't see unknown exception specifications here"); // It does not. If the subset contains everything, we've failed. @@ -806,15 +810,6 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, if (!FT) return CT_Can; - if (FT->getExceptionSpecType() == EST_Delayed) { - // FIXME: Try to resolve a delayed exception spec in ResolveExceptionSpec. - assert(isa<CXXConstructorDecl>(D) && - "only constructor exception specs can be unknown"); - S.Diag(E->getLocStart(), diag::err_exception_spec_unknown) - << E->getSourceRange(); - return CT_Can; - } - return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can; } @@ -964,7 +959,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { // possibility. case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: return CT_Can; // Many other things have subexpressions, so we have to test those. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d2e0e6b63b4d..6a503ee2d9b5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -130,6 +130,77 @@ void Sema::NoteDeletedFunction(FunctionDecl *Decl) { << 1 << Decl->isDeleted(); } +/// \brief Determine whether a FunctionDecl was ever declared with an +/// explicit storage class. +static bool hasAnyExplicitStorageClass(const FunctionDecl *D) { + for (FunctionDecl::redecl_iterator I = D->redecls_begin(), + E = D->redecls_end(); + I != E; ++I) { + if (I->getStorageClassAsWritten() != SC_None) + return true; + } + return false; +} + +/// \brief Check whether we're in an extern inline function and referring to a +/// variable or function with internal linkage (C11 6.7.4p3). +/// +/// This is only a warning because we used to silently accept this code, but +/// in many cases it will not behave correctly. This is not enabled in C++ mode +/// because the restriction language is a bit weaker (C++11 [basic.def.odr]p6) +/// and so while there may still be user mistakes, most of the time we can't +/// prove that there are errors. +static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S, + const NamedDecl *D, + SourceLocation Loc) { + // This is disabled under C++; there are too many ways for this to fire in + // contexts where the warning is a false positive, or where it is technically + // correct but benign. + if (S.getLangOpts().CPlusPlus) + return; + + // Check if this is an inlined function or method. + FunctionDecl *Current = S.getCurFunctionDecl(); + if (!Current) + return; + if (!Current->isInlined()) + return; + if (Current->getLinkage() != ExternalLinkage) + return; + + // Check if the decl has internal linkage. + if (D->getLinkage() != InternalLinkage) + return; + + // Downgrade from ExtWarn to Extension if + // (1) the supposedly external inline function is in the main file, + // and probably won't be included anywhere else. + // (2) the thing we're referencing is a pure function. + // (3) the thing we're referencing is another inline function. + // This last can give us false negatives, but it's better than warning on + // wrappers for simple C library functions. + const FunctionDecl *UsedFn = dyn_cast<FunctionDecl>(D); + bool DowngradeWarning = S.getSourceManager().isFromMainFile(Loc); + if (!DowngradeWarning && UsedFn) + DowngradeWarning = UsedFn->isInlined() || UsedFn->hasAttr<ConstAttr>(); + + S.Diag(Loc, DowngradeWarning ? diag::ext_internal_in_extern_inline + : diag::warn_internal_in_extern_inline) + << /*IsVar=*/!UsedFn << D; + + // Suggest "static" on the inline function, if possible. + if (!hasAnyExplicitStorageClass(Current)) { + const FunctionDecl *FirstDecl = Current->getCanonicalDecl(); + SourceLocation DeclBegin = FirstDecl->getSourceRange().getBegin(); + S.Diag(DeclBegin, diag::note_convert_inline_to_static) + << Current << FixItHint::CreateInsertion(DeclBegin, "static "); + } + + S.Diag(D->getCanonicalDecl()->getLocation(), + diag::note_internal_decl_declared_here) + << D; +} + /// \brief Determine whether the use of this declaration is valid, and /// emit any corresponding diagnostics. /// @@ -182,6 +253,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, // Warn if this is used but marked unused. if (D->hasAttr<UnusedAttr>()) Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName(); + + diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); + return false; } @@ -510,8 +584,7 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { // is a prvalue for the temporary. // FIXME: add some way to gate this entire thing for correctness in // potentially potentially evaluated contexts. - if (getLangOpts().CPlusPlus && E->isGLValue() && - ExprEvalContexts.back().Context != Unevaluated) { + if (getLangOpts().CPlusPlus && E->isGLValue() && !isUnevaluatedContext()) { ExprResult Temp = PerformCopyInitialization( InitializedEntity::InitializeTemporary(E->getType()), E->getExprLoc(), @@ -524,9 +597,66 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { return Owned(E); } +/// Determine the degree of POD-ness for an expression. +/// Incomplete types are considered POD, since this check can be performed +/// when we're in an unevaluated context. +Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { + if (Ty->isIncompleteType()) { + if (Ty->isObjCObjectType()) + return VAK_Invalid; + return VAK_Valid; + } + + if (Ty.isCXX98PODType(Context)) + return VAK_Valid; + + // C++0x [expr.call]p7: + // Passing a potentially-evaluated argument of class type (Clause 9) + // having a non-trivial copy constructor, a non-trivial move constructor, + // or a non-trivial destructor, with no corresponding parameter, + // is conditionally-supported with implementation-defined semantics. + if (getLangOpts().CPlusPlus0x && !Ty->isDependentType()) + if (CXXRecordDecl *Record = Ty->getAsCXXRecordDecl()) + if (Record->hasTrivialCopyConstructor() && + Record->hasTrivialMoveConstructor() && + Record->hasTrivialDestructor()) + return VAK_ValidInCXX11; + + if (getLangOpts().ObjCAutoRefCount && Ty->isObjCLifetimeType()) + return VAK_Valid; + return VAK_Invalid; +} + +bool Sema::variadicArgumentPODCheck(const Expr *E, VariadicCallType CT) { + // Don't allow one to pass an Objective-C interface to a vararg. + const QualType & Ty = E->getType(); + + // Complain about passing non-POD types through varargs. + switch (isValidVarArgType(Ty)) { + case VAK_Valid: + break; + case VAK_ValidInCXX11: + DiagRuntimeBehavior(E->getLocStart(), 0, + PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg) + << E->getType() << CT); + break; + case VAK_Invalid: { + if (Ty->isObjCObjectType()) + return DiagRuntimeBehavior(E->getLocStart(), 0, + PDiag(diag::err_cannot_pass_objc_interface_to_vararg) + << Ty << CT); + + return DiagRuntimeBehavior(E->getLocStart(), 0, + PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) + << getLangOpts().CPlusPlus0x << Ty << CT); + } + } + // c++ rules are enforced elsewhere. + return false; +} + /// DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but -/// will warn if the resulting type is not a POD type, and rejects ObjC -/// interfaces passed by value. +/// will create a trap if the resulting type is not a POD type. ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, FunctionDecl *FDecl) { if (const BuiltinType *PlaceholderTy = E->getType()->getAsPlaceholderType()) { @@ -550,76 +680,38 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, return ExprError(); E = ExprRes.take(); - // Don't allow one to pass an Objective-C interface to a vararg. - if (E->getType()->isObjCObjectType() && - DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::err_cannot_pass_objc_interface_to_vararg) - << E->getType() << CT)) - return ExprError(); - - // Complain about passing non-POD types through varargs. However, don't - // perform this check for incomplete types, which we can get here when we're - // in an unevaluated context. - if (!E->getType()->isIncompleteType() && !E->getType().isPODType(Context)) { - // C++0x [expr.call]p7: - // Passing a potentially-evaluated argument of class type (Clause 9) - // having a non-trivial copy constructor, a non-trivial move constructor, - // or a non-trivial destructor, with no corresponding parameter, - // is conditionally-supported with implementation-defined semantics. - bool TrivialEnough = false; - if (getLangOpts().CPlusPlus0x && !E->getType()->isDependentType()) { - if (CXXRecordDecl *Record = E->getType()->getAsCXXRecordDecl()) { - if (Record->hasTrivialCopyConstructor() && - Record->hasTrivialMoveConstructor() && - Record->hasTrivialDestructor()) { - DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cxx98_compat_pass_non_pod_arg_to_vararg) - << E->getType() << CT); - TrivialEnough = true; - } - } - } + // Diagnostics regarding non-POD argument types are + // emitted along with format string checking in Sema::CheckFunctionCall(). + if (isValidVarArgType(E->getType()) == VAK_Invalid) { + // Turn this into a trap. + CXXScopeSpec SS; + SourceLocation TemplateKWLoc; + UnqualifiedId Name; + Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"), + E->getLocStart()); + ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc, + Name, true, false); + if (TrapFn.isInvalid()) + return ExprError(); - if (!TrivialEnough && - getLangOpts().ObjCAutoRefCount && - E->getType()->isObjCLifetimeType()) - TrivialEnough = true; - - if (TrivialEnough) { - // Nothing to diagnose. This is okay. - } else if (DiagRuntimeBehavior(E->getLocStart(), 0, - PDiag(diag::warn_cannot_pass_non_pod_arg_to_vararg) - << getLangOpts().CPlusPlus0x << E->getType() - << CT)) { - // Turn this into a trap. - CXXScopeSpec SS; - SourceLocation TemplateKWLoc; - UnqualifiedId Name; - Name.setIdentifier(PP.getIdentifierInfo("__builtin_trap"), - E->getLocStart()); - ExprResult TrapFn = ActOnIdExpression(TUScope, SS, TemplateKWLoc, Name, - true, false); - if (TrapFn.isInvalid()) - return ExprError(); + ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), + E->getLocStart(), MultiExprArg(), + E->getLocEnd()); + if (Call.isInvalid()) + return ExprError(); - ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), E->getLocStart(), - MultiExprArg(), E->getLocEnd()); - if (Call.isInvalid()) - return ExprError(); - - ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma, - Call.get(), E); - if (Comma.isInvalid()) - return ExprError(); - E = Comma.get(); - } + ExprResult Comma = ActOnBinOp(TUScope, E->getLocStart(), tok::comma, + Call.get(), E); + if (Comma.isInvalid()) + return ExprError(); + return Comma.get(); } - // c++ rules are enforced elsewhere. + if (!getLangOpts().CPlusPlus && RequireCompleteType(E->getExprLoc(), E->getType(), diag::err_call_incomplete_argument)) return ExprError(); - + return Owned(E); } @@ -942,6 +1034,10 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, QualType RHSType = Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); + // For conversion purposes, we ignore any atomic qualifier on the LHS. + if (const AtomicType *AtomicLHS = LHSType->getAs<AtomicType>()) + LHSType = AtomicLHS->getValueType(); + // If both types are identical, no conversion is needed. if (LHSType == RHSType) return LHSType; @@ -949,7 +1045,7 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, // If either side is a non-arithmetic type (e.g. a pointer), we are done. // The caller can deal with this (e.g. pointer + int). if (!LHSType->isArithmeticType() || !RHSType->isArithmeticType()) - return LHSType; + return QualType(); // Apply unary and bitfield promotions to the LHS's type. QualType LHSUnpromotedType = LHSType; @@ -1370,7 +1466,8 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // unqualified lookup. This is useful when (for example) the // original lookup would not have found something because it was a // dependent name. - DeclContext *DC = SS.isEmpty() ? CurContext : 0; + DeclContext *DC = (SS.isEmpty() && !CallsUndergoingInstantiation.empty()) + ? CurContext : 0; while (DC) { if (isa<CXXRecordDecl>(DC)) { LookupQualifiedName(R, DC); @@ -1394,42 +1491,44 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // Give a code modification hint to insert 'this->'. // TODO: fixit for inserting 'Base<T>::' in the other cases. // Actually quite difficult! + if (getLangOpts().MicrosoftMode) + diagnostic = diag::warn_found_via_dependent_bases_lookup; if (isInstance) { + Diag(R.getNameLoc(), diagnostic) << Name + << FixItHint::CreateInsertion(R.getNameLoc(), "this->"); UnresolvedLookupExpr *ULE = cast<UnresolvedLookupExpr>( CallsUndergoingInstantiation.back()->getCallee()); - CXXMethodDecl *DepMethod = cast_or_null<CXXMethodDecl>( - CurMethod->getInstantiatedFromMemberFunction()); - if (DepMethod) { - if (getLangOpts().MicrosoftMode) - diagnostic = diag::warn_found_via_dependent_bases_lookup; - Diag(R.getNameLoc(), diagnostic) << Name - << FixItHint::CreateInsertion(R.getNameLoc(), "this->"); - QualType DepThisType = DepMethod->getThisType(Context); - CheckCXXThisCapture(R.getNameLoc()); - CXXThisExpr *DepThis = new (Context) CXXThisExpr( - R.getNameLoc(), DepThisType, false); - TemplateArgumentListInfo TList; - if (ULE->hasExplicitTemplateArgs()) - ULE->copyTemplateArgumentsInto(TList); - - CXXScopeSpec SS; - SS.Adopt(ULE->getQualifierLoc()); - CXXDependentScopeMemberExpr *DepExpr = - CXXDependentScopeMemberExpr::Create( - Context, DepThis, DepThisType, true, SourceLocation(), - SS.getWithLocInContext(Context), - ULE->getTemplateKeywordLoc(), 0, - R.getLookupNameInfo(), - ULE->hasExplicitTemplateArgs() ? &TList : 0); - CallsUndergoingInstantiation.back()->setCallee(DepExpr); - } else { - // FIXME: we should be able to handle this case too. It is correct - // to add this-> here. This is a workaround for PR7947. - Diag(R.getNameLoc(), diagnostic) << Name; - } + + + CXXMethodDecl *DepMethod; + if (CurMethod->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + DepMethod = cast<CXXMethodDecl>(CurMethod->getPrimaryTemplate()-> + getInstantiatedFromMemberTemplate()->getTemplatedDecl()); + else + DepMethod = cast<CXXMethodDecl>( + CurMethod->getInstantiatedFromMemberFunction()); + assert(DepMethod && "No template pattern found"); + + QualType DepThisType = DepMethod->getThisType(Context); + CheckCXXThisCapture(R.getNameLoc()); + CXXThisExpr *DepThis = new (Context) CXXThisExpr( + R.getNameLoc(), DepThisType, false); + TemplateArgumentListInfo TList; + if (ULE->hasExplicitTemplateArgs()) + ULE->copyTemplateArgumentsInto(TList); + + CXXScopeSpec SS; + SS.Adopt(ULE->getQualifierLoc()); + CXXDependentScopeMemberExpr *DepExpr = + CXXDependentScopeMemberExpr::Create( + Context, DepThis, DepThisType, true, SourceLocation(), + SS.getWithLocInContext(Context), + ULE->getTemplateKeywordLoc(), 0, + R.getLookupNameInfo(), + ULE->hasExplicitTemplateArgs() ? &TList : 0); + CallsUndergoingInstantiation.back()->setCallee(DepExpr); } else { - if (getLangOpts().MicrosoftMode) - diagnostic = diag::warn_found_via_dependent_bases_lookup; Diag(R.getNameLoc(), diagnostic) << Name; } @@ -1862,6 +1961,10 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S, return ExprError(); MarkAnyDeclReferenced(Loc, IV); + + ObjCMethodFamily MF = CurMethod->getMethodFamily(); + if (MF != OMF_init && MF != OMF_dealloc && MF != OMF_finalize) + Diag(Loc, diag::warn_direct_ivar_access) << IV->getDeclName(); return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(), Loc, SelfExpr.take(), true, true)); @@ -2303,7 +2406,7 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, // FIXME: Does the addition of const really only apply in // potentially-evaluated contexts? Since the variable isn't actually // captured in an unevaluated context, it seems that the answer is no. - if (ExprEvalContexts.back().Context != Sema::Unevaluated) { + if (!isUnevaluatedContext()) { QualType CapturedType = getCapturedDeclRefType(cast<VarDecl>(VD), Loc); if (!CapturedType.isNull()) type = CapturedType; @@ -2381,6 +2484,7 @@ ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) { default: llvm_unreachable("Unknown simple primary expr!"); case tok::kw___func__: IT = PredefinedExpr::Func; break; // [C99 6.4.2.2] case tok::kw___FUNCTION__: IT = PredefinedExpr::Function; break; + case tok::kw_L__FUNCTION__: IT = PredefinedExpr::LFunction; break; case tok::kw___PRETTY_FUNCTION__: IT = PredefinedExpr::PrettyFunction; break; } @@ -2402,7 +2506,10 @@ ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) { unsigned Length = PredefinedExpr::ComputeName(IT, currentDecl).length(); llvm::APInt LengthI(32, Length + 1); - ResTy = Context.CharTy.withConst(); + if (IT == PredefinedExpr::LFunction) + ResTy = Context.WCharTy.withConst(); + else + ResTy = Context.CharTy.withConst(); ResTy = Context.getConstantArrayType(ResTy, LengthI, ArrayType::Normal, 0); } return Owned(new (Context) PredefinedExpr(Loc, ResTy, IT)); @@ -2603,7 +2710,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { llvm::APSInt Value(CharBits, CharIsUnsigned); for (unsigned I = 0, N = Literal.getUDSuffixOffset(); I != N; ++I) { Value = ThisTokBegin[I]; - TemplateArgument Arg(Value, Context.CharTy); + TemplateArgument Arg(Context, Value, Context.CharTy); TemplateArgumentLocInfo ArgInfo; ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo)); } @@ -2647,7 +2754,12 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { diag::warn_cxx98_compat_longlong : diag::ext_longlong); // Get the value in the widest-possible width. - llvm::APInt ResultVal(Context.getTargetInfo().getIntMaxTWidth(), 0); + unsigned MaxWidth = Context.getTargetInfo().getIntMaxTWidth(); + // The microsoft literal suffix extensions support 128-bit literals, which + // may be wider than [u]intmax_t. + if (Literal.isMicrosoftInteger && MaxWidth < 128) + MaxWidth = 128; + llvm::APInt ResultVal(MaxWidth, 0); if (Literal.GetIntegerValue(ResultVal)) { // If this value didn't fit into uintmax_t, warn and force to ull. @@ -2695,7 +2807,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { } } - // Finally, check long long if needed. + // Check long long if needed. if (Ty.isNull()) { unsigned LongLongSize = Context.getTargetInfo().getLongLongWidth(); @@ -2712,6 +2824,16 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { Width = LongLongSize; } } + + // If it doesn't fit in unsigned long long, and we're using Microsoft + // extensions, then its a 128-bit integer literal. + if (Ty.isNull() && Literal.isMicrosoftInteger) { + if (Literal.isUnsigned) + Ty = Context.UnsignedInt128Ty; + else + Ty = Context.Int128Ty; + Width = 128; + } // If we still couldn't decide a type, we probably have something that // does not fit in a signed long long, but has no U suffix. @@ -2783,8 +2905,9 @@ static bool CheckObjCTraitOperandConstraints(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, UnaryExprOrTypeTrait TraitKind) { - // Reject sizeof(interface) and sizeof(interface<proto>) in 64-bit mode. - if (S.LangOpts.ObjCNonFragileABI && T->isObjCObjectType()) { + // Reject sizeof(interface) and sizeof(interface<proto>) if the + // runtime doesn't allow it. + if (!S.LangOpts.ObjCRuntime.allowsSizeofAlignof() && T->isObjCObjectType()) { S.Diag(Loc, diag::err_sizeof_nonfragile_interface) << T << (TraitKind == UETT_SizeOf) << ArgRange; @@ -2822,9 +2945,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, return false; if (RequireCompleteExprType(E, - PDiag(diag::err_sizeof_alignof_incomplete_type) - << ExprKind << E->getSourceRange(), - std::make_pair(SourceLocation(), PDiag(0)))) + diag::err_sizeof_alignof_incomplete_type, + ExprKind, E->getSourceRange())) return true; // Completeing the expression's type may have changed it. @@ -2891,8 +3013,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, return false; if (RequireCompleteType(OpLoc, ExprType, - PDiag(diag::err_sizeof_alignof_incomplete_type) - << ExprKind << ExprRange)) + diag::err_sizeof_alignof_incomplete_type, + ExprKind, ExprRange)) return true; if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange, @@ -3075,6 +3197,22 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, return BuildUnaryOp(S, OpLoc, Opc, Input); } +/// \brief Diagnose if arithmetic on the given ObjC pointer is illegal. +/// +/// \return true on error +static bool checkArithmeticOnObjCPointer(Sema &S, + SourceLocation opLoc, + Expr *op) { + assert(op->getType()->isObjCObjectPointerType()); + if (S.LangOpts.ObjCRuntime.allowsPointerArithmetic()) + return false; + + S.Diag(opLoc, diag::err_arithmetic_nonfragile_interface) + << op->getType()->castAs<ObjCObjectPointerType>()->getPointeeType() + << op->getSourceRange(); + return true; +} + ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, Expr *Idx, SourceLocation RLoc) { @@ -3105,7 +3243,6 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, return CreateBuiltinArraySubscriptExpr(Base, LLoc, Idx, RLoc); } - ExprResult Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, Expr *Idx, SourceLocation RLoc) { @@ -3143,13 +3280,21 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); } else if (const ObjCObjectPointerType *PTy = - LHSTy->getAs<ObjCObjectPointerType>()) { + LHSTy->getAs<ObjCObjectPointerType>()) { BaseExpr = LHSExp; IndexExpr = RHSExp; - Result = BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0); - if (!Result.isInvalid()) - return Owned(Result.take()); + + // Use custom logic if this should be the pseudo-object subscript + // expression. + if (!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic()) + return BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0); + ResultType = PTy->getPointeeType(); + if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) { + Diag(LLoc, diag::err_subscript_nonfragile_interface) + << ResultType << BaseExpr->getSourceRange(); + return ExprError(); + } } else if (const PointerType *PTy = RHSTy->getAs<PointerType>()) { // Handle the uncommon case of "123[Ptr]". BaseExpr = RHSExp; @@ -3161,6 +3306,11 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = RHSExp; IndexExpr = LHSExp; ResultType = PTy->getPointeeType(); + if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) { + Diag(LLoc, diag::err_subscript_nonfragile_interface) + << ResultType << BaseExpr->getSourceRange(); + return ExprError(); + } } else if (const VectorType *VTy = LHSTy->getAs<VectorType>()) { BaseExpr = LHSExp; // vectors: V[123] IndexExpr = RHSExp; @@ -3230,16 +3380,8 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, if (!ResultType.hasQualifiers()) VK = VK_RValue; } else if (!ResultType->isDependentType() && RequireCompleteType(LLoc, ResultType, - PDiag(diag::err_subscript_incomplete_type) - << BaseExpr->getSourceRange())) - return ExprError(); - - // Diagnose bad cases where we step over interface counts. - if (ResultType->isObjCObjectType() && LangOpts.ObjCNonFragileABI) { - Diag(LLoc, diag::err_subscript_nonfragile_interface) - << ResultType << BaseExpr->getSourceRange(); + diag::err_subscript_incomplete_type, BaseExpr)) return ExprError(); - } assert(VK == VK_RValue || LangOpts.CPlusPlus || !ResultType.isCForbiddenLValueType()); @@ -3263,14 +3405,20 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, if (Param->hasUninstantiatedDefaultArg()) { Expr *UninstExpr = Param->getUninstantiatedDefaultArg(); + EnterExpressionEvaluationContext EvalContext(*this, PotentiallyEvaluated, + Param); + // Instantiate the expression. MultiLevelTemplateArgumentList ArgList = getTemplateInstantiationArgs(FD, 0, /*RelativeToPrimary=*/true); std::pair<const TemplateArgument *, unsigned> Innermost = ArgList.getInnermost(); - InstantiatingTemplate Inst(*this, CallLoc, Param, Innermost.first, - Innermost.second); + InstantiatingTemplate Inst(*this, CallLoc, Param, + ArrayRef<TemplateArgument>(Innermost.first, + Innermost.second)); + if (Inst) + return ExprError(); ExprResult Result; { @@ -3299,9 +3447,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, if (Result.isInvalid()) return ExprError(); + Expr *Arg = Result.takeAs<Expr>(); + CheckImplicitConversions(Arg, Param->getOuterLocStart()); // Build the default argument expression. - return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param, - Result.takeAs<Expr>())); + return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param, Arg)); } // If the default expression creates temporaries, we need to @@ -3331,6 +3480,25 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param)); } + +Sema::VariadicCallType +Sema::getVariadicCallType(FunctionDecl *FDecl, const FunctionProtoType *Proto, + Expr *Fn) { + if (Proto && Proto->isVariadic()) { + if (dyn_cast_or_null<CXXConstructorDecl>(FDecl)) + return VariadicConstructor; + else if (Fn && Fn->getType()->isBlockPointerType()) + return VariadicBlock; + else if (FDecl) { + if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(FDecl)) + if (Method->isInstance()) + return VariadicMethod; + } + return VariadicFunction; + } + return VariadicDoesNotApply; +} + /// ConvertArgumentsForCall - Converts the arguments specified in /// Args/NumArgs to the parameter types of the function FDecl with /// function prototype Proto. Call is the call expression itself, and @@ -3365,11 +3533,18 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, // arguments for the remaining parameters), don't make the call. if (NumArgs < NumArgsInProto) { if (NumArgs < MinArgs) { - Diag(RParenLoc, MinArgs == NumArgsInProto - ? diag::err_typecheck_call_too_few_args - : diag::err_typecheck_call_too_few_args_at_least) - << FnKind - << MinArgs << NumArgs << Fn->getSourceRange(); + if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) + Diag(RParenLoc, MinArgs == NumArgsInProto && !Proto->isVariadic() + ? diag::err_typecheck_call_too_few_args_one + : diag::err_typecheck_call_too_few_args_at_least_one) + << FnKind + << FDecl->getParamDecl(0) << Fn->getSourceRange(); + else + Diag(RParenLoc, MinArgs == NumArgsInProto && !Proto->isVariadic() + ? diag::err_typecheck_call_too_few_args + : diag::err_typecheck_call_too_few_args_at_least) + << FnKind + << MinArgs << NumArgs << Fn->getSourceRange(); // Emit the location of the prototype. if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig) @@ -3385,14 +3560,24 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, // them. if (NumArgs > NumArgsInProto) { if (!Proto->isVariadic()) { - Diag(Args[NumArgsInProto]->getLocStart(), - MinArgs == NumArgsInProto - ? diag::err_typecheck_call_too_many_args - : diag::err_typecheck_call_too_many_args_at_most) - << FnKind - << NumArgsInProto << NumArgs << Fn->getSourceRange() - << SourceRange(Args[NumArgsInProto]->getLocStart(), - Args[NumArgs-1]->getLocEnd()); + if (NumArgsInProto == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) + Diag(Args[NumArgsInProto]->getLocStart(), + MinArgs == NumArgsInProto + ? diag::err_typecheck_call_too_many_args_one + : diag::err_typecheck_call_too_many_args_at_most_one) + << FnKind + << FDecl->getParamDecl(0) << NumArgs << Fn->getSourceRange() + << SourceRange(Args[NumArgsInProto]->getLocStart(), + Args[NumArgs-1]->getLocEnd()); + else + Diag(Args[NumArgsInProto]->getLocStart(), + MinArgs == NumArgsInProto + ? diag::err_typecheck_call_too_many_args + : diag::err_typecheck_call_too_many_args_at_most) + << FnKind + << NumArgsInProto << NumArgs << Fn->getSourceRange() + << SourceRange(Args[NumArgsInProto]->getLocStart(), + Args[NumArgs-1]->getLocEnd()); // Emit the location of the prototype. if (FDecl && !FDecl->getBuiltinID() && !IsExecConfig) @@ -3405,12 +3590,8 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, } } SmallVector<Expr *, 8> AllArgs; - VariadicCallType CallType = - Proto->isVariadic() ? VariadicFunction : VariadicDoesNotApply; - if (Fn->getType()->isBlockPointerType()) - CallType = VariadicBlock; // Block - else if (isa<MemberExpr>(Fn)) - CallType = VariadicMethod; + VariadicCallType CallType = getVariadicCallType(FDecl, Proto, Fn); + Invalid = GatherArgumentsForCall(Call->getLocStart(), FDecl, Proto, 0, Args, NumArgs, AllArgs, CallType); if (Invalid) @@ -3448,8 +3629,7 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, if (RequireCompleteType(Arg->getLocStart(), ProtoArgType, - PDiag(diag::err_call_incomplete_argument) - << Arg->getSourceRange())) + diag::err_call_incomplete_argument, Arg)) return true; // Pass the argument @@ -3500,7 +3680,6 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, // If this is a variadic call, handle args passed through "...". if (CallType != VariadicDoesNotApply) { - // Assume that extern "C" functions with variadic arguments that // return __unknown_anytype aren't *really* variadic. if (Proto->getResultType() == Context.UnknownAnyTy && @@ -3763,20 +3942,19 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, // Make the call expr early, before semantic checks. This guarantees cleanup // of arguments and function on error. CallExpr *TheCall; - if (Config) { + if (Config) TheCall = new (Context) CUDAKernelCallExpr(Context, Fn, cast<CallExpr>(Config), Args, NumArgs, Context.BoolTy, VK_RValue, RParenLoc); - } else { + else TheCall = new (Context) CallExpr(Context, Fn, Args, NumArgs, Context.BoolTy, VK_RValue, RParenLoc); - } unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); @@ -3839,7 +4017,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, TheCall->setType(FuncT->getCallResultType(Context)); TheCall->setValueKind(Expr::getValueKindForType(FuncT->getResultType())); - if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT)) { + const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FuncT); + if (Proto) { if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, NumArgs, RParenLoc, IsExecConfig)) return ExprError(); @@ -3851,8 +4030,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, // on our knowledge of the function definition. const FunctionDecl *Def = 0; if (FDecl->hasBody(Def) && NumArgs != Def->param_size()) { - const FunctionProtoType *Proto - = Def->getType()->getAs<FunctionProtoType>(); + Proto = Def->getType()->getAs<FunctionProtoType>(); if (!Proto || !(Proto->isVariadic() && NumArgs >= Def->param_size())) Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) << (NumArgs > Def->param_size()) << FDecl << Fn->getSourceRange(); @@ -3892,8 +4070,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, if (RequireCompleteType(Arg->getLocStart(), Arg->getType(), - PDiag(diag::err_call_incomplete_argument) - << Arg->getSourceRange())) + diag::err_call_incomplete_argument, Arg)) return ExprError(); TheCall->setArg(i, Arg); @@ -3911,13 +4088,13 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, // Do special checking on direct calls to functions. if (FDecl) { - if (CheckFunctionCall(FDecl, TheCall)) + if (CheckFunctionCall(FDecl, TheCall, Proto)) return ExprError(); if (BuiltinID) return CheckBuiltinFunctionCall(BuiltinID, TheCall); } else if (NDecl) { - if (CheckBlockCall(NDecl, TheCall)) + if (CheckBlockCall(NDecl, TheCall, Proto)) return ExprError(); } @@ -3946,18 +4123,17 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, if (literalType->isArrayType()) { if (RequireCompleteType(LParenLoc, Context.getBaseElementType(literalType), - PDiag(diag::err_illegal_decl_array_incomplete_type) - << SourceRange(LParenLoc, - LiteralExpr->getSourceRange().getEnd()))) + diag::err_illegal_decl_array_incomplete_type, + SourceRange(LParenLoc, + LiteralExpr->getSourceRange().getEnd()))) return ExprError(); if (literalType->isVariableArrayType()) return ExprError(Diag(LParenLoc, diag::err_variable_object_no_init) << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())); } else if (!literalType->isDependentType() && RequireCompleteType(LParenLoc, literalType, - PDiag(diag::err_typecheck_decl_incomplete_type) - << SourceRange(LParenLoc, - LiteralExpr->getSourceRange().getEnd()))) + diag::err_typecheck_decl_incomplete_type, + SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) return ExprError(); InitializedEntity Entity @@ -4054,11 +4230,6 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) { // pointers. Everything else should be possible. QualType SrcTy = Src.get()->getType(); - if (const AtomicType *SrcAtomicTy = SrcTy->getAs<AtomicType>()) - SrcTy = SrcAtomicTy->getValueType(); - if (const AtomicType *DestAtomicTy = DestTy->getAs<AtomicType>()) - DestTy = DestAtomicTy->getValueType(); - if (Context.hasSameUnqualifiedType(SrcTy, DestTy)) return CK_NoOp; @@ -4461,7 +4632,10 @@ bool Sema::DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr, if (NullKind == Expr::NPCK_NotNull) return false; - if (NullKind == Expr::NPCK_ZeroInteger) { + if (NullKind == Expr::NPCK_ZeroExpression) + return false; + + if (NullKind == Expr::NPCK_ZeroLiteral) { // In this case, check to make sure that we got here from a "NULL" // string in the source code. NullExpr = NullExpr->IgnoreParenImpCasts(); @@ -5382,21 +5556,19 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, return Compatible; } + // If we have an atomic type, try a non-atomic assignment, then just add an + // atomic qualification step. if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(LHSType)) { - if (AtomicTy->getValueType() == RHSType) { - Kind = CK_NonAtomicToAtomic; - return Compatible; - } - } - - if (const AtomicType *AtomicTy = dyn_cast<AtomicType>(RHSType)) { - if (AtomicTy->getValueType() == LHSType) { - Kind = CK_AtomicToNonAtomic; - return Compatible; - } + Sema::AssignConvertType result = + CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); + if (result != Compatible) + return result; + if (Kind != CK_NoOp) + RHS = ImpCastExprToType(RHS.take(), AtomicTy->getValueType(), Kind); + Kind = CK_NonAtomicToAtomic; + return Compatible; } - // If the left-hand side is a reference type, then we are in a // (rare!) case where we've allowed the use of references in C, // e.g., as a parameter type in a built-in function. In this case, @@ -5936,14 +6108,8 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, return QualType(); - if (!LHS.get()->getType()->isArithmeticType() || - !RHS.get()->getType()->isArithmeticType()) { - if (IsCompAssign && - LHS.get()->getType()->isAtomicType() && - RHS.get()->getType()->isArithmeticType()) - return compType; + if (compType.isNull() || !compType->isArithmeticType()) return InvalidOperands(Loc, LHS, RHS); - } // Check for division by zero. if (IsDiv && @@ -5971,8 +6137,7 @@ QualType Sema::CheckRemainderOperands( if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - if (!LHS.get()->getType()->isIntegerType() || - !RHS.get()->getType()->isIntegerType()) + if (compType.isNull() || !compType->isIntegerType()) return InvalidOperands(Loc, LHS, RHS); // Check for remainder by zero. @@ -6036,17 +6201,12 @@ static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, /// \returns True if pointer has incomplete type static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, Expr *Operand) { - if ((Operand->getType()->isPointerType() && - !Operand->getType()->isDependentType()) || - Operand->getType()->isObjCObjectPointerType()) { - QualType PointeeTy = Operand->getType()->getPointeeType(); - if (S.RequireCompleteType( - Loc, PointeeTy, - S.PDiag(diag::err_typecheck_arithmetic_incomplete_type) - << PointeeTy << Operand->getSourceRange())) - return true; - } - return false; + assert(Operand->getType()->isAnyPointerType() && + !Operand->getType()->isDependentType()); + QualType PointeeTy = Operand->getType()->getPointeeType(); + return S.RequireCompleteType(Loc, PointeeTy, + diag::err_typecheck_arithmetic_incomplete_type, + PointeeTy, Operand->getSourceRange()); } /// \brief Check the validity of an arithmetic pointer operand. @@ -6117,26 +6277,14 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, return !S.getLangOpts().CPlusPlus; } - if (checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) return false; - if (checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) return false; + if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) + return false; + if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) + return false; return true; } -/// \brief Check bad cases where we step over interface counts. -static bool checkArithmethicPointerOnNonFragileABI(Sema &S, - SourceLocation OpLoc, - Expr *Op) { - assert(Op->getType()->isAnyPointerType()); - QualType PointeeTy = Op->getType()->getPointeeType(); - if (!PointeeTy->isObjCObjectType() || !S.LangOpts.ObjCNonFragileABI) - return true; - - S.Diag(OpLoc, diag::err_arithmetic_nonfragile_interface) - << PointeeTy << Op->getSourceRange(); - return false; -} - /// diagnoseStringPlusInt - Emit a warning when adding an integer to a string /// literal. static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, @@ -6208,25 +6356,31 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get()); // handle the common case first (both operands are arithmetic). - if (LHS.get()->getType()->isArithmeticType() && - RHS.get()->getType()->isArithmeticType()) { + if (!compType.isNull() && compType->isArithmeticType()) { if (CompLHSTy) *CompLHSTy = compType; return compType; } - if (LHS.get()->getType()->isAtomicType() && - RHS.get()->getType()->isArithmeticType()) { - *CompLHSTy = LHS.get()->getType(); - return compType; - } + // Type-checking. Ultimately the pointer's going to be in PExp; + // note that we bias towards the LHS being the pointer. + Expr *PExp = LHS.get(), *IExp = RHS.get(); - // Put any potential pointer into PExp - Expr* PExp = LHS.get(), *IExp = RHS.get(); - if (IExp->getType()->isAnyPointerType()) + bool isObjCPointer; + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { std::swap(PExp, IExp); - - if (!PExp->getType()->isAnyPointerType()) - return InvalidOperands(Loc, LHS, RHS); + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + return InvalidOperands(Loc, LHS, RHS); + } + } + assert(PExp->getType()->isAnyPointerType()); if (!IExp->getType()->isIntegerType()) return InvalidOperands(Loc, LHS, RHS); @@ -6234,8 +6388,7 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) return QualType(); - // Diagnose bad cases where we step over interface counts. - if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, PExp)) + if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) return QualType(); // Check array bounds for pointer arithemtic @@ -6274,24 +6427,18 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, // Enforce type constraints: C99 6.5.6p3. // Handle the common case first (both operands are arithmetic). - if (LHS.get()->getType()->isArithmeticType() && - RHS.get()->getType()->isArithmeticType()) { + if (!compType.isNull() && compType->isArithmeticType()) { if (CompLHSTy) *CompLHSTy = compType; return compType; } - if (LHS.get()->getType()->isAtomicType() && - RHS.get()->getType()->isArithmeticType()) { - *CompLHSTy = LHS.get()->getType(); - return compType; - } - // Either ptr - int or ptr - ptr. if (LHS.get()->getType()->isAnyPointerType()) { QualType lpointee = LHS.get()->getType()->getPointeeType(); // Diagnose bad cases where we step over interface counts. - if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, LHS.get())) + if (LHS.get()->getType()->isObjCObjectPointerType() && + checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) return QualType(); // The result type of a pointer-int computation is the pointer type. @@ -6560,6 +6707,163 @@ static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc, << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); } +static bool isObjCObjectLiteral(ExprResult &E) { + switch (E.get()->getStmtClass()) { + case Stmt::ObjCArrayLiteralClass: + case Stmt::ObjCDictionaryLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ObjCBoxedExprClass: + return true; + default: + // Note that ObjCBoolLiteral is NOT an object literal! + return false; + } +} + +static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) { + // Get the LHS object's interface type. + QualType Type = LHS->getType(); + QualType InterfaceType; + if (const ObjCObjectPointerType *PTy = Type->getAs<ObjCObjectPointerType>()) { + InterfaceType = PTy->getPointeeType(); + if (const ObjCObjectType *iQFaceTy = + InterfaceType->getAsObjCQualifiedInterfaceType()) + InterfaceType = iQFaceTy->getBaseType(); + } else { + // If this is not actually an Objective-C object, bail out. + return false; + } + + // If the RHS isn't an Objective-C object, bail out. + if (!RHS->getType()->isObjCObjectPointerType()) + return false; + + // Try to find the -isEqual: method. + Selector IsEqualSel = S.NSAPIObj->getIsEqualSelector(); + ObjCMethodDecl *Method = S.LookupMethodInObjectType(IsEqualSel, + InterfaceType, + /*instance=*/true); + if (!Method) { + if (Type->isObjCIdType()) { + // For 'id', just check the global pool. + Method = S.LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(), + /*receiverId=*/true, + /*warn=*/false); + } else { + // Check protocols. + Method = S.LookupMethodInQualifiedType(IsEqualSel, + cast<ObjCObjectPointerType>(Type), + /*instance=*/true); + } + } + + if (!Method) + return false; + + QualType T = Method->param_begin()[0]->getType(); + if (!T->isObjCObjectPointerType()) + return false; + + QualType R = Method->getResultType(); + if (!R->isScalarType()) + return false; + + return true; +} + +static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS, + BinaryOperator::Opcode Opc){ + Expr *Literal; + Expr *Other; + if (isObjCObjectLiteral(LHS)) { + Literal = LHS.get(); + Other = RHS.get(); + } else { + Literal = RHS.get(); + Other = LHS.get(); + } + + // Don't warn on comparisons against nil. + Other = Other->IgnoreParenCasts(); + if (Other->isNullPointerConstant(S.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) + return; + + // This should be kept in sync with warn_objc_literal_comparison. + // LK_String should always be last, since it has its own warning flag. + enum { + LK_Array, + LK_Dictionary, + LK_Numeric, + LK_Boxed, + LK_String + } LiteralKind; + + switch (Literal->getStmtClass()) { + case Stmt::ObjCStringLiteralClass: + // "string literal" + LiteralKind = LK_String; + break; + case Stmt::ObjCArrayLiteralClass: + // "array literal" + LiteralKind = LK_Array; + break; + case Stmt::ObjCDictionaryLiteralClass: + // "dictionary literal" + LiteralKind = LK_Dictionary; + break; + case Stmt::ObjCBoxedExprClass: { + Expr *Inner = cast<ObjCBoxedExpr>(Literal)->getSubExpr(); + switch (Inner->getStmtClass()) { + case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::CXXBoolLiteralExprClass: + // "numeric literal" + LiteralKind = LK_Numeric; + break; + case Stmt::ImplicitCastExprClass: { + CastKind CK = cast<CastExpr>(Inner)->getCastKind(); + // Boolean literals can be represented by implicit casts. + if (CK == CK_IntegralToBoolean || CK == CK_IntegralCast) { + LiteralKind = LK_Numeric; + break; + } + // FALLTHROUGH + } + default: + // "boxed expression" + LiteralKind = LK_Boxed; + break; + } + break; + } + default: + llvm_unreachable("Unknown Objective-C object literal kind"); + } + + if (LiteralKind == LK_String) + S.Diag(Loc, diag::warn_objc_string_literal_comparison) + << Literal->getSourceRange(); + else + S.Diag(Loc, diag::warn_objc_literal_comparison) + << LiteralKind << Literal->getSourceRange(); + + if (BinaryOperator::isEqualityOp(Opc) && + hasIsEqualMethod(S, LHS.get(), RHS.get())) { + SourceLocation Start = LHS.get()->getLocStart(); + SourceLocation End = S.PP.getLocForEndOfToken(RHS.get()->getLocEnd()); + SourceRange OpRange(Loc, S.PP.getLocForEndOfToken(Loc)); + + S.Diag(Loc, diag::note_objc_literal_comparison_isequal) + << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![") + << FixItHint::CreateReplacement(OpRange, "isEqual:") + << FixItHint::CreateInsertion(End, "]"); + } +} + // C99 6.5.8, C++ [expr.rel] QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, unsigned OpaqueOpc, @@ -6884,6 +7188,9 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (!Context.areComparableObjCPointerTypes(LHSType, RHSType)) diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); + if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS)) + diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc); + if (LHSIsNull && !RHSIsNull) LHS = ImpCastExprToType(LHS.take(), RHSType, CK_BitCast); else @@ -7037,8 +7344,7 @@ inline QualType Sema::CheckBitwiseOperands( LHS = LHSResult.take(); RHS = RHSResult.take(); - if (LHS.get()->getType()->isIntegralOrUnscopedEnumerationType() && - RHS.get()->getType()->isIntegralOrUnscopedEnumerationType()) + if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) return compType; return InvalidOperands(Loc, LHS, RHS); } @@ -7251,6 +7557,7 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { break; case Expr::MLV_ArrayType: + case Expr::MLV_ArrayTemporary: Diag = diag::err_typecheck_array_not_modifiable_lvalue; NeedType = true; break; @@ -7271,8 +7578,7 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { case Expr::MLV_IncompleteType: case Expr::MLV_IncompleteVoidType: return S.RequireCompleteType(Loc, E->getType(), - S.PDiag(diag::err_typecheck_incomplete_type_not_modifiable_lvalue) - << E->getSourceRange()); + diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); case Expr::MLV_DuplicateVectorComponents: Diag = diag::err_typecheck_duplicate_vector_components_not_mlvalue; break; @@ -7297,7 +7603,27 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { return true; } +static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr, + SourceLocation Loc, + Sema &Sema) { + // C / C++ fields + MemberExpr *ML = dyn_cast<MemberExpr>(LHSExpr); + MemberExpr *MR = dyn_cast<MemberExpr>(RHSExpr); + if (ML && MR && ML->getMemberDecl() == MR->getMemberDecl()) { + if (isa<CXXThisExpr>(ML->getBase()) && isa<CXXThisExpr>(MR->getBase())) + Sema.Diag(Loc, diag::warn_identity_field_assign) << 0; + } + // Objective-C instance variables + ObjCIvarRefExpr *OL = dyn_cast<ObjCIvarRefExpr>(LHSExpr); + ObjCIvarRefExpr *OR = dyn_cast<ObjCIvarRefExpr>(RHSExpr); + if (OL && OR && OL->getDecl() == OR->getDecl()) { + DeclRefExpr *RL = dyn_cast<DeclRefExpr>(OL->getBase()->IgnoreImpCasts()); + DeclRefExpr *RR = dyn_cast<DeclRefExpr>(OR->getBase()->IgnoreImpCasts()); + if (RL && RR && RL->getDecl() == RR->getDecl()) + Sema.Diag(Loc, diag::warn_identity_field_assign) << 1; + } +} // C99 6.5.16.1 QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, @@ -7314,6 +7640,10 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, CompoundType; AssignConvertType ConvTy; if (CompoundType.isNull()) { + Expr *RHSCheck = RHS.get(); + + CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this); + QualType LHSTy(LHSType); ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS); if (RHS.isInvalid()) @@ -7334,7 +7664,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, // If the RHS is a unary plus or minus, check to see if they = and + are // right next to each other. If so, the user may have typo'd "x =+ 4" // instead of "x += 4". - Expr *RHSCheck = RHS.get(); if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RHSCheck)) RHSCheck = ICE->getSubExpr(); if (UnaryOperator *UO = dyn_cast<UnaryOperator>(RHSCheck)) { @@ -7384,8 +7713,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, // C99 6.5.17 static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, SourceLocation Loc) { - S.DiagnoseUnusedExprResult(LHS.get()); - LHS = S.CheckPlaceholderExpr(LHS.take()); RHS = S.CheckPlaceholderExpr(RHS.take()); if (LHS.isInvalid() || RHS.isInvalid()) @@ -7401,6 +7728,8 @@ static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, if (LHS.isInvalid()) return QualType(); + S.DiagnoseUnusedExprResult(LHS.get()); + if (!S.getLangOpts().CPlusPlus) { RHS = S.DefaultFunctionArrayLvalueConversion(RHS.take()); if (RHS.isInvalid()) @@ -7441,14 +7770,16 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, S.Diag(OpLoc, diag::warn_increment_bool) << Op->getSourceRange(); } else if (ResType->isRealType()) { // OK! - } else if (ResType->isAnyPointerType()) { + } else if (ResType->isPointerType()) { // C99 6.5.2.4p2, 6.5.6p2 if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) return QualType(); - - // Diagnose bad cases where we step over interface counts. - else if (!checkArithmethicPointerOnNonFragileABI(S, OpLoc, Op)) - return QualType(); + } else if (ResType->isObjCObjectPointerType()) { + // On modern runtimes, ObjC pointer arithmetic is forbidden. + // Otherwise, we just need a complete type. + if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || + checkArithmeticOnObjCPointer(S, OpLoc, Op)) + return QualType(); } else if (ResType->isAnyComplexType()) { // C99 does not support ++/-- on complex types, we allow as an extension. S.Diag(OpLoc, diag::ext_integer_increment_complex) @@ -8064,7 +8395,7 @@ static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, << DiagRange << BinOp::getOpcodeStr(Opc) << OpStr; SuggestParentheses(Self, OpLoc, Self.PDiag(diag::note_precedence_bitwise_silence) << OpStr, - RHSExpr->getSourceRange()); + (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange()); SuggestParentheses(Self, OpLoc, Self.PDiag(diag::note_precedence_bitwise_first) << BinOp::getOpcodeStr(Opc), ParensRange); @@ -8669,8 +9000,7 @@ ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, // with an incomplete type would be ill-formed. if (!Dependent && RequireCompleteType(BuiltinLoc, ArgTy, - PDiag(diag::err_offsetof_incomplete_type) - << TypeRange)) + diag::err_offsetof_incomplete_type, TypeRange)) return ExprError(); // offsetof with non-identifier designators (e.g. "offsetof(x, a.b[c])") are a @@ -8743,10 +9073,18 @@ ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, // The macro offsetof accepts a restricted set of type arguments in this // International Standard. type shall be a POD structure or a POD union // (clause 9). + // C++11 [support.types]p4: + // If type is not a standard-layout class (Clause 9), the results are + // undefined. if (CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) { - if (!CRD->isPOD() && !DidWarnAboutNonPOD && + bool IsSafe = LangOpts.CPlusPlus0x? CRD->isStandardLayout() : CRD->isPOD(); + unsigned DiagID = + LangOpts.CPlusPlus0x? diag::warn_offsetof_non_standardlayout_type + : diag::warn_offsetof_non_pod_type; + + if (!IsSafe && !DidWarnAboutNonPOD && DiagRuntimeBehavior(BuiltinLoc, 0, - PDiag(diag::warn_offsetof_non_pod_type) + PDiag(DiagID) << SourceRange(CompPtr[0].LocStart, OC.LocEnd) << CurrentType)) DidWarnAboutNonPOD = true; @@ -8850,8 +9188,9 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, } else { // The conditional expression is required to be a constant expression. llvm::APSInt condEval(32); - ExprResult CondICE = VerifyIntegerConstantExpression(CondExpr, &condEval, - PDiag(diag::err_typecheck_choose_expr_requires_constant), false); + ExprResult CondICE + = VerifyIntegerConstantExpression(CondExpr, &condEval, + diag::err_typecheck_choose_expr_requires_constant, false); if (CondICE.isInvalid()) return ExprError(); CondExpr = CondICE.take(); @@ -8892,7 +9231,8 @@ void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { PushExpressionEvaluationContext(PotentiallyEvaluated); } -void Sema::ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) { +void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, + Scope *CurScope) { assert(ParamInfo.getIdentifier()==0 && "block-id should have no identifier!"); assert(ParamInfo.getContext() == Declarator::BlockLiteralContext); BlockScopeInfo *CurBlock = getCurBlock(); @@ -8900,6 +9240,18 @@ void Sema::ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) { TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo, CurScope); QualType T = Sig->getType(); + // FIXME: We should allow unexpanded parameter packs here, but that would, + // in turn, make the block expression contain unexpanded parameter packs. + if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) { + // Drop the parameters. + FunctionProtoType::ExtProtoInfo EPI; + EPI.HasTrailingReturn = false; + EPI.TypeQuals |= DeclSpec::TQ_const; + T = Context.getFunctionType(Context.DependentTy, /*Args=*/0, /*NumArgs=*/0, + EPI); + Sig = Context.getTrivialTypeSourceInfo(T); + } + // GetTypeForDeclarator always produces a function type for a block // literal signature. Furthermore, it is always a FunctionProtoType // unless the function was written with a typedef. @@ -9038,7 +9390,10 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, PopExpressionEvaluationContext(); BlockScopeInfo *BSI = cast<BlockScopeInfo>(FunctionScopes.back()); - + + if (BSI->HasImplicitReturnType) + deduceClosureReturnType(*BSI); + PopDeclContext(); QualType RetTy = Context.VoidTy; @@ -9111,7 +9466,12 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BSI->TheDecl->setBody(cast<CompoundStmt>(Body)); - computeNRVO(Body, getCurBlock()); + // Try to apply the named return value optimization. We have to check again + // if we can do this, though, because blocks keep return statements around + // to deduce an implicit return type. + if (getLangOpts().CPlusPlus && RetTy->isRecordType() && + !BSI->TheDecl->isDependentContext()) + computeNRVO(Body, getCurBlock()); BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy); const AnalysisBasedWarnings::Policy &WP = AnalysisWarnings.getDefaultPolicy(); @@ -9182,14 +9542,14 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, if (!TInfo->getType()->isDependentType()) { if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), - PDiag(diag::err_second_parameter_to_va_arg_incomplete) - << TInfo->getTypeLoc().getSourceRange())) + diag::err_second_parameter_to_va_arg_incomplete, + TInfo->getTypeLoc())) return ExprError(); if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType(), - PDiag(diag::err_second_parameter_to_va_arg_abstract) - << TInfo->getTypeLoc().getSourceRange())) + TInfo->getType(), + diag::err_second_parameter_to_va_arg_abstract, + TInfo->getTypeLoc())) return ExprError(); if (!TInfo->getType().isPODType(Context)) { @@ -9291,7 +9651,10 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, bool MayHaveFunctionDiff = false; switch (ConvTy) { - case Compatible: return false; + case Compatible: + DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr); + return false; + case PointerToInt: DiagKind = diag::ext_typecheck_convert_pointer_int; ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); @@ -9434,15 +9797,44 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result) { - return VerifyIntegerConstantExpression(E, Result, - PDiag(diag::err_expr_not_ice) << LangOpts.CPlusPlus); + class SimpleICEDiagnoser : public VerifyICEDiagnoser { + public: + virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) { + S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus << SR; + } + } Diagnoser; + + return VerifyIntegerConstantExpression(E, Result, Diagnoser); +} + +ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, + llvm::APSInt *Result, + unsigned DiagID, + bool AllowFold) { + class IDDiagnoser : public VerifyICEDiagnoser { + unsigned DiagID; + + public: + IDDiagnoser(unsigned DiagID) + : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { } + + virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) { + S.Diag(Loc, DiagID) << SR; + } + } Diagnoser(DiagID); + + return VerifyIntegerConstantExpression(E, Result, Diagnoser, AllowFold); +} + +void Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc, + SourceRange SR) { + S.Diag(Loc, diag::ext_expr_not_ice) << SR << S.LangOpts.CPlusPlus; } ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, - const PartialDiagnostic &NotIceDiag, - bool AllowFold, - const PartialDiagnostic &FoldDiag) { + VerifyICEDiagnoser &Diagnoser, + bool AllowFold) { SourceLocation DiagLoc = E->getLocStart(); if (getLangOpts().CPlusPlus0x) { @@ -9452,23 +9844,111 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, // have a single non-explicit conversion function to an integral or // unscoped enumeration type ExprResult Converted; - if (NotIceDiag.getDiagID()) { - Converted = ConvertToIntegralOrEnumerationType( - DiagLoc, E, - PDiag(diag::err_ice_not_integral), - PDiag(diag::err_ice_incomplete_type), - PDiag(diag::err_ice_explicit_conversion), - PDiag(diag::note_ice_conversion_here), - PDiag(diag::err_ice_ambiguous_conversion), - PDiag(diag::note_ice_conversion_here), - PDiag(0), - /*AllowScopedEnumerations*/ false); + if (!Diagnoser.Suppress) { + class CXX11ConvertDiagnoser : public ICEConvertDiagnoser { + public: + CXX11ConvertDiagnoser() : ICEConvertDiagnoser(false, true) { } + + virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_ice_not_integral) << T; + } + + virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, + SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_ice_incomplete_type) << T; + } + + virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, + SourceLocation Loc, + QualType T, + QualType ConvTy) { + return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy; + } + + virtual DiagnosticBuilder noteExplicitConv(Sema &S, + CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T; + } + + virtual DiagnosticBuilder noteAmbiguous(Sema &S, + CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseConversion(Sema &S, + SourceLocation Loc, + QualType T, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + } ConvertDiagnoser; + + Converted = ConvertToIntegralOrEnumerationType(DiagLoc, E, + ConvertDiagnoser, + /*AllowScopedEnumerations*/ false); } else { // The caller wants to silently enquire whether this is an ICE. Don't // produce any diagnostics if it isn't. - Converted = ConvertToIntegralOrEnumerationType( - DiagLoc, E, PDiag(), PDiag(), PDiag(), PDiag(), - PDiag(), PDiag(), PDiag(), false); + class SilentICEConvertDiagnoser : public ICEConvertDiagnoser { + public: + SilentICEConvertDiagnoser() : ICEConvertDiagnoser(true, true) { } + + virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, + SourceLocation Loc, + QualType T) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, + SourceLocation Loc, + QualType T, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder noteExplicitConv(Sema &S, + CXXConversionDecl *Conv, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc, + QualType T) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder noteAmbiguous(Sema &S, + CXXConversionDecl *Conv, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + + virtual DiagnosticBuilder diagnoseConversion(Sema &S, + SourceLocation Loc, + QualType T, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + } ConvertDiagnoser; + + Converted = ConvertToIntegralOrEnumerationType(DiagLoc, E, + ConvertDiagnoser, false); } if (Converted.isInvalid()) return Converted; @@ -9477,8 +9957,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, return ExprError(); } else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) { // An ICE must be of integral or unscoped enumeration type. - if (NotIceDiag.getDiagID()) - Diag(DiagLoc, NotIceDiag) << E->getSourceRange(); + if (!Diagnoser.Suppress) + Diagnoser.diagnoseNotICE(*this, DiagLoc, E->getSourceRange()); return ExprError(); } @@ -9518,8 +9998,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, } if (!Folded || !AllowFold) { - if (NotIceDiag.getDiagID()) { - Diag(DiagLoc, NotIceDiag) << E->getSourceRange(); + if (!Diagnoser.Suppress) { + Diagnoser.diagnoseNotICE(*this, DiagLoc, E->getSourceRange()); for (unsigned I = 0, N = Notes.size(); I != N; ++I) Diag(Notes[I].first, Notes[I].second); } @@ -9527,11 +10007,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, return ExprError(); } - if (FoldDiag.getDiagID()) - Diag(DiagLoc, FoldDiag) << E->getSourceRange(); - else - Diag(DiagLoc, diag::ext_expr_not_ice) - << E->getSourceRange() << LangOpts.CPlusPlus; + Diagnoser.diagnoseFold(*this, DiagLoc, E->getSourceRange()); for (unsigned I = 0, N = Notes.size(); I != N; ++I) Diag(Notes[I].first, Notes[I].second); @@ -9569,7 +10045,7 @@ namespace { // Error on DeclRefExprs referring to FieldDecls. ExprResult TransformDeclRefExpr(DeclRefExpr *E) { if (isa<FieldDecl>(E->getDecl()) && - SemaRef.ExprEvalContexts.back().Context != Sema::Unevaluated) + !SemaRef.isUnevaluatedContext()) return SemaRef.Diag(E->getLocation(), diag::err_invalid_non_static_member_use) << E->getDecl() << E->getSourceRange(); @@ -9774,11 +10250,11 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { // FIXME: Is this really right? if (CurContext == Func) return; - // Instantiate the exception specification for any function which is + // Resolve the exception specification for any function which is // used: CodeGen will need it. const FunctionProtoType *FPT = Func->getType()->getAs<FunctionProtoType>(); - if (FPT && FPT->getExceptionSpecType() == EST_Uninstantiated) - InstantiateExceptionSpec(Loc, Func); + if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) + ResolveExceptionSpec(Loc, FPT); // Implicit instantiation of function templates and member functions of // class templates. @@ -9891,14 +10367,15 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var, QualType FieldType, QualType DeclRefType, - SourceLocation Loc) { + SourceLocation Loc, + bool RefersToEnclosingLocal) { CXXRecordDecl *Lambda = LSI->Lambda; // Build the non-static data member. FieldDecl *Field = FieldDecl::Create(S.Context, Lambda, Loc, Loc, 0, FieldType, S.Context.getTrivialTypeSourceInfo(FieldType, Loc), - 0, false, false); + 0, false, ICIS_NoInit); Field->setImplicit(true); Field->setAccess(AS_private); Lambda->addDecl(Field); @@ -9920,8 +10397,8 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, // C++ [expr.prim.labda]p12: // An entity captured by a lambda-expression is odr-used (3.2) in // the scope containing the lambda-expression. - Expr *Ref = new (S.Context) DeclRefExpr(Var, false, DeclRefType, - VK_LValue, Loc); + Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal, + DeclRefType, VK_LValue, Loc); Var->setReferenced(true); Var->setUsed(true); @@ -10264,7 +10741,8 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, Expr *CopyExpr = 0; if (BuildAndDiagnose) { ExprResult Result = captureInLambda(*this, LSI, Var, CaptureType, - DeclRefType, Loc); + DeclRefType, Loc, + I == N-1); if (!Result.isInvalid()) CopyExpr = Result.take(); } @@ -10439,6 +10917,23 @@ static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, } SemaRef.MarkAnyDeclReferenced(Loc, D); + + // If this is a call to a method via a cast, also mark the method in the + // derived class used in case codegen can devirtualize the call. + const MemberExpr *ME = dyn_cast<MemberExpr>(E); + if (!ME) + return; + CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ME->getMemberDecl()); + if (!MD) + return; + const Expr *Base = ME->getBase(); + const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType(); + if (!MostDerivedClassDecl) + return; + CXXMethodDecl *DM = MD->getCorrespondingMethodInClass(MostDerivedClassDecl); + if (!DM) + return; + SemaRef.MarkAnyDeclReferenced(Loc, DM); } /// \brief Perform reference-marking and odr-use handling for a DeclRefExpr. @@ -10645,18 +11140,30 @@ bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, return false; } - PartialDiagnostic Note = - FD ? PDiag(diag::note_function_with_incomplete_return_type_declared_here) - << FD->getDeclName() : PDiag(); - SourceLocation NoteLoc = FD ? FD->getLocation() : SourceLocation(); - - if (RequireCompleteType(Loc, ReturnType, - FD ? - PDiag(diag::err_call_function_incomplete_return) - << CE->getSourceRange() << FD->getDeclName() : - PDiag(diag::err_call_incomplete_return) - << CE->getSourceRange(), - std::make_pair(NoteLoc, Note))) + class CallReturnIncompleteDiagnoser : public TypeDiagnoser { + FunctionDecl *FD; + CallExpr *CE; + + public: + CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE) + : FD(FD), CE(CE) { } + + virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) { + if (!FD) { + S.Diag(Loc, diag::err_call_incomplete_return) + << T << CE->getSourceRange(); + return; + } + + S.Diag(Loc, diag::err_call_function_incomplete_return) + << CE->getSourceRange() << FD->getDeclName() << T; + S.Diag(FD->getLocation(), + diag::note_function_with_incomplete_return_type_declared_here) + << FD->getDeclName(); + } + } Diagnoser(FD, CE); + + if (RequireCompleteType(Loc, ReturnType, Diagnoser)) return true; return false; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index af86cb2c43e1..27402599a401 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -6,9 +6,10 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This file implements semantic analysis for C++ expressions. -// +/// +/// \file +/// \brief Implements semantic analysis for C++ expressions. +/// //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" @@ -332,7 +333,7 @@ ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, // When typeid is applied to an expression other than an glvalue of a // polymorphic class type [...] [the] expression is an unevaluated // operand. [...] - if (RecordD->isPolymorphic() && E->Classify(Context).isGLValue()) { + if (RecordD->isPolymorphic() && E->isGLValue()) { // The subexpression is potentially evaluated; switch the context // and recheck the subexpression. ExprResult Result = TranformToPotentiallyEvaluated(E); @@ -375,10 +376,20 @@ Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc, LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName); LookupQualifiedName(R, getStdNamespace()); CXXTypeInfoDecl = R.getAsSingle<RecordDecl>(); + // Microsoft's typeinfo doesn't have type_info in std but in the global + // namespace if _HAS_EXCEPTIONS is defined to 0. See PR13153. + if (!CXXTypeInfoDecl && LangOpts.MicrosoftMode) { + LookupQualifiedName(R, Context.getTranslationUnitDecl()); + CXXTypeInfoDecl = R.getAsSingle<RecordDecl>(); + } if (!CXXTypeInfoDecl) return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid)); } + if (!getLangOpts().RTTI) { + return ExprError(Diag(OpLoc, diag::err_no_typeid_with_fno_rtti)); + } + QualType TypeInfoType = Context.getTypeDeclType(CXXTypeInfoDecl); if (isType) { @@ -584,14 +595,13 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, } if (!isPointer || !Ty->isVoidType()) { if (RequireCompleteType(ThrowLoc, Ty, - PDiag(isPointer ? diag::err_throw_incomplete_ptr - : diag::err_throw_incomplete) - << E->getSourceRange())) + isPointer? diag::err_throw_incomplete_ptr + : diag::err_throw_incomplete, + E->getSourceRange())) return ExprError(); if (RequireNonAbstractType(ThrowLoc, E->getType(), - PDiag(diag::err_throw_abstract_type) - << E->getSourceRange())) + diag::err_throw_abstract_type, E)) return ExprError(); } @@ -737,7 +747,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { FieldDecl *Field = FieldDecl::Create(Context, Lambda, Loc, Loc, 0, ThisTy, Context.getTrivialTypeSourceInfo(ThisTy, Loc), - 0, false, false); + 0, false, ICIS_NoInit); Field->setImplicit(true); Field->setAccess(AS_private); Lambda->addDecl(Field); @@ -839,8 +849,7 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, if (!Ty->isVoidType() && RequireCompleteType(TyBeginLoc, ElemTy, - PDiag(diag::err_invalid_incomplete_type_use) - << FullRange)) + diag::err_invalid_incomplete_type_use, FullRange)) return ExprError(); if (RequireNonAbstractType(TyBeginLoc, Ty, @@ -932,7 +941,7 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, } /// \brief Parsed a C++ 'new' expression (C++ 5.3.4). - +/// /// E.g.: /// @code new (memory) int[size][4] @endcode /// or @@ -945,10 +954,8 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, /// \param PlacementRParen Closing paren of the placement arguments. /// \param TypeIdParens If the type is in parens, the source range. /// \param D The type to be allocated, as well as array dimensions. -/// \param ConstructorLParen Opening paren of the constructor args, empty if -/// initializer-list syntax is used. -/// \param ConstructorArgs Constructor/initialization arguments. -/// \param ConstructorRParen Closing paren of the constructor args. +/// \param Initializer The initializing expression or initializer-list, or null +/// if there is none. ExprResult Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, SourceLocation PlacementLParen, MultiExprArg PlacementArgs, @@ -960,7 +967,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, // If the specified type is an array, unwrap it and save the expression. if (D.getNumTypeObjects() > 0 && D.getTypeObject(0).Kind == DeclaratorChunk::Array) { - DeclaratorChunk &Chunk = D.getTypeObject(0); + DeclaratorChunk &Chunk = D.getTypeObject(0); if (TypeContainsAuto) return ExprError(Diag(Chunk.Loc, diag::err_new_array_of_auto) << D.getSourceRange()); @@ -984,8 +991,10 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, DeclaratorChunk::ArrayTypeInfo &Array = D.getTypeObject(I).Arr; if (Expr *NumElts = (Expr *)Array.NumElts) { if (!NumElts->isTypeDependent() && !NumElts->isValueDependent()) { - Array.NumElts = VerifyIntegerConstantExpression(NumElts, 0, - PDiag(diag::err_new_array_nonconst)).take(); + Array.NumElts + = VerifyIntegerConstantExpression(NumElts, 0, + diag::err_new_array_nonconst) + .take(); if (!Array.NumElts) return ExprError(); } @@ -1084,8 +1093,10 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, } } - // C++0x [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. - if (TypeMayContainAuto && AllocType->getContainedAutoType()) { + // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. + AutoType *AT = 0; + if (TypeMayContainAuto && + (AT = AllocType->getContainedAutoType()) && !AT->isDeduced()) { if (initStyle == CXXNewExpr::NoInit || NumInits == 0) return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg) << AllocType << TypeRange); @@ -1101,8 +1112,7 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, } Expr *Deduce = Inits[0]; TypeSourceInfo *DeducedType = 0; - if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) == - DAR_Failed) + if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) == DAR_Failed) return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure) << AllocType << Deduce->getType() << TypeRange << Deduce->getSourceRange()); @@ -1150,19 +1160,64 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, // enumeration type, or a class type for which a single non-explicit // conversion function to integral or unscoped enumeration type exists. if (ArraySize && !ArraySize->isTypeDependent()) { - ExprResult ConvertedSize = ConvertToIntegralOrEnumerationType( - StartLoc, ArraySize, - PDiag(diag::err_array_size_not_integral) << getLangOpts().CPlusPlus0x, - PDiag(diag::err_array_size_incomplete_type) - << ArraySize->getSourceRange(), - PDiag(diag::err_array_size_explicit_conversion), - PDiag(diag::note_array_size_conversion), - PDiag(diag::err_array_size_ambiguous_conversion), - PDiag(diag::note_array_size_conversion), - PDiag(getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_array_size_conversion : - diag::ext_array_size_conversion), - /*AllowScopedEnumerations*/ false); + class SizeConvertDiagnoser : public ICEConvertDiagnoser { + Expr *ArraySize; + + public: + SizeConvertDiagnoser(Expr *ArraySize) + : ICEConvertDiagnoser(false, false), ArraySize(ArraySize) { } + + virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_array_size_not_integral) + << S.getLangOpts().CPlusPlus0x << T; + } + + virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_array_size_incomplete_type) + << T << ArraySize->getSourceRange(); + } + + virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, + SourceLocation Loc, + QualType T, + QualType ConvTy) { + return S.Diag(Loc, diag::err_array_size_explicit_conversion) << T << ConvTy; + } + + virtual DiagnosticBuilder noteExplicitConv(Sema &S, + CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_array_size_conversion) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_array_size_ambiguous_conversion) << T; + } + + virtual DiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_array_size_conversion) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc, + QualType T, + QualType ConvTy) { + return S.Diag(Loc, + S.getLangOpts().CPlusPlus0x + ? diag::warn_cxx98_compat_array_size_conversion + : diag::ext_array_size_conversion) + << T << ConvTy->isEnumeralType() << ConvTy; + } + } SizeDiagnoser(ArraySize); + + ExprResult ConvertedSize + = ConvertToIntegralOrEnumerationType(StartLoc, ArraySize, SizeDiagnoser, + /*AllowScopedEnumerations*/ false); if (ConvertedSize.isInvalid()) return ExprError(); @@ -1401,9 +1456,7 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, return Diag(Loc, diag::err_bad_new_type) << AllocType << 1 << R; else if (!AllocType->isDependentType() && - RequireCompleteType(Loc, AllocType, - PDiag(diag::err_new_incomplete_type) - << R)) + RequireCompleteType(Loc, AllocType, diag::err_new_incomplete_type,R)) return true; else if (RequireNonAbstractType(Loc, AllocType, diag::err_allocation_of_abstract_type)) @@ -2014,7 +2067,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, if (const RecordType *Record = Type->getAs<RecordType>()) { if (RequireCompleteType(StartLoc, Type, - PDiag(diag::err_delete_incomplete_class_type))) + diag::err_delete_incomplete_class_type)) return ExprError(); SmallVector<CXXConversionDecl*, 4> ObjectPtrConversions; @@ -2084,8 +2137,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, << Type << Ex.get()->getSourceRange()); } else if (!Pointee->isDependentType()) { if (!RequireCompleteType(StartLoc, Pointee, - PDiag(diag::warn_delete_incomplete) - << Ex.get()->getSourceRange())) { + diag::warn_delete_incomplete, Ex.get())) { if (const RecordType *RT = PointeeElem->getAs<RecordType>()) PointeeRD = cast<CXXRecordDecl>(RT->getDecl()); } @@ -2096,9 +2148,6 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, // delete-expression; it is not necessary to cast away the constness // (5.2.11) of the pointer expression before it is used as the operand // of the delete-expression. ] - if (!Context.hasSameType(Ex.get()->getType(), Context.VoidPtrTy)) - Ex = Owned(ImplicitCastExpr::Create(Context, Context.VoidPtrTy, - CK_BitCast, Ex.take(), 0, VK_RValue)); if (Pointee->isArrayType() && !ArrayForm) { Diag(StartLoc, diag::warn_delete_array_type) @@ -2176,6 +2225,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, DeclareGlobalNewDelete(); DeclContext *TUDecl = Context.getTranslationUnitDecl(); Expr *Arg = Ex.get(); + if (!Context.hasSameType(Arg->getType(), Context.VoidPtrTy)) + Arg = ImplicitCastExpr::Create(Context, Context.VoidPtrTy, + CK_BitCast, Arg, 0, VK_RValue); if (FindAllocationOverload(StartLoc, SourceRange(), DeleteName, &Arg, 1, TUDecl, /*AllowMissing=*/false, OperatorDelete)) @@ -3138,8 +3190,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); if (!CPT) return false; - if (CPT->getExceptionSpecType() == EST_Delayed) - return false; if (!CPT->isNothrow(Self.Context)) return false; } @@ -3180,8 +3230,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); if (!CPT) return false; - if (CPT->getExceptionSpecType() == EST_Delayed) - return false; // FIXME: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1) @@ -3218,8 +3266,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); if (!CPT) return false; - if (CPT->getExceptionSpecType() == EST_Delayed) - return false; // TODO: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0; @@ -3284,6 +3330,25 @@ ExprResult Sema::ActOnBinaryTypeTrait(BinaryTypeTrait BTT, return BuildBinaryTypeTrait(BTT, KWLoc, LhsTSInfo, RhsTSInfo, RParen); } +/// \brief Determine whether T has a non-trivial Objective-C lifetime in +/// ARC mode. +static bool hasNontrivialObjCLifetime(QualType T) { + switch (T.getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return false; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return true; + + case Qualifiers::OCL_None: + return T->isObjCLifetimeType(); + } + + llvm_unreachable("Unknown ObjC lifetime qualifier"); +} + static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, ArrayRef<TypeSourceInfo *> Args, SourceLocation RParenLoc) { @@ -3357,8 +3422,14 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, ArgExprs.size())); if (Result.isInvalid() || SFINAE.hasErrorOccurred()) return false; - - // The initialization succeeded; not make sure there are no non-trivial + + // Under Objective-C ARC, if the destination has non-trivial Objective-C + // lifetime, this is a non-trivial construction. + if (S.getLangOpts().ObjCAutoRefCount && + hasNontrivialObjCLifetime(Args[0]->getType().getNonReferenceType())) + return false; + + // The initialization succeeded; now make sure there are no non-trivial // calls. return !Result.get()->hasNonTrivialCall(S.Context); } @@ -3471,9 +3542,25 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT, // We model the initialization as a copy-initialization of a temporary // of the appropriate type, which for this expression is identical to the // return statement (since NRVO doesn't apply). + + // Functions aren't allowed to return function or array types. + if (RhsT->isFunctionType() || RhsT->isArrayType()) + return false; + + // A return statement in a void function must have void type. + if (RhsT->isVoidType()) + return LhsT->isVoidType(); + + // A function definition requires a complete, non-abstract return type. + if (Self.RequireCompleteType(KeyLoc, RhsT, 0) || + Self.RequireNonAbstractType(KeyLoc, RhsT, 0)) + return false; + + // Compute the result of add_rvalue_reference. if (LhsT->isObjectType() || LhsT->isFunctionType()) LhsT = Self.Context.getRValueReferenceType(LhsT); - + + // Build a fake source and destination for initialization. InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT)); OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context), Expr::getValueKindForType(LhsT)); @@ -3539,6 +3626,12 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT, if (Result.isInvalid() || SFINAE.hasErrorOccurred()) return false; + // Under Objective-C ARC, if the destination has non-trivial Objective-C + // lifetime, this is a non-trivial assignment. + if (Self.getLangOpts().ObjCAutoRefCount && + hasNontrivialObjCLifetime(LhsT.getNonReferenceType())) + return false; + return !Result.get()->hasNonTrivialCall(Self.Context); } } @@ -3615,7 +3708,7 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT, llvm::APSInt Value; uint64_t Dim; if (Self.VerifyIntegerConstantExpression(DimExpr, &Value, - Self.PDiag(diag::err_dimension_expr_not_constant_integer), + diag::err_dimension_expr_not_constant_integer, false).isInvalid()) return 0; if (Value.isSigned() && Value.isNegative()) { @@ -3767,8 +3860,8 @@ QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS, if (!Context.hasSameUnqualifiedType(Class, LHSType)) { // If we want to check the hierarchy, we need a complete type. - if (RequireCompleteType(Loc, LHSType, PDiag(diag::err_bad_memptr_lhs) - << OpSpelling << (int)isIndirect)) { + if (RequireCompleteType(Loc, LHSType, diag::err_bad_memptr_lhs, + OpSpelling, (int)isIndirect)) { return QualType(); } CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, @@ -4023,13 +4116,14 @@ static bool ConvertForConditional(Sema &Self, ExprResult &E, QualType T) { /// /// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y /// extension. In this case, LHS == Cond. (But they're not aliases.) -QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, ExprResult &RHS, - ExprValueKind &VK, ExprObjectKind &OK, +QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, + ExprResult &RHS, ExprValueKind &VK, + ExprObjectKind &OK, SourceLocation QuestionLoc) { // FIXME: Handle C99's complex types, vector types, block pointers and Obj-C++ // interface pointers. - // C++0x 5.16p1 + // C++11 [expr.cond]p1 // The first expression is contextually converted to bool. if (!Cond.get()->isTypeDependent()) { ExprResult CondRes = CheckCXXBooleanCondition(Cond.take()); @@ -4046,7 +4140,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex if (LHS.get()->isTypeDependent() || RHS.get()->isTypeDependent()) return Context.DependentTy; - // C++0x 5.16p2 + // C++11 [expr.cond]p2 // If either the second or the third operand has type (cv) void, ... QualType LTy = LHS.get()->getType(); QualType RTy = RHS.get()->getType(); @@ -4059,12 +4153,26 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex RHS = DefaultFunctionArrayLvalueConversion(RHS.take()); if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); + + // Finish off the lvalue-to-rvalue conversion by copy-initializing a + // temporary if necessary. DefaultFunctionArrayLvalueConversion doesn't + // do this part for us. + ExprResult &NonVoid = LVoid ? RHS : LHS; + if (NonVoid.get()->getType()->isRecordType() && + NonVoid.get()->isGLValue()) { + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(NonVoid.get()->getType()); + NonVoid = PerformCopyInitialization(Entity, SourceLocation(), NonVoid); + if (NonVoid.isInvalid()) + return QualType(); + } + LTy = LHS.get()->getType(); RTy = RHS.get()->getType(); // ... and one of the following shall hold: // -- The second or the third operand (but not both) is a throw- - // expression; the result is of the type of the other and is an rvalue. + // expression; the result is of the type of the other and is a prvalue. bool LThrow = isa<CXXThrowExpr>(LHS.get()); bool RThrow = isa<CXXThrowExpr>(RHS.get()); if (LThrow && !RThrow) @@ -4073,7 +4181,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex return LTy; // -- Both the second and third operands have type void; the result is of - // type void and is an rvalue. + // type void and is a prvalue. if (LVoid && RVoid) return Context.VoidTy; @@ -4086,10 +4194,10 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex // Neither is void. - // C++0x 5.16p3 + // C++11 [expr.cond]p3 // Otherwise, if the second and third operand have different types, and - // either has (cv) class type, and attempt is made to convert each of those - // operands to the other. + // either has (cv) class type [...] an attempt is made to convert each of + // those operands to the type of the other. if (!Context.hasSameType(LTy, RTy) && (LTy->isRecordType() || RTy->isRecordType())) { ImplicitConversionSequence ICSLeftToRight, ICSRightToLeft; @@ -4122,7 +4230,31 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex } } - // C++0x 5.16p4 + // C++11 [expr.cond]p3 + // if both are glvalues of the same value category and the same type except + // for cv-qualification, an attempt is made to convert each of those + // operands to the type of the other. + ExprValueKind LVK = LHS.get()->getValueKind(); + ExprValueKind RVK = RHS.get()->getValueKind(); + if (!Context.hasSameType(LTy, RTy) && + Context.hasSameUnqualifiedType(LTy, RTy) && + LVK == RVK && LVK != VK_RValue) { + // Since the unqualified types are reference-related and we require the + // result to be as if a reference bound directly, the only conversion + // we can perform is to add cv-qualifiers. + Qualifiers LCVR = Qualifiers::fromCVRMask(LTy.getCVRQualifiers()); + Qualifiers RCVR = Qualifiers::fromCVRMask(RTy.getCVRQualifiers()); + if (RCVR.isStrictSupersetOf(LCVR)) { + LHS = ImpCastExprToType(LHS.take(), RTy, CK_NoOp, LVK); + LTy = LHS.get()->getType(); + } + else if (LCVR.isStrictSupersetOf(RCVR)) { + RHS = ImpCastExprToType(RHS.take(), LTy, CK_NoOp, RVK); + RTy = RHS.get()->getType(); + } + } + + // C++11 [expr.cond]p4 // If the second and third operands are glvalues of the same value // category and have the same type, the result is of that type and // value category and it is a bit-field if the second or the third @@ -4130,9 +4262,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex // We only extend this to bitfields, not to the crazy other kinds of // l-values. bool Same = Context.hasSameType(LTy, RTy); - if (Same && - LHS.get()->isGLValue() && - LHS.get()->getValueKind() == RHS.get()->getValueKind() && + if (Same && LVK == RVK && LVK != VK_RValue && LHS.get()->isOrdinaryOrBitFieldObject() && RHS.get()->isOrdinaryOrBitFieldObject()) { VK = LHS.get()->getValueKind(); @@ -4142,8 +4272,8 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex return LTy; } - // C++0x 5.16p5 - // Otherwise, the result is an rvalue. If the second and third operands + // C++11 [expr.cond]p5 + // Otherwise, the result is a prvalue. If the second and third operands // do not have the same type, and either has (cv) class type, ... if (!Same && (LTy->isRecordType() || RTy->isRecordType())) { // ... overload resolution is used to determine the conversions (if any) @@ -4153,8 +4283,8 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex return QualType(); } - // C++0x 5.16p6 - // LValue-to-rvalue, array-to-pointer, and function-to-pointer standard + // C++11 [expr.cond]p6 + // Lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard // conversions are performed on the second and third operands. LHS = DefaultFunctionArrayLvalueConversion(LHS.take()); RHS = DefaultFunctionArrayLvalueConversion(RHS.take()); @@ -4207,9 +4337,11 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex } // -- The second and third operands have pointer type, or one has pointer - // type and the other is a null pointer constant; pointer conversions - // and qualification conversions are performed to bring them to their - // composite pointer type. The result is of the composite pointer type. + // type and the other is a null pointer constant, or both are null + // pointer constants, at least one of which is non-integral; pointer + // conversions and qualification conversions are performed to bring them + // to their composite pointer type. The result is of the composite + // pointer type. // -- The second and third operands have pointer to member type, or one has // pointer to member type and the other is a null pointer constant; // pointer to member conversions and qualification conversions are @@ -4247,7 +4379,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex /// \brief Find a merged pointer type and convert the two expressions to it. /// /// This finds the composite pointer type (or member pointer type) for @p E1 -/// and @p E2 according to C++0x 5.9p2. It converts both expressions to this +/// and @p E2 according to C++11 5.9p2. It converts both expressions to this /// type and returns it. /// It does not emit diagnostics. /// @@ -4267,15 +4399,27 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, assert(getLangOpts().CPlusPlus && "This function assumes C++"); QualType T1 = E1->getType(), T2 = E2->getType(); - if (!T1->isAnyPointerType() && !T1->isMemberPointerType() && - !T2->isAnyPointerType() && !T2->isMemberPointerType()) - return QualType(); - - // C++0x 5.9p2 + // C++11 5.9p2 // Pointer conversions and qualification conversions are performed on // pointer operands to bring them to their composite pointer type. If // one operand is a null pointer constant, the composite pointer type is - // the type of the other operand. + // std::nullptr_t if the other operand is also a null pointer constant or, + // if the other operand is a pointer, the type of the other operand. + if (!T1->isAnyPointerType() && !T1->isMemberPointerType() && + !T2->isAnyPointerType() && !T2->isMemberPointerType()) { + if (T1->isNullPtrType() && + E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { + E2 = ImpCastExprToType(E2, T1, CK_NullToPointer).take(); + return T1; + } + if (T2->isNullPtrType() && + E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { + E1 = ImpCastExprToType(E1, T2, CK_NullToPointer).take(); + return T2; + } + return QualType(); + } + if (E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { if (T2->isMemberPointerType()) E1 = ImpCastExprToType(E1, T2, CK_NullToMemberPointer).take(); @@ -4522,8 +4666,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { ObjCMethodDecl *D = 0; if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) { D = Send->getMethodDecl(); - } else if (ObjCNumericLiteral *NumLit = dyn_cast<ObjCNumericLiteral>(E)) { - D = NumLit->getObjCNumericLiteralMethod(); + } else if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) { + D = BoxedExpr->getBoxingMethod(); } else if (ObjCArrayLiteral *ArrayLit = dyn_cast<ObjCArrayLiteral>(E)) { D = ArrayLit->getArrayWithObjectsMethod(); } else if (ObjCDictionaryLiteral *DictLit @@ -4706,6 +4850,11 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) { // Disable the special decltype handling now. Rec.IsDecltype = false; + // In MS mode, don't perform any extra checking of call return types within a + // decltype expression. + if (getLangOpts().MicrosoftMode) + return Owned(E); + // Perform the semantic checks we delayed until this point. CallExpr *TopCall = dyn_cast<CallExpr>(E); for (unsigned I = 0, N = Rec.DelayedDecltypeCalls.size(); I != N; ++I) { @@ -4733,11 +4882,11 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) { CXXDestructorDecl *Destructor = LookupDestructor(RD); Temp->setDestructor(Destructor); - MarkFunctionReferenced(E->getExprLoc(), Destructor); - CheckDestructorAccess(E->getExprLoc(), Destructor, + MarkFunctionReferenced(Bind->getExprLoc(), Destructor); + CheckDestructorAccess(Bind->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_temp) - << E->getType()); - DiagnoseUseOfDecl(Destructor, E->getExprLoc()); + << Bind->getType()); + DiagnoseUseOfDecl(Destructor, Bind->getExprLoc()); // We need a cleanup, but we don't need to remember the temporary. ExprNeedsCleanups = true; @@ -4833,8 +4982,7 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, // the member function body. if (!BaseType->isDependentType() && !isThisOutsideMemberFunctionBody(BaseType) && - RequireCompleteType(OpLoc, BaseType, - PDiag(diag::err_incomplete_member_access))) + RequireCompleteType(OpLoc, BaseType, diag::err_incomplete_member_access)) return ExprError(); // C++ [basic.lookup.classref]p2: @@ -5222,6 +5370,61 @@ ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation, return BuildCXXNoexceptExpr(KeyLoc, Operand, RParen); } +static bool IsSpecialDiscardedValue(Expr *E) { + // In C++11, discarded-value expressions of a certain form are special, + // according to [expr]p10: + // The lvalue-to-rvalue conversion (4.1) is applied only if the + // expression is an lvalue of volatile-qualified type and it has + // one of the following forms: + E = E->IgnoreParens(); + + // - id-expression (5.1.1), + if (isa<DeclRefExpr>(E)) + return true; + + // - subscripting (5.2.1), + if (isa<ArraySubscriptExpr>(E)) + return true; + + // - class member access (5.2.5), + if (isa<MemberExpr>(E)) + return true; + + // - indirection (5.3.1), + if (UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) + if (UO->getOpcode() == UO_Deref) + return true; + + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { + // - pointer-to-member operation (5.5), + if (BO->isPtrMemOp()) + return true; + + // - comma expression (5.18) where the right operand is one of the above. + if (BO->getOpcode() == BO_Comma) + return IsSpecialDiscardedValue(BO->getRHS()); + } + + // - conditional expression (5.16) where both the second and the third + // operands are one of the above, or + if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) + return IsSpecialDiscardedValue(CO->getTrueExpr()) && + IsSpecialDiscardedValue(CO->getFalseExpr()); + // The related edge case of "*x ?: *x". + if (BinaryConditionalOperator *BCO = + dyn_cast<BinaryConditionalOperator>(E)) { + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(BCO->getTrueExpr())) + return IsSpecialDiscardedValue(OVE->getSourceExpr()) && + IsSpecialDiscardedValue(BCO->getFalseExpr()); + } + + // Objective-C++ extensions to the rule. + if (isa<PseudoObjectExpr>(E) || isa<ObjCIvarRefExpr>(E)) + return true; + + return false; +} + /// Perform the conversions required for an expression used in a /// context that ignores the result. ExprResult Sema::IgnoredValueConversions(Expr *E) { @@ -5246,8 +5449,21 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { return Owned(E); } - // Otherwise, this rule does not apply in C++, at least not for the moment. - if (getLangOpts().CPlusPlus) return Owned(E); + if (getLangOpts().CPlusPlus) { + // The C++11 standard defines the notion of a discarded-value expression; + // normally, we don't need to do anything to handle it, but if it is a + // volatile lvalue with a special form, we perform an lvalue-to-rvalue + // conversion. + if (getLangOpts().CPlusPlus0x && E->isGLValue() && + E->getType().isVolatileQualified() && + IsSpecialDiscardedValue(E)) { + ExprResult Res = DefaultLvalueConversion(E); + if (Res.isInvalid()) + return Owned(E); + E = Res.take(); + } + return Owned(E); + } // GCC seems to also exclude expressions of incomplete enum type. if (const EnumType *T = E->getType()->getAs<EnumType>()) { @@ -5269,7 +5485,7 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { return Owned(E); } -ExprResult Sema::ActOnFinishFullExpr(Expr *FE) { +ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC) { ExprResult FullExpr = Owned(FE); if (!FullExpr.get()) @@ -5295,7 +5511,7 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE) { if (FullExpr.isInvalid()) return ExprError(); - CheckImplicitConversions(FullExpr.get(), FullExpr.get()->getExprLoc()); + CheckImplicitConversions(FullExpr.get(), CC); return MaybeCreateExprWithCleanups(FullExpr); } diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 6c84caa3f371..53f22f65ddf5 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -115,7 +115,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, NamedDecl *D = *I; if (D->isCXXInstanceMember()) { - if (dyn_cast<FieldDecl>(D)) + if (dyn_cast<FieldDecl>(D) || dyn_cast<IndirectFieldDecl>(D)) isField = true; CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext()); @@ -436,8 +436,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, if (PT && (!getLangOpts().ObjC1 || PT->getPointeeType()->isRecordType())) { assert(BaseExpr && "cannot happen with implicit member accesses"); - Diag(NameInfo.getLoc(), diag::err_typecheck_member_reference_struct_union) - << BaseType << BaseExpr->getSourceRange(); + Diag(OpLoc, diag::err_typecheck_member_reference_struct_union) + << BaseType << BaseExpr->getSourceRange() << NameInfo.getSourceRange(); return ExprError(); } } @@ -548,8 +548,8 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, RecordDecl *RDecl = RTy->getDecl(); if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) && SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), - SemaRef.PDiag(diag::err_typecheck_incomplete_tag) - << BaseRange)) + diag::err_typecheck_incomplete_tag, + BaseRange)) return true; if (HasTemplateArgs) { @@ -813,8 +813,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierInScope, LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs, - bool SuppressQualifierCheck) { + const TemplateArgumentListInfo *TemplateArgs, + bool SuppressQualifierCheck, + ActOnMemberAccessExtraArgs *ExtraArgs) { QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); @@ -835,6 +836,32 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, ? computeDeclContext(SS, false) : BaseType->getAs<RecordType>()->getDecl()); + if (ExtraArgs) { + ExprResult RetryExpr; + if (!IsArrow && BaseExpr) { + SFINAETrap Trap(*this, true); + ParsedType ObjectType; + bool MayBePseudoDestructor = false; + RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr, + OpLoc, tok::arrow, ObjectType, + MayBePseudoDestructor); + if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) { + CXXScopeSpec TempSS(SS); + RetryExpr = ActOnMemberAccessExpr( + ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS, + TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl, + ExtraArgs->HasTrailingLParen); + } + if (Trap.hasErrorOccurred()) + RetryExpr = ExprError(); + } + if (RetryExpr.isUsable()) { + Diag(OpLoc, diag::err_no_member_overloaded_arrow) + << MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->"); + return RetryExpr; + } + } + Diag(R.getNameLoc(), diag::err_no_member) << MemberName << DC << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); @@ -1122,10 +1149,22 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, ObjCImpDecl, HasTemplateArgs); goto fail; } - - if (RequireCompleteType(OpLoc, BaseType, - PDiag(diag::err_typecheck_incomplete_tag) - << BaseExpr.get()->getSourceRange())) + else if (Member && Member->isStr("isa")) { + // If an ivar is (1) the first ivar in a root class and (2) named `isa`, + // then issue the same deprecated warning that id->isa gets. + ObjCInterfaceDecl *ClassDeclared = 0; + if (ObjCIvarDecl *IV = + IDecl->lookupInstanceVariable(Member, ClassDeclared)) { + if (!ClassDeclared->getSuperClass() + && (*ClassDeclared->ivar_begin()) == IV) { + Diag(MemberLoc, diag::warn_objc_isa_use); + Diag(IV->getLocation(), diag::note_ivar_decl); + } + } + } + + if (RequireCompleteType(OpLoc, BaseType, diag::err_typecheck_incomplete_tag, + BaseExpr.get())) return ExprError(); ObjCInterfaceDecl *ClassDeclared = 0; @@ -1211,6 +1250,7 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, << IV->getDeclName(); } } + bool warn = true; if (getLangOpts().ObjCAutoRefCount) { Expr *BaseExp = BaseExpr.get()->IgnoreParenImpCasts(); if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BaseExp)) @@ -1218,10 +1258,20 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, BaseExp = UO->getSubExpr()->IgnoreParenCasts(); if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(BaseExp)) - if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak) + if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { Diag(DE->getLocation(), diag::error_arc_weak_ivar_access); + warn = false; + } + } + if (warn) { + if (ObjCMethodDecl *MD = getCurMethodDecl()) { + ObjCMethodFamily MF = MD->getMethodFamily(); + warn = (MF != OMF_init && MF != OMF_dealloc && + MF != OMF_finalize); + } + if (warn) + Diag(MemberLoc, diag::warn_direct_ivar_access) << IV->getDeclName(); } - return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(), MemberLoc, BaseExpr.take(), IsArrow)); @@ -1327,9 +1377,6 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, // methods. Setter = IFace->lookupPrivateMethod(SetterSel, false); } - // Look through local category implementations associated with the class. - if (!Setter) - Setter = IFace->getCategoryClassMethod(SetterSel); if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) return ExprError(); @@ -1418,8 +1465,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, ObjCImpDecl, HasTemplateArgs); } - Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union) - << BaseType << BaseExpr.get()->getSourceRange(); + Diag(OpLoc, diag::err_typecheck_member_reference_struct_union) + << BaseType << BaseExpr.get()->getSourceRange() << MemberLoc; return ExprError(); } @@ -1434,9 +1481,9 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, /// \param HasTrailingLParen whether the next token is '(', which /// is used to diagnose mis-uses of special members that can /// only be called -/// \param ObjCImpDecl the current ObjC @implementation decl; -/// this is an ugly hack around the fact that ObjC @implementations -/// aren't properly put in the context chain +/// \param ObjCImpDecl the current Objective-C \@implementation +/// decl; this is an ugly hack around the fact that Objective-C +/// \@implementations aren't properly put in the context chain ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, SourceLocation OpLoc, tok::TokenKind OpKind, @@ -1506,9 +1553,11 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, return move(Result); } + ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl, HasTrailingLParen}; Result = BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, - FirstQualifierInScope, R, TemplateArgs); + FirstQualifierInScope, R, TemplateArgs, + false, &ExtraArgs); } return move(Result); @@ -1563,6 +1612,8 @@ BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, MemberType = S.Context.getQualifiedType(MemberType, Combined); } + S.UnusedPrivateFields.remove(Field); + ExprResult Base = S.PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(), FoundDecl, Field); diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index b62d56efdab6..0aabf8b634d1 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -111,7 +111,7 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ Ty = Context.getObjCIdType(); } } else { - IdentifierInfo *NSIdent = &Context.Idents.get("NSString"); + IdentifierInfo *NSIdent = NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, LookupOrdinaryName); if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { @@ -140,20 +140,47 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ return new (Context) ObjCStringLiteral(S, Ty, AtLoc); } +/// \brief Emits an error if the given method does not exist, or if the return +/// type is not an Objective-C object. +static bool validateBoxingMethod(Sema &S, SourceLocation Loc, + const ObjCInterfaceDecl *Class, + Selector Sel, const ObjCMethodDecl *Method) { + if (!Method) { + // FIXME: Is there a better way to avoid quotes than using getName()? + S.Diag(Loc, diag::err_undeclared_boxing_method) << Sel << Class->getName(); + return false; + } + + // Make sure the return type is reasonable. + QualType ReturnType = Method->getResultType(); + if (!ReturnType->isObjCObjectPointerType()) { + S.Diag(Loc, diag::err_objc_literal_method_sig) + << Sel; + S.Diag(Method->getLocation(), diag::note_objc_literal_method_return) + << ReturnType; + return false; + } + + return true; +} + /// \brief Retrieve the NSNumber factory method that should be used to create /// an Objective-C literal for the given type. static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, - QualType T, QualType ReturnType, - SourceRange Range) { + QualType NumberType, + bool isLiteral = false, + SourceRange R = SourceRange()) { llvm::Optional<NSAPI::NSNumberLiteralMethodKind> Kind - = S.NSAPIObj->getNSNumberFactoryMethodKind(T); + = S.NSAPIObj->getNSNumberFactoryMethodKind(NumberType); if (!Kind) { - S.Diag(Loc, diag::err_invalid_nsnumber_type) - << T << Range; + if (isLiteral) { + S.Diag(Loc, diag::err_invalid_nsnumber_type) + << NumberType << R; + } return 0; } - + // If we already looked up this method, we're done. if (S.NSNumberLiteralMethods[*Kind]) return S.NSNumberLiteralMethods[*Kind]; @@ -161,39 +188,62 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, Selector Sel = S.NSAPIObj->getNSNumberLiteralSelector(*Kind, /*Instance=*/false); + ASTContext &CX = S.Context; + + // Look up the NSNumber class, if we haven't done so already. It's cached + // in the Sema instance. + if (!S.NSNumberDecl) { + IdentifierInfo *NSNumberId = + S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber); + NamedDecl *IF = S.LookupSingleName(S.TUScope, NSNumberId, + Loc, Sema::LookupOrdinaryName); + S.NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + if (!S.NSNumberDecl) { + if (S.getLangOpts().DebuggerObjCLiteral) { + // Create a stub definition of NSNumber. + S.NSNumberDecl = ObjCInterfaceDecl::Create(CX, + CX.getTranslationUnitDecl(), + SourceLocation(), NSNumberId, + 0, SourceLocation()); + } else { + // Otherwise, require a declaration of NSNumber. + S.Diag(Loc, diag::err_undeclared_nsnumber); + return 0; + } + } else if (!S.NSNumberDecl->hasDefinition()) { + S.Diag(Loc, diag::err_undeclared_nsnumber); + return 0; + } + + // generate the pointer to NSNumber type. + QualType NSNumberObject = CX.getObjCInterfaceType(S.NSNumberDecl); + S.NSNumberPointer = CX.getObjCObjectPointerType(NSNumberObject); + } + // Look for the appropriate method within NSNumber. - ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel);; + ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel); if (!Method && S.getLangOpts().DebuggerObjCLiteral) { + // create a stub definition this NSNumber factory method. TypeSourceInfo *ResultTInfo = 0; - Method = ObjCMethodDecl::Create(S.Context, SourceLocation(), SourceLocation(), Sel, - ReturnType, - ResultTInfo, - S.Context.getTranslationUnitDecl(), - false /*Instance*/, false/*isVariadic*/, - /*isSynthesized=*/false, - /*isImplicitlyDeclared=*/true, /*isDefined=*/false, - ObjCMethodDecl::Required, - false); + Method = ObjCMethodDecl::Create(CX, SourceLocation(), SourceLocation(), Sel, + S.NSNumberPointer, ResultTInfo, + S.NSNumberDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, + ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); ParmVarDecl *value = ParmVarDecl::Create(S.Context, Method, SourceLocation(), SourceLocation(), - &S.Context.Idents.get("value"), - T, /*TInfo=*/0, SC_None, SC_None, 0); + &CX.Idents.get("value"), + NumberType, /*TInfo=*/0, SC_None, + SC_None, 0); Method->setMethodParams(S.Context, value, ArrayRef<SourceLocation>()); } - if (!Method) { - S.Diag(Loc, diag::err_undeclared_nsnumber_method) << Sel; - return 0; - } - - // Make sure the return type is reasonable. - if (!Method->getResultType()->isObjCObjectPointerType()) { - S.Diag(Loc, diag::err_objc_literal_method_sig) - << Sel; - S.Diag(Method->getLocation(), diag::note_objc_literal_method_return) - << Method->getResultType(); + if (!validateBoxingMethod(S, Loc, S.NSNumberDecl, Sel, Method)) return 0; - } // Note: if the parameter type is out-of-line, we'll catch it later in the // implicit conversion. @@ -202,29 +252,9 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, return Method; } -/// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the -/// numeric literal expression. Type of the expression will be "NSNumber *" -/// or "id" if NSNumber is unavailable. +/// BuildObjCNumericLiteral - builds an ObjCBoxedExpr AST node for the +/// numeric literal expression. Type of the expression will be "NSNumber *". ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { - // Look up the NSNumber class, if we haven't done so already. - if (!NSNumberDecl) { - NamedDecl *IF = LookupSingleName(TUScope, - NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber), - AtLoc, LookupOrdinaryName); - NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); - - if (!NSNumberDecl && getLangOpts().DebuggerObjCLiteral) - NSNumberDecl = ObjCInterfaceDecl::Create (Context, - Context.getTranslationUnitDecl(), - SourceLocation(), - NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber), - 0, SourceLocation()); - if (!NSNumberDecl) { - Diag(AtLoc, diag::err_undeclared_nsnumber); - return ExprError(); - } - } - // Determine the type of the literal. QualType NumberType = Number->getType(); if (CharacterLiteral *Char = dyn_cast<CharacterLiteral>(Number)) { @@ -249,29 +279,29 @@ ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { } } - ObjCMethodDecl *Method = 0; // Look for the appropriate method within NSNumber. // Construct the literal. - QualType Ty - = Context.getObjCObjectPointerType( - Context.getObjCInterfaceType(NSNumberDecl)); - Method = getNSNumberFactoryMethod(*this, AtLoc, - NumberType, Ty, - Number->getSourceRange()); - + SourceRange NR(Number->getSourceRange()); + ObjCMethodDecl *Method = getNSNumberFactoryMethod(*this, AtLoc, NumberType, + true, NR); if (!Method) return ExprError(); // Convert the number to the type that the parameter expects. - QualType ElementT = Method->param_begin()[0]->getType(); - ExprResult ConvertedNumber = PerformImplicitConversion(Number, ElementT, - AA_Sending); + ParmVarDecl *ParamDecl = Method->param_begin()[0]; + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + ParamDecl); + ExprResult ConvertedNumber = PerformCopyInitialization(Entity, + SourceLocation(), + Owned(Number)); if (ConvertedNumber.isInvalid()) return ExprError(); Number = ConvertedNumber.get(); + // Use the effective source range of the literal, including the leading '@'. return MaybeBindToTemporary( - new (Context) ObjCNumericLiteral(Number, Ty, Method, AtLoc)); + new (Context) ObjCBoxedExpr(Number, NSNumberPointer, Method, + SourceRange(AtLoc, NR.getEnd()))); } ExprResult Sema::ActOnObjCBoolLiteral(SourceLocation AtLoc, @@ -308,9 +338,11 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, // type. if (S.getLangOpts().CPlusPlus && Element->getType()->isRecordType()) { InitializedEntity Entity - = InitializedEntity::InitializeParameter(S.Context, T, /*Consumed=*/false); + = InitializedEntity::InitializeParameter(S.Context, T, + /*Consumed=*/false); InitializationKind Kind - = InitializationKind::CreateCopy(Element->getLocStart(), SourceLocation()); + = InitializationKind::CreateCopy(Element->getLocStart(), + SourceLocation()); InitializationSequence Seq(S, Entity, Kind, &Element, 1); if (!Seq.Failed()) return Seq.Perform(S, Entity, Kind, MultiExprArg(S, &Element, 1)); @@ -385,26 +417,191 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, Element->getLocStart(), Element); } +ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { + if (ValueExpr->isTypeDependent()) { + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, Context.DependentTy, NULL, SR); + return Owned(BoxedExpr); + } + ObjCMethodDecl *BoxingMethod = NULL; + QualType BoxedType; + // Convert the expression to an RValue, so we can check for pointer types... + ExprResult RValue = DefaultFunctionArrayLvalueConversion(ValueExpr); + if (RValue.isInvalid()) { + return ExprError(); + } + ValueExpr = RValue.get(); + QualType ValueType(ValueExpr->getType()); + if (const PointerType *PT = ValueType->getAs<PointerType>()) { + QualType PointeeType = PT->getPointeeType(); + if (Context.hasSameUnqualifiedType(PointeeType, Context.CharTy)) { + + if (!NSStringDecl) { + IdentifierInfo *NSStringId = + NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); + NamedDecl *Decl = LookupSingleName(TUScope, NSStringId, + SR.getBegin(), LookupOrdinaryName); + NSStringDecl = dyn_cast_or_null<ObjCInterfaceDecl>(Decl); + if (!NSStringDecl) { + if (getLangOpts().DebuggerObjCLiteral) { + // Support boxed expressions in the debugger w/o NSString declaration. + DeclContext *TU = Context.getTranslationUnitDecl(); + NSStringDecl = ObjCInterfaceDecl::Create(Context, TU, + SourceLocation(), + NSStringId, + 0, SourceLocation()); + } else { + Diag(SR.getBegin(), diag::err_undeclared_nsstring); + return ExprError(); + } + } else if (!NSStringDecl->hasDefinition()) { + Diag(SR.getBegin(), diag::err_undeclared_nsstring); + return ExprError(); + } + assert(NSStringDecl && "NSStringDecl should not be NULL"); + QualType NSStringObject = Context.getObjCInterfaceType(NSStringDecl); + NSStringPointer = Context.getObjCObjectPointerType(NSStringObject); + } + + if (!StringWithUTF8StringMethod) { + IdentifierInfo *II = &Context.Idents.get("stringWithUTF8String"); + Selector stringWithUTF8String = Context.Selectors.getUnarySelector(II); + + // Look for the appropriate method within NSString. + BoxingMethod = NSStringDecl->lookupClassMethod(stringWithUTF8String); + if (!BoxingMethod && getLangOpts().DebuggerObjCLiteral) { + // Debugger needs to work even if NSString hasn't been defined. + TypeSourceInfo *ResultTInfo = 0; + ObjCMethodDecl *M = + ObjCMethodDecl::Create(Context, SourceLocation(), SourceLocation(), + stringWithUTF8String, NSStringPointer, + ResultTInfo, NSStringDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, + ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); + QualType ConstCharType = Context.CharTy.withConst(); + ParmVarDecl *value = + ParmVarDecl::Create(Context, M, + SourceLocation(), SourceLocation(), + &Context.Idents.get("value"), + Context.getPointerType(ConstCharType), + /*TInfo=*/0, + SC_None, SC_None, 0); + M->setMethodParams(Context, value, ArrayRef<SourceLocation>()); + BoxingMethod = M; + } + + if (!validateBoxingMethod(*this, SR.getBegin(), NSStringDecl, + stringWithUTF8String, BoxingMethod)) + return ExprError(); + + StringWithUTF8StringMethod = BoxingMethod; + } + + BoxingMethod = StringWithUTF8StringMethod; + BoxedType = NSStringPointer; + } + } else if (ValueType->isBuiltinType()) { + // The other types we support are numeric, char and BOOL/bool. We could also + // provide limited support for structure types, such as NSRange, NSRect, and + // NSSize. See NSValue (NSValueGeometryExtensions) in <Foundation/NSGeometry.h> + // for more details. + + // Check for a top-level character literal. + if (const CharacterLiteral *Char = + dyn_cast<CharacterLiteral>(ValueExpr->IgnoreParens())) { + // In C, character literals have type 'int'. That's not the type we want + // to use to determine the Objective-c literal kind. + switch (Char->getKind()) { + case CharacterLiteral::Ascii: + ValueType = Context.CharTy; + break; + + case CharacterLiteral::Wide: + ValueType = Context.getWCharType(); + break; + + case CharacterLiteral::UTF16: + ValueType = Context.Char16Ty; + break; + + case CharacterLiteral::UTF32: + ValueType = Context.Char32Ty; + break; + } + } + + // FIXME: Do I need to do anything special with BoolTy expressions? + + // Look for the appropriate method within NSNumber. + BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), ValueType); + BoxedType = NSNumberPointer; + + } else if (const EnumType *ET = ValueType->getAs<EnumType>()) { + if (!ET->getDecl()->isComplete()) { + Diag(SR.getBegin(), diag::err_objc_incomplete_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), + ET->getDecl()->getIntegerType()); + BoxedType = NSNumberPointer; + } + + if (!BoxingMethod) { + Diag(SR.getBegin(), diag::err_objc_illegal_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + // Convert the expression to the type that the parameter requires. + ParmVarDecl *ParamDecl = BoxingMethod->param_begin()[0]; + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + ParamDecl); + ExprResult ConvertedValueExpr = PerformCopyInitialization(Entity, + SourceLocation(), + Owned(ValueExpr)); + if (ConvertedValueExpr.isInvalid()) + return ExprError(); + ValueExpr = ConvertedValueExpr.get(); + + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, BoxedType, + BoxingMethod, SR); + return MaybeBindToTemporary(BoxedExpr); +} + +/// Build an ObjC subscript pseudo-object expression, given that +/// that's supported by the runtime. ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, Expr *IndexExpr, ObjCMethodDecl *getterMethod, ObjCMethodDecl *setterMethod) { - // Feature support is for modern abi. - if (!LangOpts.ObjCNonFragileABI) - return ExprError(); - // If the expression is type-dependent, there's nothing for us to do. - assert ((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && - "base or index cannot have dependent type here"); + assert(!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic()); + + // We can't get dependent types here; our callers should have + // filtered them out. + assert((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && + "base or index cannot have dependent type here"); + + // Filter out placeholders in the index. In theory, overloads could + // be preserved here, although that might not actually work correctly. ExprResult Result = CheckPlaceholderExpr(IndexExpr); if (Result.isInvalid()) return ExprError(); IndexExpr = Result.get(); - // Perform lvalue-to-rvalue conversion. + // Perform lvalue-to-rvalue conversion on the base. Result = DefaultLvalueConversion(BaseExpr); if (Result.isInvalid()) return ExprError(); BaseExpr = Result.get(); + + // Build the pseudo-object expression. return Owned(ObjCSubscriptRefExpr::Create(Context, BaseExpr, IndexExpr, @@ -440,11 +637,10 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { if (!ArrayWithObjectsMethod) { Selector Sel = NSAPIObj->getNSArraySelector(NSAPI::NSArr_arrayWithObjectsCount); - ArrayWithObjectsMethod = NSArrayDecl->lookupClassMethod(Sel); - if (!ArrayWithObjectsMethod && getLangOpts().DebuggerObjCLiteral) { + ObjCMethodDecl *Method = NSArrayDecl->lookupClassMethod(Sel); + if (!Method && getLangOpts().DebuggerObjCLiteral) { TypeSourceInfo *ResultTInfo = 0; - ArrayWithObjectsMethod = - ObjCMethodDecl::Create(Context, + Method = ObjCMethodDecl::Create(Context, SourceLocation(), SourceLocation(), Sel, IdT, ResultTInfo, @@ -455,80 +651,68 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { ObjCMethodDecl::Required, false); SmallVector<ParmVarDecl *, 2> Params; - ParmVarDecl *objects = ParmVarDecl::Create(Context, ArrayWithObjectsMethod, - SourceLocation(), SourceLocation(), - &Context.Idents.get("objects"), - Context.getPointerType(IdT), - /*TInfo=*/0, - SC_None, - SC_None, - 0); + ParmVarDecl *objects = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/0, SC_None, SC_None, + 0); Params.push_back(objects); - ParmVarDecl *cnt = ParmVarDecl::Create(Context, ArrayWithObjectsMethod, - SourceLocation(), SourceLocation(), - &Context.Idents.get("cnt"), - Context.UnsignedLongTy, - /*TInfo=*/0, - SC_None, - SC_None, - 0); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/0, SC_None, SC_None, + 0); Params.push_back(cnt); - ArrayWithObjectsMethod->setMethodParams(Context, Params, - ArrayRef<SourceLocation>()); - - + Method->setMethodParams(Context, Params, ArrayRef<SourceLocation>()); } - if (!ArrayWithObjectsMethod) { - Diag(SR.getBegin(), diag::err_undeclared_arraywithobjects) << Sel; + if (!validateBoxingMethod(*this, SR.getBegin(), NSArrayDecl, Sel, Method)) + return ExprError(); + + // Dig out the type that all elements should be converted to. + QualType T = Method->param_begin()[0]->getType(); + const PointerType *PtrT = T->getAs<PointerType>(); + if (!PtrT || + !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->param_begin()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << T + << Context.getPointerType(IdT.withConst()); return ExprError(); } - } - // Make sure the return type is reasonable. - if (!ArrayWithObjectsMethod->getResultType()->isObjCObjectPointerType()) { - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << ArrayWithObjectsMethod->getSelector(); - Diag(ArrayWithObjectsMethod->getLocation(), - diag::note_objc_literal_method_return) - << ArrayWithObjectsMethod->getResultType(); - return ExprError(); - } + // Check that the 'count' parameter is integral. + if (!Method->param_begin()[1]->getType()->isIntegerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->param_begin()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 + << Method->param_begin()[1]->getType() + << "integral"; + return ExprError(); + } - // Dig out the type that all elements should be converted to. - QualType T = ArrayWithObjectsMethod->param_begin()[0]->getType(); - const PointerType *PtrT = T->getAs<PointerType>(); - if (!PtrT || - !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) { - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << ArrayWithObjectsMethod->getSelector(); - Diag(ArrayWithObjectsMethod->param_begin()[0]->getLocation(), - diag::note_objc_literal_method_param) - << 0 << T - << Context.getPointerType(IdT.withConst()); - return ExprError(); - } - T = PtrT->getPointeeType(); - - // Check that the 'count' parameter is integral. - if (!ArrayWithObjectsMethod->param_begin()[1]->getType()->isIntegerType()) { - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << ArrayWithObjectsMethod->getSelector(); - Diag(ArrayWithObjectsMethod->param_begin()[1]->getLocation(), - diag::note_objc_literal_method_param) - << 1 - << ArrayWithObjectsMethod->param_begin()[1]->getType() - << "integral"; - return ExprError(); + // We've found a good +arrayWithObjects:count: method. Save it! + ArrayWithObjectsMethod = Method; } + QualType ObjectsType = ArrayWithObjectsMethod->param_begin()[0]->getType(); + QualType RequiredType = ObjectsType->castAs<PointerType>()->getPointeeType(); + // Check that each of the elements provided is valid in a collection literal, // performing conversions as necessary. Expr **ElementsBuffer = Elements.get(); for (unsigned I = 0, N = Elements.size(); I != N; ++I) { ExprResult Converted = CheckObjCCollectionLiteralElement(*this, ElementsBuffer[I], - T); + RequiredType); if (Converted.isInvalid()) return ExprError(); @@ -573,11 +757,10 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, QualType IdT = Context.getObjCIdType(); if (!DictionaryWithObjectsMethod) { Selector Sel = NSAPIObj->getNSDictionarySelector( - NSAPI::NSDict_dictionaryWithObjectsForKeysCount); - DictionaryWithObjectsMethod = NSDictionaryDecl->lookupClassMethod(Sel); - if (!DictionaryWithObjectsMethod && getLangOpts().DebuggerObjCLiteral) { - DictionaryWithObjectsMethod = - ObjCMethodDecl::Create(Context, + NSAPI::NSDict_dictionaryWithObjectsForKeysCount); + ObjCMethodDecl *Method = NSDictionaryDecl->lookupClassMethod(Sel); + if (!Method && getLangOpts().DebuggerObjCLiteral) { + Method = ObjCMethodDecl::Create(Context, SourceLocation(), SourceLocation(), Sel, IdT, 0 /*TypeSourceInfo */, @@ -588,117 +771,107 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, ObjCMethodDecl::Required, false); SmallVector<ParmVarDecl *, 3> Params; - ParmVarDecl *objects = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, - SourceLocation(), SourceLocation(), - &Context.Idents.get("objects"), - Context.getPointerType(IdT), - /*TInfo=*/0, - SC_None, - SC_None, - 0); + ParmVarDecl *objects = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/0, SC_None, SC_None, + 0); Params.push_back(objects); - ParmVarDecl *keys = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, - SourceLocation(), SourceLocation(), - &Context.Idents.get("keys"), - Context.getPointerType(IdT), - /*TInfo=*/0, - SC_None, - SC_None, - 0); + ParmVarDecl *keys = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("keys"), + Context.getPointerType(IdT), + /*TInfo=*/0, SC_None, SC_None, + 0); Params.push_back(keys); - ParmVarDecl *cnt = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, - SourceLocation(), SourceLocation(), - &Context.Idents.get("cnt"), - Context.UnsignedLongTy, - /*TInfo=*/0, - SC_None, - SC_None, - 0); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/0, SC_None, SC_None, + 0); Params.push_back(cnt); - DictionaryWithObjectsMethod->setMethodParams(Context, Params, - ArrayRef<SourceLocation>()); + Method->setMethodParams(Context, Params, ArrayRef<SourceLocation>()); } - if (!DictionaryWithObjectsMethod) { - Diag(SR.getBegin(), diag::err_undeclared_dictwithobjects) << Sel; - return ExprError(); + if (!validateBoxingMethod(*this, SR.getBegin(), NSDictionaryDecl, Sel, + Method)) + return ExprError(); + + // Dig out the type that all values should be converted to. + QualType ValueT = Method->param_begin()[0]->getType(); + const PointerType *PtrValue = ValueT->getAs<PointerType>(); + if (!PtrValue || + !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->param_begin()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << ValueT + << Context.getPointerType(IdT.withConst()); + return ExprError(); } - } - - // Make sure the return type is reasonable. - if (!DictionaryWithObjectsMethod->getResultType()->isObjCObjectPointerType()){ - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << DictionaryWithObjectsMethod->getSelector(); - Diag(DictionaryWithObjectsMethod->getLocation(), - diag::note_objc_literal_method_return) - << DictionaryWithObjectsMethod->getResultType(); - return ExprError(); - } - // Dig out the type that all values should be converted to. - QualType ValueT = DictionaryWithObjectsMethod->param_begin()[0]->getType(); - const PointerType *PtrValue = ValueT->getAs<PointerType>(); - if (!PtrValue || - !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) { - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << DictionaryWithObjectsMethod->getSelector(); - Diag(DictionaryWithObjectsMethod->param_begin()[0]->getLocation(), - diag::note_objc_literal_method_param) - << 0 << ValueT - << Context.getPointerType(IdT.withConst()); - return ExprError(); - } - ValueT = PtrValue->getPointeeType(); - - // Dig out the type that all keys should be converted to. - QualType KeyT = DictionaryWithObjectsMethod->param_begin()[1]->getType(); - const PointerType *PtrKey = KeyT->getAs<PointerType>(); - if (!PtrKey || - !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), - IdT)) { - bool err = true; - if (PtrKey) { - if (QIDNSCopying.isNull()) { - // key argument of selector is id<NSCopying>? - if (ObjCProtocolDecl *NSCopyingPDecl = - LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) { - ObjCProtocolDecl *PQ[] = {NSCopyingPDecl}; - QIDNSCopying = - Context.getObjCObjectType(Context.ObjCBuiltinIdTy, - (ObjCProtocolDecl**) PQ,1); - QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying); + // Dig out the type that all keys should be converted to. + QualType KeyT = Method->param_begin()[1]->getType(); + const PointerType *PtrKey = KeyT->getAs<PointerType>(); + if (!PtrKey || + !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + IdT)) { + bool err = true; + if (PtrKey) { + if (QIDNSCopying.isNull()) { + // key argument of selector is id<NSCopying>? + if (ObjCProtocolDecl *NSCopyingPDecl = + LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) { + ObjCProtocolDecl *PQ[] = {NSCopyingPDecl}; + QIDNSCopying = + Context.getObjCObjectType(Context.ObjCBuiltinIdTy, + (ObjCProtocolDecl**) PQ,1); + QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying); + } } + if (!QIDNSCopying.isNull()) + err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + QIDNSCopying); } - if (!QIDNSCopying.isNull()) - err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), - QIDNSCopying); - } - if (err) { + if (err) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->param_begin()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 << KeyT + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + } + + // Check that the 'count' parameter is integral. + QualType CountType = Method->param_begin()[2]->getType(); + if (!CountType->isIntegerType()) { Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << DictionaryWithObjectsMethod->getSelector(); - Diag(DictionaryWithObjectsMethod->param_begin()[1]->getLocation(), + << Sel; + Diag(Method->param_begin()[2]->getLocation(), diag::note_objc_literal_method_param) - << 1 << KeyT - << Context.getPointerType(IdT.withConst()); + << 2 << CountType + << "integral"; return ExprError(); } - } - KeyT = PtrKey->getPointeeType(); - // Check that the 'count' parameter is integral. - if (!DictionaryWithObjectsMethod->param_begin()[2]->getType() - ->isIntegerType()) { - Diag(SR.getBegin(), diag::err_objc_literal_method_sig) - << DictionaryWithObjectsMethod->getSelector(); - Diag(DictionaryWithObjectsMethod->param_begin()[2]->getLocation(), - diag::note_objc_literal_method_param) - << 2 - << DictionaryWithObjectsMethod->param_begin()[2]->getType() - << "integral"; - return ExprError(); + // We've found a good +dictionaryWithObjects:keys:count: method; save it! + DictionaryWithObjectsMethod = Method; } + QualType ValuesT = DictionaryWithObjectsMethod->param_begin()[0]->getType(); + QualType ValueT = ValuesT->castAs<PointerType>()->getPointeeType(); + QualType KeysT = DictionaryWithObjectsMethod->param_begin()[1]->getType(); + QualType KeyT = KeysT->castAs<PointerType>()->getPointeeType(); + // Check that each of the keys and values provided is valid in a collection // literal, performing conversions as necessary. bool HasPackExpansions = false; @@ -757,8 +930,8 @@ ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc, if (!EncodedType->getAsArrayTypeUnsafe() && //// Incomplete array is handled. !EncodedType->isVoidType()) // void is handled too. if (RequireCompleteType(AtLoc, EncodedType, - PDiag(diag::err_incomplete_type_objc_at_encode) - << EncodedTypeInfo->getTypeLoc().getSourceRange())) + diag::err_incomplete_type_objc_at_encode, + EncodedTypeInfo->getTypeLoc())) return ExprError(); std::string Str; @@ -846,8 +1019,9 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, SourceLocation AtLoc, SourceLocation ProtoLoc, SourceLocation LParenLoc, + SourceLocation ProtoIdLoc, SourceLocation RParenLoc) { - ObjCProtocolDecl* PDecl = LookupProtocol(ProtocolId, ProtoLoc); + ObjCProtocolDecl* PDecl = LookupProtocol(ProtocolId, ProtoIdLoc); if (!PDecl) { Diag(ProtoLoc, diag::err_undeclared_protocol) << ProtocolId; return true; @@ -857,7 +1031,7 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, if (Ty.isNull()) return true; Ty = Context.getObjCObjectPointerType(Ty); - return new (Context) ObjCProtocolExpr(Ty, PDecl, AtLoc, RParenLoc); + return new (Context) ObjCProtocolExpr(Ty, PDecl, AtLoc, ProtoIdLoc, RParenLoc); } /// Try to capture an implicit reference to 'self'. @@ -1023,8 +1197,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, if (RequireCompleteType(argExpr->getSourceRange().getBegin(), param->getType(), - PDiag(diag::err_call_incomplete_argument) - << argExpr->getSourceRange())) + diag::err_call_incomplete_argument, argExpr)) return true; InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, @@ -1042,7 +1215,8 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, if (Args[i]->isTypeDependent()) continue; - ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, 0); + ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, + 0); IsError |= Arg.isInvalid(); Args[i] = Arg.take(); } @@ -1079,57 +1253,6 @@ bool Sema::isSelfExpr(Expr *receiver) { return false; } -// Helper method for ActOnClassMethod/ActOnInstanceMethod. -// Will search "local" class/category implementations for a method decl. -// If failed, then we search in class's root for an instance method. -// Returns 0 if no method is found. -ObjCMethodDecl *Sema::LookupPrivateClassMethod(Selector Sel, - ObjCInterfaceDecl *ClassDecl) { - ObjCMethodDecl *Method = 0; - // lookup in class and all superclasses - while (ClassDecl && !Method) { - if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation()) - Method = ImpDecl->getClassMethod(Sel); - - // Look through local category implementations associated with the class. - if (!Method) - Method = ClassDecl->getCategoryClassMethod(Sel); - - // Before we give up, check if the selector is an instance method. - // But only in the root. This matches gcc's behaviour and what the - // runtime expects. - if (!Method && !ClassDecl->getSuperClass()) { - Method = ClassDecl->lookupInstanceMethod(Sel); - // Look through local category implementations associated - // with the root class. - if (!Method) - Method = LookupPrivateInstanceMethod(Sel, ClassDecl); - } - - ClassDecl = ClassDecl->getSuperClass(); - } - return Method; -} - -ObjCMethodDecl *Sema::LookupPrivateInstanceMethod(Selector Sel, - ObjCInterfaceDecl *ClassDecl) { - if (!ClassDecl->hasDefinition()) - return 0; - - ObjCMethodDecl *Method = 0; - while (ClassDecl && !Method) { - // If we have implementations in scope, check "private" methods. - if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation()) - Method = ImpDecl->getInstanceMethod(Sel); - - // Look through local category implementations associated with the class. - if (!Method) - Method = ClassDecl->getCategoryInstanceMethod(Sel); - ClassDecl = ClassDecl->getSuperClass(); - } - return Method; -} - /// LookupMethodInType - Look up a method in an ObjCObjectType. ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type, bool isInstance) { @@ -1141,13 +1264,8 @@ ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type, // Okay, look for "private" methods declared in any // @implementations we've seen. - if (isInstance) { - if (ObjCMethodDecl *method = LookupPrivateInstanceMethod(sel, iface)) - return method; - } else { - if (ObjCMethodDecl *method = LookupPrivateClassMethod(sel, iface)) - return method; - } + if (ObjCMethodDecl *method = iface->lookupPrivateMethod(sel, isInstance)) + return method; } // Check qualifiers. @@ -1176,6 +1294,69 @@ ObjCMethodDecl *Sema::LookupMethodInQualifiedType(Selector Sel, return 0; } +static void DiagnoseARCUseOfWeakReceiver(Sema &S, Expr *Receiver) { + if (!Receiver) + return; + + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Receiver)) + Receiver = OVE->getSourceExpr(); + + Expr *RExpr = Receiver->IgnoreParenImpCasts(); + SourceLocation Loc = RExpr->getLocStart(); + QualType T = RExpr->getType(); + ObjCPropertyDecl *PDecl = 0; + ObjCMethodDecl *GDecl = 0; + if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(RExpr)) { + RExpr = POE->getSyntacticForm(); + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(RExpr)) { + if (PRE->isImplicitProperty()) { + GDecl = PRE->getImplicitPropertyGetter(); + if (GDecl) { + T = GDecl->getResultType(); + } + } + else { + PDecl = PRE->getExplicitProperty(); + if (PDecl) { + T = PDecl->getType(); + } + } + } + } + else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RExpr)) { + // See if receiver is a method which envokes a synthesized getter + // backing a 'weak' property. + ObjCMethodDecl *Method = ME->getMethodDecl(); + if (Method && Method->isSynthesized()) { + Selector Sel = Method->getSelector(); + if (Sel.getNumArgs() == 0) { + const DeclContext *Container = Method->getDeclContext(); + PDecl = + S.LookupPropertyDecl(cast<ObjCContainerDecl>(Container), + Sel.getIdentifierInfoForSlot(0)); + } + if (PDecl) + T = PDecl->getType(); + } + } + + if (T.getObjCLifetime() == Qualifiers::OCL_Weak) { + S.Diag(Loc, diag::warn_receiver_is_weak) + << ((!PDecl && !GDecl) ? 0 : (PDecl ? 1 : 2)); + if (PDecl) + S.Diag(PDecl->getLocation(), diag::note_property_declare); + else if (GDecl) + S.Diag(GDecl->getLocation(), diag::note_method_declared_at) << GDecl; + return; + } + + if (PDecl && + (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)) { + S.Diag(Loc, diag::warn_receiver_is_weak) << 1; + S.Diag(PDecl->getLocation(), diag::note_property_declare); + } +} + /// HandleExprPropertyRefExpr - Handle foo.bar where foo is a pointer to an /// objective C interface. This is a property reference expression. ExprResult Sema:: @@ -1187,19 +1368,20 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, bool Super) { const ObjCInterfaceType *IFaceT = OPT->getInterfaceType(); ObjCInterfaceDecl *IFace = IFaceT->getDecl(); - - if (MemberName.getNameKind() != DeclarationName::Identifier) { + + if (!MemberName.isIdentifier()) { Diag(MemberLoc, diag::err_invalid_property_name) << MemberName << QualType(OPT, 0); return ExprError(); } - + IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); + SourceRange BaseRange = Super? SourceRange(SuperLoc) : BaseExpr->getSourceRange(); if (RequireCompleteType(MemberLoc, OPT->getPointeeType(), - PDiag(diag::err_property_not_found_forward_class) - << MemberName << BaseRange)) + diag::err_property_not_found_forward_class, + MemberName, BaseRange)) return ExprError(); // Search for a declared property first. @@ -1207,7 +1389,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - if (Super) return Owned(new (Context) ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, @@ -1225,7 +1406,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - + if (Super) return Owned(new (Context) ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, @@ -1258,9 +1439,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (!Getter) Getter = IFace->lookupPrivateMethod(Sel); - // Look through local category implementations associated with the class. - if (!Getter) - Getter = IFace->getCategoryInstanceMethod(Sel); if (Getter) { // Check if we can reference this property. if (DiagnoseUseOfDecl(Getter, MemberLoc)) @@ -1272,7 +1450,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, SelectorTable::constructSetterName(PP.getIdentifierTable(), PP.getSelectorTable(), Member); ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); - + // May be founf in property's qualified list. if (!Setter) Setter = LookupMethodInQualifiedType(SetterSel, OPT, true); @@ -1282,9 +1460,6 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // methods. Setter = IFace->lookupPrivateMethod(SetterSel); } - // Look through local category implementations associated with the class. - if (!Setter) - Setter = IFace->getCategoryInstanceMethod(SetterSel); if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) return ExprError(); @@ -1328,8 +1503,8 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (const ObjCObjectPointerType * OBJPT = T->getAsObjCInterfacePointerType()) { if (RequireCompleteType(MemberLoc, OBJPT->getPointeeType(), - PDiag(diag::err_property_not_as_forward_class) - << MemberName << BaseExpr->getSourceRange())) + diag::err_property_not_as_forward_class, + MemberName, BaseExpr)) return ExprError(); } Diag(MemberLoc, @@ -1603,9 +1778,9 @@ ExprResult Sema::ActOnSuperMessage(Scope *S, // is acting as a keyword. if (Method->isInstanceMethod()) { if (Sel.getMethodFamily() == OMF_dealloc) - ObjCShouldCallSuperDealloc = false; + getCurFunction()->ObjCShouldCallSuperDealloc = false; if (Sel.getMethodFamily() == OMF_finalize) - ObjCShouldCallSuperFinalize = false; + getCurFunction()->ObjCShouldCallSuperFinalize = false; // Since we are in an instance method, this is an instance // message to the superclass instance. @@ -1711,9 +1886,9 @@ static void checkCocoaAPI(Sema &S, const ObjCMessageExpr *Msg) { /// /// \param LBracLoc The location of the opening square bracket ']'. /// -/// \param RBrac The location of the closing square bracket ']'. +/// \param RBracLoc The location of the closing square bracket ']'. /// -/// \param Args The message arguments. +/// \param ArgsIn The message arguments. ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, QualType ReceiverType, SourceLocation SuperLoc, @@ -1762,11 +1937,11 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, SourceRange TypeRange = SuperLoc.isValid()? SourceRange(SuperLoc) : ReceiverTypeInfo->getTypeLoc().getSourceRange(); - if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class), + if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class), (getLangOpts().ObjCAutoRefCount - ? PDiag(diag::err_arc_receiver_forward_class) - : PDiag(diag::warn_receiver_forward_class)) - << TypeRange)) { + ? diag::err_arc_receiver_forward_class + : diag::warn_receiver_forward_class), + TypeRange)) { // A forward class used in messaging is treated as a 'Class' Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); @@ -1779,7 +1954,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, // If we have an implementation in scope, check "private" methods. if (!Method) - Method = LookupPrivateClassMethod(Sel, Class); + Method = Class->lookupPrivateClassMethod(Sel); if (Method && DiagnoseUseOfDecl(Method, Loc)) return ExprError(); @@ -1881,9 +2056,9 @@ ExprResult Sema::BuildInstanceMessageImplicit(Expr *Receiver, /// /// \param LBracLoc The location of the opening square bracket ']'. /// -/// \param RBrac The location of the closing square bracket ']'. +/// \param RBracLoc The location of the closing square bracket ']'. /// -/// \param Args The message arguments. +/// \param ArgsIn The message arguments. ExprResult Sema::BuildInstanceMessage(Expr *Receiver, QualType ReceiverType, SourceLocation SuperLoc, @@ -1948,7 +2123,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, receiverIsId); if (!Method) Method = LookupFactoryMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc), + SourceRange(LBracLoc,RBracLoc), receiverIsId); } else if (ReceiverType->isObjCClassType() || ReceiverType->isObjCQualifiedClassType()) { @@ -1976,7 +2151,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = ClassDecl->lookupClassMethod(Sel); if (!Method) - Method = LookupPrivateClassMethod(Sel, ClassDecl); + Method = ClassDecl->lookupPrivateClassMethod(Sel); } if (Method && DiagnoseUseOfDecl(Method, Loc)) return ExprError(); @@ -2009,12 +2184,15 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // We allow sending a message to a qualified ID ("id<foo>"), which is ok as // long as one of the protocols implements the selector (if not, warn). + // And as long as message is not deprecated/unavailable (warn if it is). if (const ObjCObjectPointerType *QIdTy = ReceiverType->getAsObjCQualifiedIdType()) { // Search protocols for instance methods. Method = LookupMethodInQualifiedType(Sel, QIdTy, true); if (!Method) Method = LookupMethodInQualifiedType(Sel, QIdTy, false); + if (Method && DiagnoseUseOfDecl(Method, Loc)) + return ExprError(); } else if (const ObjCObjectPointerType *OCIType = ReceiverType->getAsObjCInterfacePointerType()) { // We allow sending a message to a pointer to an interface (an object). @@ -2025,12 +2203,10 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, const ObjCInterfaceDecl *forwardClass = 0; if (RequireCompleteType(Loc, OCIType->getPointeeType(), getLangOpts().ObjCAutoRefCount - ? PDiag(diag::err_arc_receiver_forward_instance) - << (Receiver ? Receiver->getSourceRange() - : SourceRange(SuperLoc)) - : PDiag(diag::warn_receiver_forward_instance) - << (Receiver ? Receiver->getSourceRange() - : SourceRange(SuperLoc)))) { + ? diag::err_arc_receiver_forward_instance + : diag::warn_receiver_forward_instance, + Receiver? Receiver->getSourceRange() + : SourceRange(SuperLoc))) { if (getLangOpts().ObjCAutoRefCount) return ExprError(); @@ -2048,7 +2224,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!Method) { // If we have implementations in scope, check "private" methods. - Method = LookupPrivateInstanceMethod(Sel, ClassDecl); + Method = ClassDecl->lookupPrivateMethod(Sel); if (!Method && getLangOpts().ObjCAutoRefCount) { Diag(Loc, diag::err_arc_may_not_respond) @@ -2062,7 +2238,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // compatibility. FIXME: should we deviate?? if (OCIType->qual_empty()) { Method = LookupInstanceMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc)); + SourceRange(LBracLoc, RBracLoc)); if (Method && !forwardClass) Diag(Loc, diag::warn_maynot_respond) << OCIType->getInterfaceDecl()->getIdentifier() << Sel; @@ -2087,8 +2263,9 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // TODO: specialized warning on null receivers? bool IsNull = Receiver->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + CastKind Kind = IsNull ? CK_NullToPointer : CK_IntegralToPointer; Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(), - IsNull ? CK_NullToPointer : CK_IntegralToPointer).take(); + Kind).take(); } ReceiverType = Receiver->getType(); } else { @@ -2232,10 +2409,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } if (getLangOpts().ObjCAutoRefCount) { - if (Receiver && - (Receiver->IgnoreParenImpCasts()->getType().getObjCLifetime() - == Qualifiers::OCL_Weak)) - Diag(Receiver->getLocStart(), diag::warn_receiver_is_weak); + DiagnoseARCUseOfWeakReceiver(*this, Receiver); // In ARC, annotate delegate init calls. if (Result->getMethodFamily() == OMF_init && @@ -2373,6 +2547,7 @@ namespace { ASTContext &Context; ARCConversionTypeClass SourceClass; ARCConversionTypeClass TargetClass; + bool Diagnose; static bool isCFType(QualType type) { // Someday this can use ns_bridged. For now, it has to do this. @@ -2381,8 +2556,9 @@ namespace { public: ARCCastChecker(ASTContext &Context, ARCConversionTypeClass source, - ARCConversionTypeClass target) - : Context(Context), SourceClass(source), TargetClass(target) {} + ARCConversionTypeClass target, bool diagnose) + : Context(Context), SourceClass(source), TargetClass(target), + Diagnose(diagnose) {} using super::Visit; ACCResult Visit(Expr *e) { @@ -2500,7 +2676,8 @@ namespace { // now we're not going to permit implicit handling of +1 results, // because it's a bit frightening. if (fn->hasAttr<CFReturnsRetainedAttr>()) - return ACC_invalid; // ACC_plusOne if we start accepting this + return Diagnose ? ACC_plusOne + : ACC_invalid; // ACC_plusOne if we start accepting this // Recognize this specific builtin function, which is used by CFSTR. unsigned builtinID = fn->getBuiltinID(); @@ -2510,10 +2687,11 @@ namespace { // Otherwise, don't do anything implicit with an unaudited function. if (!fn->hasAttr<CFAuditedTransferAttr>()) return ACC_invalid; - + // Otherwise, it's +0 unless it follows the create convention. if (ento::coreFoundation::followsCreateRule(fn)) - return ACC_invalid; // ACC_plusOne if we start accepting this + return Diagnose ? ACC_plusOne + : ACC_invalid; // ACC_plusOne if we start accepting this return ACC_plusZero; } @@ -2564,11 +2742,12 @@ namespace { }; } -static bool -KnownName(Sema &S, const char *name) { - LookupResult R(S, &S.Context.Idents.get(name), SourceLocation(), +bool Sema::isKnownName(StringRef name) { + if (name.empty()) + return false; + LookupResult R(*this, &Context.Idents.get(name), SourceLocation(), Sema::LookupOrdinaryName); - return S.LookupName(R, S.TUScope, false); + return LookupName(R, TUScope, false); } static void addFixitForObjCARCConversion(Sema &S, @@ -2595,14 +2774,23 @@ static void addFixitForObjCARCConversion(Sema &S, castedE = CCE->getSubExpr(); castedE = castedE->IgnoreImpCasts(); SourceRange range = castedE->getSourceRange(); + + SmallString<32> BridgeCall; + + SourceManager &SM = S.getSourceManager(); + char PrevChar = *SM.getCharacterData(range.getBegin().getLocWithOffset(-1)); + if (Lexer::isIdentifierBodyChar(PrevChar, S.getLangOpts())) + BridgeCall += ' '; + + BridgeCall += CFBridgeName; + if (isa<ParenExpr>(castedE)) { DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), - CFBridgeName)); + BridgeCall)); } else { - std::string namePlusParen = CFBridgeName; - namePlusParen += "("; + BridgeCall += '('; DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), - namePlusParen)); + BridgeCall)); DiagB.AddFixItHint(FixItHint::CreateInsertion( S.PP.getLocForEndOfToken(range.getEnd()), ")")); @@ -2677,14 +2865,20 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange, << castType << castRange << castExpr->getSourceRange(); - bool br = KnownName(S, "CFBridgingRelease"); + bool br = S.isKnownName("CFBridgingRelease"); + ACCResult CreateRule = + ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr); + assert(CreateRule != ACC_bottom && "This cast should already be accepted."); + if (CreateRule != ACC_plusOne) { DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge); addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, castType, castExpr, "__bridge ", 0); } + if (CreateRule != ACC_plusZero) { - DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge_transfer) + DiagnosticBuilder DiagB = S.Diag(br ? castExpr->getExprLoc() : noteLoc, + diag::note_arc_bridge_transfer) << castExprType << br; addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, castType, castExpr, "__bridge_transfer ", @@ -2696,7 +2890,7 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange, // Bridge from a CF type to an ARC type. if (exprACTC == ACTC_retainable && isAnyRetainable(castACTC)) { - bool br = KnownName(S, "CFBridgingRetain"); + bool br = S.isKnownName("CFBridgingRetain"); S.Diag(loc, diag::err_arc_cast_requires_bridge) << unsigned(CCK == Sema::CCK_ImplicitConversion) // cast|implicit << unsigned(castExprType->isBlockPointerType()) // of ObjC|block type @@ -2705,14 +2899,19 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange, << castType << castRange << castExpr->getSourceRange(); - + ACCResult CreateRule = + ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr); + assert(CreateRule != ACC_bottom && "This cast should already be accepted."); + if (CreateRule != ACC_plusOne) { DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge); addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, castType, castExpr, "__bridge ", 0); } + if (CreateRule != ACC_plusZero) { - DiagnosticBuilder DiagB = S.Diag(noteLoc, diag::note_arc_bridge_retained) + DiagnosticBuilder DiagB = S.Diag(br ? castExpr->getExprLoc() : noteLoc, + diag::note_arc_bridge_retained) << castType << br; addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, castType, castExpr, "__bridge_retained ", @@ -2785,7 +2984,7 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, CCK != CCK_ImplicitConversion) return ACR_okay; - switch (ARCCastChecker(Context, exprACTC, castACTC).Visit(castExpr)) { + switch (ARCCastChecker(Context, exprACTC, castACTC, false).Visit(castExpr)) { // For invalid casts, fall through. case ACC_invalid: break; @@ -2949,7 +3148,7 @@ ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, break; case OBC_BridgeRetained: { - bool br = KnownName(*this, "CFBridgingRelease"); + bool br = isKnownName("CFBridgingRelease"); Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) << 2 << FromType @@ -2992,7 +3191,7 @@ ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, break; case OBC_BridgeTransfer: { - bool br = KnownName(*this, "CFBridgingRetain"); + bool br = isKnownName("CFBridgingRetain"); Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) << (FromType->isBlockPointerType()? 1 : 0) << FromType diff --git a/lib/Sema/SemaFixItUtils.cpp b/lib/Sema/SemaFixItUtils.cpp index b78ea7d18ee7..b61b9307dde9 100644 --- a/lib/Sema/SemaFixItUtils.cpp +++ b/lib/Sema/SemaFixItUtils.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Lex/Preprocessor.h" @@ -163,42 +164,54 @@ static bool isMacroDefined(const Sema &S, StringRef Name) { return S.PP.getMacroInfo(&S.getASTContext().Idents.get(Name)); } -const char *Sema::getFixItZeroInitializerForType(QualType T) const { +static std::string getScalarZeroExpressionForType(const Type& T, const Sema& S) { + assert(T.isScalarType() && "use scalar types only"); + // Suggest "0" for non-enumeration scalar types, unless we can find a + // better initializer. + if (T.isEnumeralType()) + return std::string(); + if ((T.isObjCObjectPointerType() || T.isBlockPointerType()) && + isMacroDefined(S, "nil")) + return "nil"; + if (T.isRealFloatingType()) + return "0.0"; + if (T.isBooleanType() && S.LangOpts.CPlusPlus) + return "false"; + if (T.isPointerType() || T.isMemberPointerType()) { + if (S.LangOpts.CPlusPlus0x) + return "nullptr"; + if (isMacroDefined(S, "NULL")) + return "NULL"; + } + if (T.isCharType()) + return "'\\0'"; + if (T.isWideCharType()) + return "L'\\0'"; + if (T.isChar16Type()) + return "u'\\0'"; + if (T.isChar32Type()) + return "U'\\0'"; + return "0"; +} + +std::string Sema::getFixItZeroInitializerForType(QualType T) const { if (T->isScalarType()) { - // Suggest " = 0" for non-enumeration scalar types, unless we can find a - // better initializer. - if (T->isEnumeralType()) - return 0; - if ((T->isObjCObjectPointerType() || T->isBlockPointerType()) && - isMacroDefined(*this, "nil")) - return " = nil"; - if (T->isRealFloatingType()) - return " = 0.0"; - if (T->isBooleanType() && LangOpts.CPlusPlus) - return " = false"; - if (T->isPointerType() || T->isMemberPointerType()) { - if (LangOpts.CPlusPlus0x) - return " = nullptr"; - else if (isMacroDefined(*this, "NULL")) - return " = NULL"; - } - if (T->isCharType()) - return " = '\\0'"; - if (T->isWideCharType()) - return " = L'\\0'"; - if (T->isChar16Type()) - return " = u'\\0'"; - if (T->isChar32Type()) - return " = U'\\0'"; - return " = 0"; + std::string s = getScalarZeroExpressionForType(*T, *this); + if (!s.empty()) + s = " = " + s; + return s; } const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); if (!RD || !RD->hasDefinition()) - return 0; + return std::string(); if (LangOpts.CPlusPlus0x && !RD->hasUserProvidedDefaultConstructor()) return "{}"; if (RD->isAggregate()) return " = {}"; - return 0; + return std::string(); +} + +std::string Sema::getFixItZeroLiteralForType(QualType T) const { + return getScalarZeroExpressionForType(*T, *this); } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index a65b41fd1cb6..62ab1e645089 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -92,8 +92,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) { // C99 6.7.8p14. We have an array of character type with unknown size // being initialized to a string literal. - llvm::APSInt ConstVal(32); - ConstVal = StrLength; + llvm::APInt ConstVal(32, StrLength); // Return a new array type (C99 6.7.8p22). DeclT = S.Context.getConstantArrayType(IAT->getElementType(), ConstVal, @@ -687,22 +686,21 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, } else if (DeclType->isVectorType()) { CheckVectorType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); - } else if (DeclType->isAggregateType()) { - if (DeclType->isRecordType()) { - RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); - CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(), - SubobjectIsDesignatorContext, Index, - StructuredList, StructuredIndex, - TopLevelObject); - } else if (DeclType->isArrayType()) { - llvm::APSInt Zero( - SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()), - false); - CheckArrayType(Entity, IList, DeclType, Zero, - SubobjectIsDesignatorContext, Index, - StructuredList, StructuredIndex); - } else - llvm_unreachable("Aggregate that isn't a structure or array?!"); + } else if (DeclType->isRecordType()) { + assert(DeclType->isAggregateType() && + "non-aggregate records should be handed in CheckSubElementType"); + RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); + CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(), + SubobjectIsDesignatorContext, Index, + StructuredList, StructuredIndex, + TopLevelObject); + } else if (DeclType->isArrayType()) { + llvm::APSInt Zero( + SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()), + false); + CheckArrayType(Entity, IList, DeclType, Zero, + SubobjectIsDesignatorContext, Index, + StructuredList, StructuredIndex); } else if (DeclType->isVoidType() || DeclType->isFunctionType()) { // This type is invalid, issue a diagnostic. ++Index; @@ -710,19 +708,6 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, SemaRef.Diag(IList->getLocStart(), diag::err_illegal_initializer_type) << DeclType; hadError = true; - } else if (DeclType->isRecordType()) { - // C++ [dcl.init]p14: - // [...] If the class is an aggregate (8.5.1), and the initializer - // is a brace-enclosed list, see 8.5.1. - // - // Note: 8.5.1 is handled below; here, we diagnose the case where - // we have an initializer list and a destination type that is not - // an aggregate. - // FIXME: In C++0x, this is yet another form of initialization. - if (!VerifyOnly) - SemaRef.Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list) - << DeclType << IList->getSourceRange(); - hadError = true; } else if (DeclType->isReferenceType()) { CheckReferenceType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); @@ -747,18 +732,25 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, unsigned &StructuredIndex) { Expr *expr = IList->getInit(Index); if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) { - unsigned newIndex = 0; - unsigned newStructuredIndex = 0; - InitListExpr *newStructuredList - = getStructuredSubobjectInit(IList, Index, ElemType, - StructuredList, StructuredIndex, - SubInitList->getSourceRange()); - CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex, - newStructuredList, newStructuredIndex); - ++StructuredIndex; - ++Index; - return; - } else if (ElemType->isScalarType()) { + if (!ElemType->isRecordType() || ElemType->isAggregateType()) { + unsigned newIndex = 0; + unsigned newStructuredIndex = 0; + InitListExpr *newStructuredList + = getStructuredSubobjectInit(IList, Index, ElemType, + StructuredList, StructuredIndex, + SubInitList->getSourceRange()); + CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex, + newStructuredList, newStructuredIndex); + ++StructuredIndex; + ++Index; + return; + } + assert(SemaRef.getLangOpts().CPlusPlus && + "non-aggregate records are only possible in C++"); + // C++ initialization is handled later. + } + + if (ElemType->isScalarType()) { return CheckScalarType(Entity, IList, ElemType, Index, StructuredList, StructuredIndex); } else if (ElemType->isReferenceType()) { @@ -1859,7 +1851,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, } } else { // Recurse to check later designated subobjects. - QualType FieldType = (*Field)->getType(); + QualType FieldType = Field->getType(); unsigned newStructuredIndex = FieldIndex; InitializedEntity MemberEntity = @@ -2708,84 +2700,39 @@ static void MaybeProduceObjCObject(Sema &S, } } -/// \brief When initializing from init list via constructor, deal with the -/// empty init list and std::initializer_list special cases. +/// \brief When initializing from init list via constructor, handle +/// initialization of an object of type std::initializer_list<T>. /// -/// \return True if this was a special case, false otherwise. -static bool TryListConstructionSpecialCases(Sema &S, - InitListExpr *List, - CXXRecordDecl *DestRecordDecl, - QualType DestType, - InitializationSequence &Sequence) { - // C++11 [dcl.init.list]p3: - // List-initialization of an object or reference of type T is defined as - // follows: - // - If T is an aggregate, aggregate initialization is performed. - if (DestType->isAggregateType()) +/// \return true if we have handled initialization of an object of type +/// std::initializer_list<T>, false otherwise. +static bool TryInitializerListConstruction(Sema &S, + InitListExpr *List, + QualType DestType, + InitializationSequence &Sequence) { + QualType E; + if (!S.isStdInitializerList(DestType, &E)) return false; - // - Otherwise, if the initializer list has no elements and T is a class - // type with a default constructor, the object is value-initialized. - if (List->getNumInits() == 0) { - if (CXXConstructorDecl *DefaultConstructor = - S.LookupDefaultConstructor(DestRecordDecl)) { - if (DefaultConstructor->isDeleted() || - S.isFunctionConsideredUnavailable(DefaultConstructor)) { - // Fake an overload resolution failure. - OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet(); - DeclAccessPair FoundDecl = DeclAccessPair::make(DefaultConstructor, - DefaultConstructor->getAccess()); - if (FunctionTemplateDecl *ConstructorTmpl = - dyn_cast<FunctionTemplateDecl>(DefaultConstructor)) - S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl, - /*ExplicitArgs*/ 0, - ArrayRef<Expr*>(), CandidateSet, - /*SuppressUserConversions*/ false); - else - S.AddOverloadCandidate(DefaultConstructor, FoundDecl, - ArrayRef<Expr*>(), CandidateSet, - /*SuppressUserConversions*/ false); - Sequence.SetOverloadFailure( - InitializationSequence::FK_ListConstructorOverloadFailed, - OR_Deleted); - } else - Sequence.AddConstructorInitializationStep(DefaultConstructor, - DefaultConstructor->getAccess(), - DestType, - /*MultipleCandidates=*/false, - /*FromInitList=*/true, - /*AsInitList=*/false); + // Check that each individual element can be copy-constructed. But since we + // have no place to store further information, we'll recalculate everything + // later. + InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( + S.Context.getConstantArrayType(E, + llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), + List->getNumInits()), + ArrayType::Normal, 0)); + InitializedEntity Element = InitializedEntity::InitializeElement(S.Context, + 0, HiddenArray); + for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) { + Element.setElementIndex(i); + if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) { + Sequence.SetFailed( + InitializationSequence::FK_InitListElementCopyFailure); return true; } } - - // - Otherwise, if T is a specialization of std::initializer_list, [...] - QualType E; - if (S.isStdInitializerList(DestType, &E)) { - // Check that each individual element can be copy-constructed. But since we - // have no place to store further information, we'll recalculate everything - // later. - InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( - S.Context.getConstantArrayType(E, - llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), - List->getNumInits()), - ArrayType::Normal, 0)); - InitializedEntity Element = InitializedEntity::InitializeElement(S.Context, - 0, HiddenArray); - for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) { - Element.setElementIndex(i); - if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) { - Sequence.SetFailed( - InitializationSequence::FK_InitListElementCopyFailure); - return true; - } - } - Sequence.AddStdInitializerListConstructionStep(DestType); - return true; - } - - // Not a special case. - return false; + Sequence.AddStdInitializerListConstructionStep(DestType); + return true; } static OverloadingResult @@ -2886,11 +2833,6 @@ static void TryConstructorInitialization(Sema &S, CXXRecordDecl *DestRecordDecl = cast<CXXRecordDecl>(DestRecordType->getDecl()); - if (InitListSyntax && - TryListConstructionSpecialCases(S, cast<InitListExpr>(Args[0]), - DestRecordDecl, DestType, Sequence)) - return; - // Build the candidate set directly in the initialization sequence // structure, so that it will persist if we fail. OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet(); @@ -2917,15 +2859,21 @@ static void TryConstructorInitialization(Sema &S, // constructors of the class T and the argument list consists of the // initializer list as a single argument. if (InitListSyntax) { + InitListExpr *ILE = cast<InitListExpr>(Args[0]); AsInitializerList = true; - Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs, - CandidateSet, ConStart, ConEnd, Best, - CopyInitialization, AllowExplicit, - /*OnlyListConstructor=*/true, - InitListSyntax); + + // If the initializer list has no elements and T has a default constructor, + // the first phase is omitted. + if (ILE->getNumInits() != 0 || + (!DestRecordDecl->hasDeclaredDefaultConstructor() && + !DestRecordDecl->needsImplicitDefaultConstructor())) + Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs, + CandidateSet, ConStart, ConEnd, Best, + CopyInitialization, AllowExplicit, + /*OnlyListConstructor=*/true, + InitListSyntax); // Time to unwrap the init list. - InitListExpr *ILE = cast<InitListExpr>(Args[0]); Args = ILE->getInits(); NumArgs = ILE->getNumInits(); } @@ -2933,7 +2881,7 @@ static void TryConstructorInitialization(Sema &S, // C++11 [over.match.list]p1: // - If no viable initializer-list constructor is found, overload resolution // is performed again, where the candidate functions are all the - // constructors of the class T nad the argument list consists of the + // constructors of the class T and the argument list consists of the // elements of the initializer list. if (Result == OR_No_Viable_Function) { AsInitializerList = false; @@ -2951,13 +2899,13 @@ static void TryConstructorInitialization(Sema &S, return; } - // C++0x [dcl.init]p6: + // C++11 [dcl.init]p6: // If a program calls for the default initialization of an object // of a const-qualified type T, T shall be a class type with a // user-provided default constructor. if (Kind.getKind() == InitializationKind::IK_Default && Entity.getType().isConstQualified() && - cast<CXXConstructorDecl>(Best->Function)->isImplicit()) { + !cast<CXXConstructorDecl>(Best->Function)->isUserProvided()) { Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); return; } @@ -3018,6 +2966,12 @@ static void TryReferenceInitializationCore(Sema &S, Qualifiers T2Quals, InitializationSequence &Sequence); +static void TryValueInitialization(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + InitializationSequence &Sequence, + InitListExpr *InitList = 0); + static void TryListInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -3108,19 +3062,36 @@ static void TryListInitialization(Sema &S, return; } if (DestType->isRecordType()) { - if (S.RequireCompleteType(InitList->getLocStart(), DestType, S.PDiag())) { + if (S.RequireCompleteType(InitList->getLocStart(), DestType, 0)) { Sequence.setIncompleteTypeFailure(DestType); return; } + // C++11 [dcl.init.list]p3: + // - If T is an aggregate, aggregate initialization is performed. if (!DestType->isAggregateType()) { if (S.getLangOpts().CPlusPlus0x) { + // - Otherwise, if the initializer list has no elements and T is a + // class type with a default constructor, the object is + // value-initialized. + if (InitList->getNumInits() == 0) { + CXXRecordDecl *RD = DestType->getAsCXXRecordDecl(); + if (RD->hasDeclaredDefaultConstructor() || + RD->needsImplicitDefaultConstructor()) { + TryValueInitialization(S, Entity, Kind, Sequence, InitList); + return; + } + } + + // - Otherwise, if T is a specialization of std::initializer_list<E>, + // an initializer_list object constructed [...] + if (TryInitializerListConstruction(S, InitList, DestType, Sequence)) + return; + + // - Otherwise, if T is a class type, constructors are considered. Expr *Arg = InitList; - // A direct-initializer is not list-syntax, i.e. there's no special - // treatment of "A a({1, 2});". - TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType, - Sequence, - Kind.getKind() != InitializationKind::IK_Direct); + TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType, + Sequence, /*InitListSyntax*/true); } else Sequence.SetFailed( InitializationSequence::FK_InitListBadDestinationType); @@ -3605,7 +3576,11 @@ static void TryStringLiteralInitialization(Sema &S, static void TryValueInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - InitializationSequence &Sequence) { + InitializationSequence &Sequence, + InitListExpr *InitList) { + assert((!InitList || InitList->getNumInits() == 0) && + "Shouldn't use value-init for non-empty init lists"); + // C++98 [dcl.init]p5, C++11 [dcl.init]p7: // // To value-initialize an object of type T means: @@ -3616,17 +3591,15 @@ static void TryValueInitialization(Sema &S, if (const RecordType *RT = T->getAs<RecordType>()) { if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) { - // C++98: - // -- if T is a class type (clause 9) with a user-declared - // constructor (12.1), then the default constructor for T is - // called (and the initialization is ill-formed if T has no - // accessible default constructor); + bool NeedZeroInitialization = true; if (!S.getLangOpts().CPlusPlus0x) { + // C++98: + // -- if T is a class type (clause 9) with a user-declared constructor + // (12.1), then the default constructor for T is called (and the + // initialization is ill-formed if T has no accessible default + // constructor); if (ClassDecl->hasUserDeclaredConstructor()) - // FIXME: we really want to refer to a single subobject of the array, - // but Entity doesn't have a way to capture that (yet). - return TryConstructorInitialization(S, Entity, Kind, 0, 0, - T, Sequence); + NeedZeroInitialization = false; } else { // C++11: // -- if T is a class type (clause 9) with either no default constructor @@ -3634,19 +3607,28 @@ static void TryValueInitialization(Sema &S, // or deleted, then the object is default-initialized; CXXConstructorDecl *CD = S.LookupDefaultConstructor(ClassDecl); if (!CD || !CD->getCanonicalDecl()->isDefaulted() || CD->isDeleted()) - return TryConstructorInitialization(S, Entity, Kind, 0, 0, - T, Sequence); + NeedZeroInitialization = false; } // -- if T is a (possibly cv-qualified) non-union class type without a // user-provided or deleted default constructor, then the object is // zero-initialized and, if T has a non-trivial default constructor, // default-initialized; - if ((ClassDecl->getTagKind() == TTK_Class || - ClassDecl->getTagKind() == TTK_Struct)) { + // FIXME: The 'non-union' here is a defect (not yet assigned an issue + // number). Update the quotation when the defect is resolved. + if (NeedZeroInitialization) Sequence.AddZeroInitializationStep(Entity.getType()); - return TryConstructorInitialization(S, Entity, Kind, 0, 0, T, Sequence); - } + + // If this is list-value-initialization, pass the empty init list on when + // building the constructor call. This affects the semantics of a few + // things (such as whether an explicit default constructor can be called). + Expr *InitListAsExpr = InitList; + Expr **Args = InitList ? &InitListAsExpr : 0; + unsigned NumArgs = InitList ? 1 : 0; + bool InitListSyntax = InitList; + + return TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, T, + Sequence, InitListSyntax); } } @@ -4101,8 +4083,8 @@ InitializationSequence::InitializationSequence(Sema &S, AddArrayInitStep(DestType); } } - // Note: as a GNU C++ extension, we allow initialization of a - // class member from a parenthesized initializer list. + // Note: as a GNU C++ extension, we allow list-initialization of a + // class member of array type from a parenthesized initializer list. else if (S.getLangOpts().CPlusPlus && Entity.getKind() == InitializedEntity::EK_Member && Initializer && isa<InitListExpr>(Initializer)) { @@ -4409,7 +4391,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity, /// \param T The type of the temporary object, which must either be /// the type of the initializer expression or a superclass thereof. /// -/// \param Enter The entity being initialized. +/// \param Entity The entity being initialized. /// /// \param CurInit The initializer expression. /// @@ -4452,7 +4434,7 @@ static ExprResult CopyObject(Sema &S, SourceLocation Loc = getInitializationLoc(Entity, CurInit.get()); // Make sure that the type we are copying is complete. - if (S.RequireCompleteType(Loc, T, S.PDiag(diag::err_temp_copy_incomplete))) + if (S.RequireCompleteType(Loc, T, diag::err_temp_copy_incomplete)) return move(CurInit); // Perform overload resolution using the class's copy/move constructors. @@ -4516,7 +4498,7 @@ static ExprResult CopyObject(Sema &S, for (unsigned I = 1, N = Constructor->getNumParams(); I != N; ++I) { ParmVarDecl *Parm = Constructor->getParamDecl(I); if (S.RequireCompleteType(Loc, Parm->getType(), - S.PDiag(diag::err_call_incomplete_argument))) + diag::err_call_incomplete_argument)) break; // Build the default argument expression; we don't actually care @@ -4748,6 +4730,43 @@ PerformConstructorInitialization(Sema &S, return move(CurInit); } +/// Determine whether the specified InitializedEntity definitely has a lifetime +/// longer than the current full-expression. Conservatively returns false if +/// it's unclear. +static bool +InitializedEntityOutlivesFullExpression(const InitializedEntity &Entity) { + const InitializedEntity *Top = &Entity; + while (Top->getParent()) + Top = Top->getParent(); + + switch (Top->getKind()) { + case InitializedEntity::EK_Variable: + case InitializedEntity::EK_Result: + case InitializedEntity::EK_Exception: + case InitializedEntity::EK_Member: + case InitializedEntity::EK_New: + case InitializedEntity::EK_Base: + case InitializedEntity::EK_Delegating: + return true; + + case InitializedEntity::EK_ArrayElement: + case InitializedEntity::EK_VectorElement: + case InitializedEntity::EK_BlockElement: + case InitializedEntity::EK_ComplexElement: + // Could not determine what the full initialization is. Assume it might not + // outlive the full-expression. + return false; + + case InitializedEntity::EK_Parameter: + case InitializedEntity::EK_Temporary: + case InitializedEntity::EK_LambdaCapture: + // The entity being initialized might not outlive the full-expression. + return false; + } + + llvm_unreachable("unknown entity kind"); +} + ExprResult InitializationSequence::Perform(Sema &S, const InitializedEntity &Entity, @@ -4816,6 +4835,29 @@ InitializationSequence::Perform(Sema &S, if (Steps.empty()) return S.Owned((Expr *)0); + if (S.getLangOpts().CPlusPlus0x && Entity.getType()->isReferenceType() && + Args.size() == 1 && isa<InitListExpr>(Args.get()[0]) && + Entity.getKind() != InitializedEntity::EK_Parameter) { + // Produce a C++98 compatibility warning if we are initializing a reference + // from an initializer list. For parameters, we produce a better warning + // elsewhere. + Expr *Init = Args.get()[0]; + S.Diag(Init->getLocStart(), diag::warn_cxx98_compat_reference_list_init) + << Init->getSourceRange(); + } + + // Diagnose cases where we initialize a pointer to an array temporary, and the + // pointer obviously outlives the temporary. + if (Args.size() == 1 && Args.get()[0]->getType()->isArrayType() && + Entity.getType()->isPointerType() && + InitializedEntityOutlivesFullExpression(Entity)) { + Expr *Init = Args.get()[0]; + Expr::LValueClassification Kind = Init->ClassifyLValue(S.Context); + if (Kind == Expr::LV_ClassTemporary || Kind == Expr::LV_ArrayTemporary) + S.Diag(Init->getLocStart(), diag::warn_temporary_array_to_pointer_decay) + << Init->getSourceRange(); + } + QualType DestType = Entity.getType().getNonReferenceType(); // FIXME: Ugly hack around the fact that Entity.getType() is not // the same as Entity.getDecl()->getType() in cases involving type merging, @@ -4842,7 +4884,6 @@ InitializationSequence::Perform(Sema &S, case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: case SK_ConversionSequence: - case SK_ListConstructorCall: case SK_ListInitialization: case SK_UnwrapInitList: case SK_RewrapInitList: @@ -4862,6 +4903,7 @@ InitializationSequence::Perform(Sema &S, } case SK_ConstructorInitialization: + case SK_ListConstructorCall: case SK_ZeroInitialization: break; } @@ -5152,7 +5194,10 @@ InitializationSequence::Perform(Sema &S, InitializedEntity TempEntity = InitializedEntity::InitializeTemporary( Entity.getType().getNonReferenceType()); bool UseTemporary = Entity.getType()->isReferenceType(); - InitListExpr *InitList = cast<InitListExpr>(CurInit.get()); + assert(Args.size() == 1 && "expected a single argument for list init"); + InitListExpr *InitList = cast<InitListExpr>(Args.get()[0]); + S.Diag(InitList->getExprLoc(), diag::warn_cxx98_compat_ctor_list_init) + << InitList->getSourceRange(); MultiExprArg Arg(InitList->getInits(), InitList->getNumInits()); CurInit = PerformConstructorInitialization(S, UseTemporary ? TempEntity : Entity, @@ -5198,7 +5243,8 @@ InitializationSequence::Perform(Sema &S, step_iterator NextStep = Step; ++NextStep; if (NextStep != StepEnd && - NextStep->Kind == SK_ConstructorInitialization) { + (NextStep->Kind == SK_ConstructorInitialization || + NextStep->Kind == SK_ListConstructorCall)) { // The need for zero-initialization is recorded directly into // the call to the object's constructor within the next step. ConstructorInitRequiresZeroInit = true; @@ -5330,6 +5376,8 @@ InitializationSequence::Perform(Sema &S, } InitListExpr *ILE = cast<InitListExpr>(CurInit.take()); + S.Diag(ILE->getExprLoc(), diag::warn_cxx98_compat_initializer_list_init) + << ILE->getSourceRange(); unsigned NumInits = ILE->getNumInits(); SmallVector<Expr*, 16> Converted(NumInits); InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( @@ -6130,8 +6178,8 @@ Sema::CanPerformCopyInitialization(const InitializedEntity &Entity, Expr *InitE = Init.get(); assert(InitE && "No initialization expression"); - InitializationKind Kind = InitializationKind::CreateCopy(SourceLocation(), - SourceLocation()); + InitializationKind Kind + = InitializationKind::CreateCopy(InitE->getLocStart(), SourceLocation()); InitializationSequence Seq(*this, Entity, Kind, &InitE, 1); return !Seq.Failed(); } diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 6ef8d88bbe52..6414c6fbac97 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -54,9 +54,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, SourceRange IntroducerRange, TypeSourceInfo *MethodType, SourceLocation EndLoc, - llvm::ArrayRef<ParmVarDecl *> Params, - llvm::Optional<unsigned> ManglingNumber, - Decl *ContextDecl) { + llvm::ArrayRef<ParmVarDecl *> Params) { // C++11 [expr.prim.lambda]p5: // The closure type for a lambda-expression has a public inline function // call operator (13.5.4) whose parameters and return type are described by @@ -98,64 +96,76 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, P != PEnd; ++P) (*P)->setOwningFunction(Method); } - - // If we don't already have a mangling number for this lambda expression, - // allocate one now. - if (!ManglingNumber) { - ContextDecl = ExprEvalContexts.back().LambdaContextDecl; - - enum ContextKind { - Normal, - DefaultArgument, - DataMember, - StaticDataMember - } Kind = Normal; - - // Default arguments of member function parameters that appear in a class - // definition, as well as the initializers of data members, receive special - // treatment. Identify them. - if (ContextDecl) { - if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(ContextDecl)) { - if (const DeclContext *LexicalDC - = Param->getDeclContext()->getLexicalParent()) - if (LexicalDC->isRecord()) - Kind = DefaultArgument; - } else if (VarDecl *Var = dyn_cast<VarDecl>(ContextDecl)) { - if (Var->getDeclContext()->isRecord()) - Kind = StaticDataMember; - } else if (isa<FieldDecl>(ContextDecl)) { - Kind = DataMember; - } - } - - switch (Kind) { - case Normal: - if (CurContext->isDependentContext() || isInInlineFunction(CurContext)) - ManglingNumber = Context.getLambdaManglingNumber(Method); - else - ManglingNumber = 0; - - // There is no special context for this lambda. - ContextDecl = 0; - break; - - case StaticDataMember: - if (!CurContext->isDependentContext()) { - ManglingNumber = 0; - ContextDecl = 0; - break; - } - // Fall through to assign a mangling number. - - case DataMember: - case DefaultArgument: - ManglingNumber = ExprEvalContexts.back().getLambdaMangleContext() - .getManglingNumber(Method); - break; + + // Allocate a mangling number for this lambda expression, if the ABI + // requires one. + Decl *ContextDecl = ExprEvalContexts.back().LambdaContextDecl; + + enum ContextKind { + Normal, + DefaultArgument, + DataMember, + StaticDataMember + } Kind = Normal; + + // Default arguments of member function parameters that appear in a class + // definition, as well as the initializers of data members, receive special + // treatment. Identify them. + if (ContextDecl) { + if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(ContextDecl)) { + if (const DeclContext *LexicalDC + = Param->getDeclContext()->getLexicalParent()) + if (LexicalDC->isRecord()) + Kind = DefaultArgument; + } else if (VarDecl *Var = dyn_cast<VarDecl>(ContextDecl)) { + if (Var->getDeclContext()->isRecord()) + Kind = StaticDataMember; + } else if (isa<FieldDecl>(ContextDecl)) { + Kind = DataMember; } } - Class->setLambdaMangling(*ManglingNumber, ContextDecl); + // Itanium ABI [5.1.7]: + // In the following contexts [...] the one-definition rule requires closure + // types in different translation units to "correspond": + bool IsInNonspecializedTemplate = + !ActiveTemplateInstantiations.empty() || CurContext->isDependentContext(); + unsigned ManglingNumber; + switch (Kind) { + case Normal: + // -- the bodies of non-exported nonspecialized template functions + // -- the bodies of inline functions + if ((IsInNonspecializedTemplate && + !(ContextDecl && isa<ParmVarDecl>(ContextDecl))) || + isInInlineFunction(CurContext)) + ManglingNumber = Context.getLambdaManglingNumber(Method); + else + ManglingNumber = 0; + + // There is no special context for this lambda. + ContextDecl = 0; + break; + + case StaticDataMember: + // -- the initializers of nonspecialized static members of template classes + if (!IsInNonspecializedTemplate) { + ManglingNumber = 0; + ContextDecl = 0; + break; + } + // Fall through to assign a mangling number. + + case DataMember: + // -- the in-class initializers of class members + case DefaultArgument: + // -- default arguments appearing in class definitions + ManglingNumber = ExprEvalContexts.back().getLambdaMangleContext() + .getManglingNumber(Method); + break; + } + + Class->setLambdaMangling(ManglingNumber, ContextDecl); + return Method; } @@ -214,6 +224,141 @@ void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) { } } +static bool checkReturnValueType(const ASTContext &Ctx, const Expr *E, + QualType &DeducedType, + QualType &AlternateType) { + // Handle ReturnStmts with no expressions. + if (!E) { + if (AlternateType.isNull()) + AlternateType = Ctx.VoidTy; + + return Ctx.hasSameType(DeducedType, Ctx.VoidTy); + } + + QualType StrictType = E->getType(); + QualType LooseType = StrictType; + + // In C, enum constants have the type of their underlying integer type, + // not the enum. When inferring block return types, we should allow + // the enum type if an enum constant is used, unless the enum is + // anonymous (in which case there can be no variables of its type). + if (!Ctx.getLangOpts().CPlusPlus) { + const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()); + if (DRE) { + const Decl *D = DRE->getDecl(); + if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) { + const EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext()); + if (Enum->getDeclName() || Enum->getTypedefNameForAnonDecl()) + LooseType = Ctx.getTypeDeclType(Enum); + } + } + } + + // Special case for the first return statement we find. + // The return type has already been tentatively set, but we might still + // have an alternate type we should prefer. + if (AlternateType.isNull()) + AlternateType = LooseType; + + if (Ctx.hasSameType(DeducedType, StrictType)) { + // FIXME: The loose type is different when there are constants from two + // different enums. We could consider warning here. + if (AlternateType != Ctx.DependentTy) + if (!Ctx.hasSameType(AlternateType, LooseType)) + AlternateType = Ctx.VoidTy; + return true; + } + + if (Ctx.hasSameType(DeducedType, LooseType)) { + // Use DependentTy to signal that we're using an alternate type and may + // need to add casts somewhere. + AlternateType = Ctx.DependentTy; + return true; + } + + if (Ctx.hasSameType(AlternateType, StrictType) || + Ctx.hasSameType(AlternateType, LooseType)) { + DeducedType = AlternateType; + // Use DependentTy to signal that we're using an alternate type and may + // need to add casts somewhere. + AlternateType = Ctx.DependentTy; + return true; + } + + return false; +} + +void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) { + assert(CSI.HasImplicitReturnType); + + // First case: no return statements, implicit void return type. + ASTContext &Ctx = getASTContext(); + if (CSI.Returns.empty()) { + // It's possible there were simply no /valid/ return statements. + // In this case, the first one we found may have at least given us a type. + if (CSI.ReturnType.isNull()) + CSI.ReturnType = Ctx.VoidTy; + return; + } + + // Second case: at least one return statement has dependent type. + // Delay type checking until instantiation. + assert(!CSI.ReturnType.isNull() && "We should have a tentative return type."); + if (CSI.ReturnType->isDependentType()) + return; + + // Third case: only one return statement. Don't bother doing extra work! + SmallVectorImpl<ReturnStmt*>::iterator I = CSI.Returns.begin(), + E = CSI.Returns.end(); + if (I+1 == E) + return; + + // General case: many return statements. + // Check that they all have compatible return types. + // For now, that means "identical", with an exception for enum constants. + // (In C, enum constants have the type of their underlying integer type, + // not the type of the enum. C++ uses the type of the enum.) + QualType AlternateType; + + // We require the return types to strictly match here. + for (; I != E; ++I) { + const ReturnStmt *RS = *I; + const Expr *RetE = RS->getRetValue(); + if (!checkReturnValueType(Ctx, RetE, CSI.ReturnType, AlternateType)) { + // FIXME: This is a poor diagnostic for ReturnStmts without expressions. + Diag(RS->getLocStart(), + diag::err_typecheck_missing_return_type_incompatible) + << (RetE ? RetE->getType() : Ctx.VoidTy) << CSI.ReturnType + << isa<LambdaScopeInfo>(CSI); + // Don't bother fixing up the return statements in the block if some of + // them are unfixable anyway. + AlternateType = Ctx.VoidTy; + // Continue iterating so that we keep emitting diagnostics. + } + } + + // If our return statements turned out to be compatible, but we needed to + // pick a different return type, go through and fix the ones that need it. + if (AlternateType == Ctx.DependentTy) { + for (SmallVectorImpl<ReturnStmt*>::iterator I = CSI.Returns.begin(), + E = CSI.Returns.end(); + I != E; ++I) { + ReturnStmt *RS = *I; + Expr *RetE = RS->getRetValue(); + if (RetE->getType() == CSI.ReturnType) + continue; + + // Right now we only support integral fixup casts. + assert(CSI.ReturnType->isIntegralOrUnscopedEnumerationType()); + assert(RetE->getType()->isIntegralOrUnscopedEnumerationType()); + ExprResult Casted = ImpCastExprToType(RetE, CSI.ReturnType, + CK_IntegralCast); + assert(Casted.isUsable()); + RS->setRetValue(Casted.take()); + } + } +} + void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Declarator &ParamInfo, Scope *CurScope) { @@ -230,6 +375,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, TypeSourceInfo *MethodTyInfo; bool ExplicitParams = true; bool ExplicitResultType = true; + bool ContainsUnexpandedParameterPack = false; SourceLocation EndLoc; llvm::ArrayRef<ParmVarDecl *> Params; if (ParamInfo.getNumTypeObjects() == 0) { @@ -269,9 +415,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, FunctionProtoTypeLoc Proto = cast<FunctionProtoTypeLoc>(TL); Params = llvm::ArrayRef<ParmVarDecl *>(Proto.getParmArray(), Proto.getNumArgs()); + + // Check for unexpanded parameter packs in the method type. + if (MethodTyInfo->getType()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; } - CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, + CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params); if (ExplicitParams) @@ -287,7 +437,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, LambdaScopeInfo *LSI = enterLambdaScope(Method, Intro.Range, Intro.Default, ExplicitParams, ExplicitResultType, - (Method->getTypeQualifiers() & Qualifiers::Const) == 0); + !Method->isConst()); // Handle explicit captures. SourceLocation PrevCaptureLoc @@ -409,8 +559,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // Just ignore the ellipsis. } } else if (Var->isParameterPack()) { - Diag(C->Loc, diag::err_lambda_unexpanded_pack); - continue; + ContainsUnexpandedParameterPack = true; } TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef : @@ -419,6 +568,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, } finishLambdaExplicitCaptures(LSI); + LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; + // Add lambda parameters into scope. addLambdaParameters(Method, CurScope); @@ -441,7 +592,10 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope, LambdaScopeInfo *LSI = getCurLambda(); CXXRecordDecl *Class = LSI->Lambda; Class->setInvalidDecl(); - SmallVector<Decl*, 4> Fields(Class->field_begin(), Class->field_end()); + SmallVector<Decl*, 4> Fields; + for (RecordDecl::field_iterator i = Class->field_begin(), + e = Class->field_end(); i != e; ++i) + Fields.push_back(*i); ActOnFields(0, Class->getLocation(), Class, Fields, SourceLocation(), SourceLocation(), 0); CheckCompletedCXXClass(Class); @@ -578,6 +732,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, bool ExplicitParams; bool ExplicitResultType; bool LambdaExprNeedsCleanups; + bool ContainsUnexpandedParameterPack; llvm::SmallVector<VarDecl *, 4> ArrayIndexVars; llvm::SmallVector<unsigned, 4> ArrayIndexStarts; { @@ -588,6 +743,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, ExplicitParams = LSI->ExplicitParams; ExplicitResultType = !LSI->HasImplicitReturnType; LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups; + ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack; ArrayIndexVars.swap(LSI->ArrayIndexVars); ArrayIndexStarts.swap(LSI->ArrayIndexStarts); @@ -639,32 +795,14 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, // denotes the following type: // FIXME: Assumes current resolution to core issue 975. if (LSI->HasImplicitReturnType) { + deduceClosureReturnType(*LSI); + // - if there are no return statements in the // compound-statement, or all return statements return // either an expression of type void or no expression or // braced-init-list, the type void; if (LSI->ReturnType.isNull()) { LSI->ReturnType = Context.VoidTy; - } else { - // C++11 [expr.prim.lambda]p4: - // - if the compound-statement is of the form - // - // { attribute-specifier-seq[opt] return expression ; } - // - // the type of the returned expression after - // lvalue-to-rvalue conversion (4.1), array-to-pointer - // conver- sion (4.2), and function-to-pointer conversion - // (4.3); - // - // Since we're accepting the resolution to a post-C++11 core - // issue with a non-trivial extension, provide a warning (by - // default). - CompoundStmt *CompoundBody = cast<CompoundStmt>(Body); - if (!(CompoundBody->size() == 1 && - isa<ReturnStmt>(*CompoundBody->body_begin())) && - !Context.hasSameType(LSI->ReturnType, Context.VoidTy)) - Diag(IntroducerRange.getBegin(), - diag::ext_lambda_implies_void_return); } // Create a function type with the inferred return type. @@ -704,7 +842,10 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, addBlockPointerConversion(*this, IntroducerRange, Class, CallOperator); // Finalize the lambda class. - SmallVector<Decl*, 4> Fields(Class->field_begin(), Class->field_end()); + SmallVector<Decl*, 4> Fields; + for (RecordDecl::field_iterator i = Class->field_begin(), + e = Class->field_end(); i != e; ++i) + Fields.push_back(*i); ActOnFields(0, Class->getLocation(), Class, Fields, SourceLocation(), SourceLocation(), 0); CheckCompletedCXXClass(Class); @@ -717,7 +858,8 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, CaptureDefault, Captures, ExplicitParams, ExplicitResultType, CaptureInits, ArrayIndexVars, - ArrayIndexStarts, Body->getLocEnd()); + ArrayIndexStarts, Body->getLocEnd(), + ContainsUnexpandedParameterPack); // C++11 [expr.prim.lambda]p2: // A lambda-expression shall not appear in an unevaluated operand @@ -807,9 +949,7 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation, // Add a fake function body to the block. IR generation is responsible // for filling in the actual body, which cannot be expressed as an AST. - Block->setBody(new (Context) CompoundStmt(Context, 0, 0, - ConvLocation, - ConvLocation)); + Block->setBody(new (Context) CompoundStmt(ConvLocation)); // Create the block literal expression. Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType()); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 9f5138ba4a40..dad196b410c4 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -899,7 +899,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC && S->getParent() && !S->getParent()->isTemplateParamScope()) { // We've just searched the last template parameter scope and - // found nothing, so look into the the contexts between the + // found nothing, so look into the contexts between the // lexical and semantic declaration contexts returned by // findOuterContext(). This implements the name lookup behavior // of C++ [temp.local]p8. @@ -1004,7 +1004,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC && S->getParent() && !S->getParent()->isTemplateParamScope()) { // We've just searched the last template parameter scope and - // found nothing, so look into the the contexts between the + // found nothing, so look into the contexts between the // lexical and semantic declaration contexts returned by // findOuterContext(). This implements the name lookup behavior // of C++ [temp.local]p8. @@ -1100,15 +1100,12 @@ static NamedDecl *getVisibleDecl(NamedDecl *D) { /// begin. If the lookup criteria permits, name lookup may also search /// in the parent scopes. /// -/// @param Name The name of the entity that we are searching for. +/// @param [in,out] R Specifies the lookup to perform (e.g., the name to +/// look up and the lookup kind), and is updated with the results of lookup +/// including zero or more declarations and possibly additional information +/// used to diagnose ambiguities. /// -/// @param Loc If provided, the source location where we're performing -/// name lookup. At present, this is only used to produce diagnostics when -/// C library functions (like "malloc") are implicitly declared. -/// -/// @returns The result of name lookup, which includes zero or more -/// declarations and possibly additional information used to diagnose -/// ambiguities. +/// @returns \c true if lookup succeeded and false otherwise. bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) { DeclarationName Name = R.getLookupName(); if (!Name) return false; @@ -1231,7 +1228,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) { /// using directives by the given context. /// /// C++98 [namespace.qual]p2: -/// Given X::m (where X is a user-declared namespace), or given ::m +/// Given X::m (where X is a user-declared namespace), or given \::m /// (where X is the global namespace), let S be the set of all /// declarations of m in X and in the transitive closure of all /// namespaces nominated by using-directives in X and its used @@ -1244,6 +1241,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) { /// (namespace.udecl), S is the required set of declarations of /// m. Otherwise if the use of m is not one that allows a unique /// declaration to be chosen from S, the program is ill-formed. +/// /// C++98 [namespace.qual]p5: /// During the lookup of a qualified namespace member name, if the /// lookup finds more than one declaration of the member, and if one @@ -1636,22 +1634,12 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, } -/// @brief Produce a diagnostic describing the ambiguity that resulted +/// \brief Produce a diagnostic describing the ambiguity that resulted /// from name lookup. /// -/// @param Result The ambiguous name lookup result. -/// -/// @param Name The name of the entity that name lookup was -/// searching for. -/// -/// @param NameLoc The location of the name within the source code. +/// \param Result The result of the ambiguous lookup to be diagnosed. /// -/// @param LookupRange A source range that provides more -/// source-location information concerning the lookup itself. For -/// example, this range might highlight a nested-name-specifier that -/// precedes the name. -/// -/// @returns true +/// \returns true bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) { assert(Result.isAmbiguous() && "Lookup result must be ambiguous"); @@ -2444,10 +2432,11 @@ CXXConstructorDecl *Sema::LookupCopyingConstructor(CXXRecordDecl *Class, } /// \brief Look up the moving constructor for the given class. -CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class) { +CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class, + unsigned Quals) { SpecialMemberOverloadResult *Result = - LookupSpecialMember(Class, CXXMoveConstructor, false, - false, false, false, false); + LookupSpecialMember(Class, CXXMoveConstructor, Quals & Qualifiers::Const, + Quals & Qualifiers::Volatile, false, false, false); return cast_or_null<CXXConstructorDecl>(Result->getMethod()); } @@ -2488,12 +2477,14 @@ CXXMethodDecl *Sema::LookupCopyingAssignment(CXXRecordDecl *Class, /// \brief Look up the moving assignment operator for the given class. CXXMethodDecl *Sema::LookupMovingAssignment(CXXRecordDecl *Class, + unsigned Quals, bool RValueThis, unsigned ThisQuals) { assert(!(ThisQuals & ~(Qualifiers::Const | Qualifiers::Volatile)) && "non-const, non-volatile qualifiers for copy assignment this"); SpecialMemberOverloadResult *Result = - LookupSpecialMember(Class, CXXMoveAssignment, false, false, RValueThis, + LookupSpecialMember(Class, CXXMoveAssignment, Quals & Qualifiers::Const, + Quals & Qualifiers::Volatile, RValueThis, ThisQuals & Qualifiers::Const, ThisQuals & Qualifiers::Volatile); @@ -3147,7 +3138,8 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, namespace { -typedef llvm::StringMap<TypoCorrection, llvm::BumpPtrAllocator> TypoResultsMap; +typedef llvm::SmallVector<TypoCorrection, 1> TypoResultList; +typedef llvm::StringMap<TypoResultList, llvm::BumpPtrAllocator> TypoResultsMap; typedef std::map<unsigned, TypoResultsMap> TypoEditDistanceMap; static const unsigned MaxTypoDistanceResultSets = 5; @@ -3161,7 +3153,7 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer { /// /// The pointer value being set to the current DeclContext indicates /// whether there is a keyword with this name. - TypoEditDistanceMap BestResults; + TypoEditDistanceMap CorrectionResults; Sema &SemaRef; @@ -3180,23 +3172,28 @@ public: typedef TypoResultsMap::iterator result_iterator; typedef TypoEditDistanceMap::iterator distance_iterator; - distance_iterator begin() { return BestResults.begin(); } - distance_iterator end() { return BestResults.end(); } - void erase(distance_iterator I) { BestResults.erase(I); } - unsigned size() const { return BestResults.size(); } - bool empty() const { return BestResults.empty(); } - - TypoCorrection &operator[](StringRef Name) { - return BestResults.begin()->second[Name]; + distance_iterator begin() { return CorrectionResults.begin(); } + distance_iterator end() { return CorrectionResults.end(); } + void erase(distance_iterator I) { CorrectionResults.erase(I); } + unsigned size() const { return CorrectionResults.size(); } + bool empty() const { return CorrectionResults.empty(); } + + TypoResultList &operator[](StringRef Name) { + return CorrectionResults.begin()->second[Name]; } unsigned getBestEditDistance(bool Normalized) { - if (BestResults.empty()) + if (CorrectionResults.empty()) return (std::numeric_limits<unsigned>::max)(); - unsigned BestED = BestResults.begin()->first; + unsigned BestED = CorrectionResults.begin()->first; return Normalized ? TypoCorrection::NormalizeEditDistance(BestED) : BestED; } + + TypoResultsMap &getBestResults() { + return CorrectionResults.begin()->second; + } + }; } @@ -3251,19 +3248,31 @@ void TypoCorrectionConsumer::addName(StringRef Name, void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName(); - TypoResultsMap &Map = BestResults[Correction.getEditDistance(false)]; - - TypoCorrection &CurrentCorrection = Map[Name]; - if (!CurrentCorrection || - // FIXME: The following should be rolled up into an operator< on - // TypoCorrection with a more principled definition. - CurrentCorrection.isKeyword() < Correction.isKeyword() || - Correction.getAsString(SemaRef.getLangOpts()) < - CurrentCorrection.getAsString(SemaRef.getLangOpts())) - CurrentCorrection = Correction; + TypoResultList &CList = + CorrectionResults[Correction.getEditDistance(false)][Name]; + + if (!CList.empty() && !CList.back().isResolved()) + CList.pop_back(); + if (NamedDecl *NewND = Correction.getCorrectionDecl()) { + std::string CorrectionStr = Correction.getAsString(SemaRef.getLangOpts()); + for (TypoResultList::iterator RI = CList.begin(), RIEnd = CList.end(); + RI != RIEnd; ++RI) { + // If the Correction refers to a decl already in the result list, + // replace the existing result if the string representation of Correction + // comes before the current result alphabetically, then stop as there is + // nothing more to be done to add Correction to the candidate set. + if (RI->getCorrectionDecl() == NewND) { + if (CorrectionStr < RI->getAsString(SemaRef.getLangOpts())) + *RI = Correction; + return; + } + } + } + if (CList.empty() || Correction.isResolved()) + CList.push_back(Correction); - while (BestResults.size() > MaxTypoDistanceResultSets) - erase(llvm::prior(BestResults.end())); + while (CorrectionResults.size() > MaxTypoDistanceResultSets) + erase(llvm::prior(CorrectionResults.end())); } // Fill the supplied vector with the IdentifierInfo pointers for each piece of @@ -3348,7 +3357,7 @@ class NamespaceSpecifierSet { getNestedNameSpecifierIdentifiers(CurScopeSpec->getScopeRep(), CurNameSpecifierIdentifiers); // Build the list of identifiers that would be used for an absolute - // (from the global context) NestedNameSpecifier refering to the current + // (from the global context) NestedNameSpecifier referring to the current // context. for (DeclContextList::reverse_iterator C = CurContextChain.rbegin(), CEnd = CurContextChain.rend(); @@ -3515,7 +3524,16 @@ static void LookupPotentialTypoResult(Sema &SemaRef, /// \brief Add keywords to the consumer as possible typo corrections. static void AddKeywordsToConsumer(Sema &SemaRef, TypoCorrectionConsumer &Consumer, - Scope *S, CorrectionCandidateCallback &CCC) { + Scope *S, CorrectionCandidateCallback &CCC, + bool AfterNestedNameSpecifier) { + if (AfterNestedNameSpecifier) { + // For 'X::', we know exactly which keywords can appear next. + Consumer.addKeywordResult("template"); + if (CCC.WantExpressionKeywords) + Consumer.addKeywordResult("operator"); + return; + } + if (CCC.WantObjCSuper) Consumer.addKeywordResult("super"); @@ -3589,6 +3607,12 @@ static void AddKeywordsToConsumer(Sema &SemaRef, Consumer.addKeywordResult("nullptr"); } } + + if (SemaRef.getLangOpts().C11) { + // FIXME: We should not suggest _Alignof if the alignof macro + // is present. + Consumer.addKeywordResult("_Alignof"); + } } if (CCC.WantRemainingKeywords) { @@ -3777,6 +3801,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, bool SearchNamespaces = getLangOpts().CPlusPlus && (IsUnqualifiedLookup || (QualifiedDC && QualifiedDC->isNamespace())); + // In a few cases we *only* want to search for corrections bases on just + // adding or changing the nested name specifier. + bool AllowOnlyNNSChanges = Typo->getName().size() < 3; if (IsUnqualifiedLookup || SearchNamespaces) { // For unqualified lookup, look through all of the names that we have @@ -3802,7 +3829,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, } } - AddKeywordsToConsumer(*this, Consumer, S, CCC); + AddKeywordsToConsumer(*this, Consumer, S, CCC, SS && SS->isNotEmpty()); // If we haven't found anything, we're done. if (Consumer.empty()) { @@ -3813,8 +3840,8 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, return TypoCorrection(); } - // Make sure that the user typed at least 3 characters for each correction - // made. Otherwise, we don't even both looking at the results. + // Make sure the best edit distance (prior to adding any namespace qualifiers) + // is not more that about a third of the length of the typo's identifier. unsigned ED = Consumer.getBestEditDistance(true); if (ED > 0 && Typo->getName().size() / ED < 3) { // If this was an unqualified lookup, note that no correction was found. @@ -3854,19 +3881,43 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, for (TypoCorrectionConsumer::result_iterator I = DI->second.begin(), IEnd = DI->second.end(); I != IEnd; /* Increment in loop. */) { + // If we only want nested name specifier corrections, ignore potential + // corrections that have a different base identifier from the typo. + if (AllowOnlyNNSChanges && + I->second.front().getCorrectionAsIdentifierInfo() != Typo) { + TypoCorrectionConsumer::result_iterator Prev = I; + ++I; + DI->second.erase(Prev); + continue; + } + // If the item already has been looked up or is a keyword, keep it. // If a validator callback object was given, drop the correction // unless it passes validation. - if (I->second.isResolved()) { + bool Viable = false; + for (TypoResultList::iterator RI = I->second.begin(); + RI != I->second.end(); /* Increment in loop. */) { + TypoResultList::iterator Prev = RI; + ++RI; + if (Prev->isResolved()) { + if (!isCandidateViable(CCC, *Prev)) + RI = I->second.erase(Prev); + else + Viable = true; + } + } + if (Viable || I->second.empty()) { TypoCorrectionConsumer::result_iterator Prev = I; ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!Viable) DI->second.erase(Prev); continue; } + assert(I->second.size() == 1 && "Expected a single unresolved candidate"); // Perform name lookup on this name. - IdentifierInfo *Name = I->second.getCorrectionAsIdentifierInfo(); + TypoCorrection &Candidate = I->second.front(); + IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo(); LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext, EnteringContext, CCC.IsObjCIvarLookup); @@ -3874,7 +3925,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: case LookupResult::FoundUnresolvedValue: - QualifiedResults.push_back(I->second); + QualifiedResults.push_back(Candidate); // We didn't find this name in our scope, or didn't like what we found; // ignore it. { @@ -3895,18 +3946,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, for (LookupResult::iterator TRD = TmpRes.begin(), TRDEnd = TmpRes.end(); TRD != TRDEnd; ++TRD) - I->second.addCorrectionDecl(*TRD); + Candidate.addCorrectionDecl(*TRD); ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!isCandidateViable(CCC, Candidate)) DI->second.erase(Prev); break; } case LookupResult::Found: { TypoCorrectionConsumer::result_iterator Prev = I; - I->second.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>()); + Candidate.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>()); ++I; - if (!isCandidateViable(CCC, Prev->second)) + if (!isCandidateViable(CCC, Candidate)) DI->second.erase(Prev); break; } @@ -3978,10 +4029,10 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // No corrections remain... if (Consumer.empty()) return TypoCorrection(); - TypoResultsMap &BestResults = Consumer.begin()->second; - ED = TypoCorrection::NormalizeEditDistance(Consumer.begin()->first); + TypoResultsMap &BestResults = Consumer.getBestResults(); + ED = Consumer.getBestEditDistance(true); - if (ED > 0 && Typo->getName().size() / ED < 3) { + if (!AllowOnlyNNSChanges && ED > 0 && Typo->getName().size() / ED < 3) { // If this was an unqualified lookup and we believe the callback // object wouldn't have filtered out possible corrections, note // that no correction was found. @@ -3993,8 +4044,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // If only a single name remains, return that result. if (BestResults.size() == 1) { - const llvm::StringMapEntry<TypoCorrection> &Correction = *(BestResults.begin()); - const TypoCorrection &Result = Correction.second; + const TypoResultList &CorrectionList = BestResults.begin()->second; + const TypoCorrection &Result = CorrectionList.front(); + if (CorrectionList.size() != 1) return TypoCorrection(); // Don't correct to a keyword that's the same as the typo; the keyword // wasn't actually in scope. @@ -4012,7 +4064,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // some instances of CTC_Unknown, while WantRemainingKeywords is true // for CTC_Unknown but not for CTC_ObjCMessageReceiver. && CCC.WantObjCSuper && !CCC.WantRemainingKeywords - && BestResults["super"].isKeyword()) { + && BestResults["super"].front().isKeyword()) { // Prefer 'super' when we're completing in a message-receiver // context. @@ -4022,9 +4074,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] = BestResults["super"]; + UnqualifiedTyposCorrected[Typo] = BestResults["super"].front(); - return BestResults["super"]; + return BestResults["super"].front(); } // If this was an unqualified lookup and we believe the callback object did diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 5ece8f11e767..27deab226f91 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -18,6 +18,8 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" @@ -133,7 +135,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, // Proceed with constructing the ObjCPropertDecls. ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext); - if (ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(ClassDecl)) if (CDecl->IsClassExtension()) { Decl *Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc, @@ -144,10 +145,11 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, isOverridingProperty, TSI, MethodImplKind); if (Res) { - CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + CheckObjCPropertyAttributes(Res, AtLoc, Attributes, false); if (getLangOpts().ObjCAutoRefCount) checkARCPropertyDecl(*this, cast<ObjCPropertyDecl>(Res)); } + ActOnDocumentableDecl(Res); return Res; } @@ -161,11 +163,14 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, Res->setLexicalDeclContext(lexicalDC); // Validate the attributes on the @property. - CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + CheckObjCPropertyAttributes(Res, AtLoc, Attributes, + (isa<ObjCInterfaceDecl>(ClassDecl) || + isa<ObjCProtocolDecl>(ClassDecl))); if (getLangOpts().ObjCAutoRefCount) checkARCPropertyDecl(*this, Res); + ActOnDocumentableDecl(Res); return Res; } @@ -200,6 +205,37 @@ makePropertyAttributesAsWritten(unsigned Attributes) { return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten; } +static bool LocPropertyAttribute( ASTContext &Context, const char *attrName, + SourceLocation LParenLoc, SourceLocation &Loc) { + if (LParenLoc.isMacroID()) + return false; + + SourceManager &SM = Context.getSourceManager(); + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(LParenLoc); + // Try to load the file buffer. + bool invalidTemp = false; + StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Context.getLangOpts(), + file.begin(), tokenBegin, file.end()); + Token Tok; + do { + lexer.LexFromRawLexer(Tok); + if (Tok.is(tok::raw_identifier) && + StringRef(Tok.getRawIdentifierData(), Tok.getLength()) == attrName) { + Loc = Tok.getLocation(); + return true; + } + } while (Tok.isNot(tok::r_paren)); + return false; + +} + Decl * Sema::HandlePropertyInClassExtension(Scope *S, SourceLocation AtLoc, @@ -568,9 +604,70 @@ static void setImpliedPropertyAttributeForReadOnlyProperty( return; } +/// DiagnoseClassAndClassExtPropertyMismatch - diagnose inconsistant property +/// attribute declared in primary class and attributes overridden in any of its +/// class extensions. +static void +DiagnoseClassAndClassExtPropertyMismatch(Sema &S, ObjCInterfaceDecl *ClassDecl, + ObjCPropertyDecl *property) { + unsigned Attributes = property->getPropertyAttributesAsWritten(); + bool warn = (Attributes & ObjCDeclSpec::DQ_PR_readonly); + for (const ObjCCategoryDecl *CDecl = ClassDecl->getFirstClassExtension(); + CDecl; CDecl = CDecl->getNextClassExtension()) { + ObjCPropertyDecl *ClassExtProperty = 0; + for (ObjCContainerDecl::prop_iterator P = CDecl->prop_begin(), + E = CDecl->prop_end(); P != E; ++P) { + if ((*P)->getIdentifier() == property->getIdentifier()) { + ClassExtProperty = *P; + break; + } + } + if (ClassExtProperty) { + warn = false; + unsigned classExtPropertyAttr = + ClassExtProperty->getPropertyAttributesAsWritten(); + // We are issuing the warning that we postponed because class extensions + // can override readonly->readwrite and 'setter' attributes originally + // placed on class's property declaration now make sense in the overridden + // property. + if (Attributes & ObjCDeclSpec::DQ_PR_readonly) { + if (!classExtPropertyAttr || + (classExtPropertyAttr & ObjCDeclSpec::DQ_PR_readwrite)) + continue; + warn = true; + break; + } + } + } + if (warn) { + unsigned setterAttrs = (ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | + ObjCDeclSpec::DQ_PR_copy | + ObjCDeclSpec::DQ_PR_retain | + ObjCDeclSpec::DQ_PR_strong); + if (Attributes & setterAttrs) { + const char * which = + (Attributes & ObjCDeclSpec::DQ_PR_assign) ? + "assign" : + (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) ? + "unsafe_unretained" : + (Attributes & ObjCDeclSpec::DQ_PR_copy) ? + "copy" : + (Attributes & ObjCDeclSpec::DQ_PR_retain) ? + "retain" : "strong"; + + S.Diag(property->getLocation(), + diag::warn_objc_property_attr_mutually_exclusive) + << "readonly" << which; + } + } + + +} + /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared -/// as @synthesize or @dynamic. +/// as \@synthesize or \@dynamic. /// Decl *Sema::ActOnPropertyImplDecl(Scope *S, SourceLocation AtLoc, @@ -588,6 +685,9 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } if (PropertyIvarLoc.isInvalid()) PropertyIvarLoc = PropertyLoc; + SourceLocation PropertyDiagLoc = PropertyLoc; + if (PropertyDiagLoc.isInvalid()) + PropertyDiagLoc = ClassImpDecl->getLocStart(); ObjCPropertyDecl *property = 0; ObjCInterfaceDecl* IDecl = 0; // Find the class or category class where this property must have @@ -625,6 +725,27 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, return 0; } } + + if (Synthesize&& + (PIkind & ObjCPropertyDecl::OBJC_PR_readonly) && + property->hasAttr<IBOutletAttr>() && + !AtLoc.isValid()) { + Diag(IC->getLocation(), diag::warn_auto_readonly_iboutlet_property); + Diag(property->getLocation(), diag::note_property_declare); + SourceLocation readonlyLoc; + if (LocPropertyAttribute(Context, "readonly", + property->getLParenLoc(), readonlyLoc)) { + SourceLocation endLoc = + readonlyLoc.getLocWithOffset(strlen("readonly")-1); + SourceRange ReadonlySourceRange(readonlyLoc, endLoc); + Diag(property->getLocation(), + diag::note_auto_readonly_iboutlet_fixup_suggest) << + FixItHint::CreateReplacement(ReadonlySourceRange, "readwrite"); + } + } + + DiagnoseClassAndClassExtPropertyMismatch(*this, IDecl, property); + } else if ((CatImplClass = dyn_cast<ObjCCategoryImplDecl>(ClassImpDecl))) { if (Synthesize) { Diag(AtLoc, diag::error_synthesize_category_decl); @@ -654,6 +775,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, return 0; } ObjCIvarDecl *Ivar = 0; + bool CompleteTypeErr = false; + bool compat = true; // Check that we have a valid, previously declared ivar for @synthesize if (Synthesize) { // @synthesize @@ -664,7 +787,14 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared); QualType PropType = property->getType(); QualType PropertyIvarType = PropType.getNonReferenceType(); - + + if (RequireCompleteType(PropertyDiagLoc, PropertyIvarType, + diag::err_incomplete_synthesized_property, + property->getDeclName())) { + Diag(property->getLocation(), diag::note_property_declare); + CompleteTypeErr = true; + } + if (getLangOpts().ObjCAutoRefCount && (property->getPropertyAttributesAsWritten() & ObjCPropertyDecl::OBJC_PR_readonly) && @@ -680,14 +810,32 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, getLangOpts().getGC() != LangOptions::NonGC) { assert(!getLangOpts().ObjCAutoRefCount); if (PropertyIvarType.isObjCGCStrong()) { - Diag(PropertyLoc, diag::err_gc_weak_property_strong_type); + Diag(PropertyDiagLoc, diag::err_gc_weak_property_strong_type); Diag(property->getLocation(), diag::note_property_declare); } else { PropertyIvarType = Context.getObjCGCQualType(PropertyIvarType, Qualifiers::Weak); } } - + if (AtLoc.isInvalid()) { + // Check when default synthesizing a property that there is + // an ivar matching property name and issue warning; since this + // is the most common case of not using an ivar used for backing + // property in non-default synthesis case. + ObjCInterfaceDecl *ClassDeclared=0; + ObjCIvarDecl *originalIvar = + IDecl->lookupInstanceVariable(property->getIdentifier(), + ClassDeclared); + if (originalIvar) { + Diag(PropertyDiagLoc, + diag::warn_autosynthesis_property_ivar_match) + << PropertyId << (Ivar == 0) << PropertyIvar + << originalIvar->getIdentifier(); + Diag(property->getLocation(), diag::note_property_declare); + Diag(originalIvar->getLocation(), diag::note_ivar_decl); + } + } + if (!Ivar) { // In ARC, give the ivar a lifetime qualifier based on the // property attributes. @@ -699,7 +847,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // explicitly write an ownership attribute on the property. if (!property->hasWrittenStorageAttribute() && !(kind & ObjCPropertyDecl::OBJC_PR_strong)) { - Diag(PropertyLoc, + Diag(PropertyDiagLoc, diag::err_arc_objc_property_default_assign_on_object); Diag(property->getLocation(), diag::note_property_declare); } else { @@ -711,12 +859,12 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (const ObjCObjectPointerType *ObjT = PropertyIvarType->getAs<ObjCObjectPointerType>()) if (ObjT->getInterfaceDecl()->isArcWeakrefUnavailable()) { - Diag(PropertyLoc, diag::err_arc_weak_unavailable_property); + Diag(PropertyDiagLoc, diag::err_arc_weak_unavailable_property); Diag(property->getLocation(), diag::note_property_declare); err = true; } if (!err && !getLangOpts().ObjCRuntimeHasWeak) { - Diag(PropertyLoc, diag::err_arc_weak_no_runtime); + Diag(PropertyDiagLoc, diag::err_arc_weak_no_runtime); Diag(property->getLocation(), diag::note_property_declare); } } @@ -730,7 +878,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (kind & ObjCPropertyDecl::OBJC_PR_weak && !getLangOpts().ObjCAutoRefCount && getLangOpts().getGC() == LangOptions::NonGC) { - Diag(PropertyLoc, diag::error_synthesize_weak_non_arc_or_gc); + Diag(PropertyDiagLoc, diag::error_synthesize_weak_non_arc_or_gc); Diag(property->getLocation(), diag::note_property_declare); } @@ -739,17 +887,20 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, PropertyIvarType, /*Dinfo=*/0, ObjCIvarDecl::Private, (Expr *)0, true); + if (CompleteTypeErr) + Ivar->setInvalidDecl(); ClassImpDecl->addDecl(Ivar); IDecl->makeDeclVisibleInContext(Ivar); property->setPropertyIvarDecl(Ivar); - if (!getLangOpts().ObjCNonFragileABI) - Diag(PropertyLoc, diag::error_missing_property_ivar_decl) << PropertyId; + if (getLangOpts().ObjCRuntime.isFragile()) + Diag(PropertyDiagLoc, diag::error_missing_property_ivar_decl) + << PropertyId; // Note! I deliberately want it to fall thru so, we have a // a property implementation and to avoid future warnings. - } else if (getLangOpts().ObjCNonFragileABI && + } else if (getLangOpts().ObjCRuntime.isNonFragile() && !declaresSameEntity(ClassDeclared, IDecl)) { - Diag(PropertyLoc, diag::error_ivar_in_superclass_use) + Diag(PropertyDiagLoc, diag::error_ivar_in_superclass_use) << property->getDeclName() << Ivar->getDeclName() << ClassDeclared->getDeclName(); Diag(Ivar->getLocation(), diag::note_previous_access_declaration) @@ -759,8 +910,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, QualType IvarType = Context.getCanonicalType(Ivar->getType()); // Check that type of property and its ivar are type compatible. - if (Context.getCanonicalType(PropertyIvarType) != IvarType) { - bool compat = false; + if (!Context.hasSameType(PropertyIvarType, IvarType)) { + compat = false; if (isa<ObjCObjectPointerType>(PropertyIvarType) && isa<ObjCObjectPointerType>(IvarType)) compat = @@ -773,31 +924,32 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, == Compatible); } if (!compat) { - Diag(PropertyLoc, diag::error_property_ivar_type) + Diag(PropertyDiagLoc, diag::error_property_ivar_type) << property->getDeclName() << PropType << Ivar->getDeclName() << IvarType; Diag(Ivar->getLocation(), diag::note_ivar_decl); // Note! I deliberately want it to fall thru so, we have a // a property implementation and to avoid future warnings. } - - // FIXME! Rules for properties are somewhat different that those - // for assignments. Use a new routine to consolidate all cases; - // specifically for property redeclarations as well as for ivars. - QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType(); - QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType(); - if (lhsType != rhsType && - lhsType->isArithmeticType()) { - Diag(PropertyLoc, diag::error_property_ivar_type) - << property->getDeclName() << PropType - << Ivar->getDeclName() << IvarType; - Diag(Ivar->getLocation(), diag::note_ivar_decl); - // Fall thru - see previous comment + else { + // FIXME! Rules for properties are somewhat different that those + // for assignments. Use a new routine to consolidate all cases; + // specifically for property redeclarations as well as for ivars. + QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType(); + QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType(); + if (lhsType != rhsType && + lhsType->isArithmeticType()) { + Diag(PropertyDiagLoc, diag::error_property_ivar_type) + << property->getDeclName() << PropType + << Ivar->getDeclName() << IvarType; + Diag(Ivar->getLocation(), diag::note_ivar_decl); + // Fall thru - see previous comment + } } // __weak is explicit. So it works on Canonical type. if ((PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && getLangOpts().getGC() != LangOptions::NonGC)) { - Diag(PropertyLoc, diag::error_weak_property) + Diag(PropertyDiagLoc, diag::error_weak_property) << property->getDeclName() << Ivar->getDeclName(); Diag(Ivar->getLocation(), diag::note_ivar_decl); // Fall thru - see previous comment @@ -806,7 +958,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if ((property->getType()->isObjCObjectPointerType() || PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() && getLangOpts().getGC() != LangOptions::NonGC) { - Diag(PropertyLoc, diag::error_strong_property) + Diag(PropertyDiagLoc, diag::error_strong_property) << property->getDeclName() << Ivar->getDeclName(); // Fall thru - see previous comment } @@ -815,7 +967,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, checkARCPropertyImpl(*this, PropertyLoc, property, Ivar); } else if (PropertyIvar) // @dynamic - Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl); + Diag(PropertyDiagLoc, diag::error_dynamic_property_ivar_decl); assert (property && "ActOnPropertyImplDecl - property declaration missing"); ObjCPropertyImplDecl *PIDecl = @@ -825,9 +977,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, ObjCPropertyImplDecl::Synthesize : ObjCPropertyImplDecl::Dynamic), Ivar, PropertyIvarLoc); + + if (CompleteTypeErr || !compat) + PIDecl->setInvalidDecl(); + if (ObjCMethodDecl *getterMethod = property->getGetterMethodDecl()) { getterMethod->createImplicitParams(Context, IDecl); - if (getLangOpts().CPlusPlus && Synthesize && + if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr && Ivar->getType()->isRecordType()) { // For Objective-C++, need to synthesize the AST for the IVAR object to be // returned by the getter as it must conform to C++'s copy-return rules. @@ -862,8 +1018,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } if (ObjCMethodDecl *setterMethod = property->getSetterMethodDecl()) { setterMethod->createImplicitParams(Context, IDecl); - if (getLangOpts().CPlusPlus && Synthesize - && Ivar->getType()->isRecordType()) { + if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr && + Ivar->getType()->isRecordType()) { // FIXME. Eventually we want to do this for Objective-C as well. ImplicitParamDecl *SelfDecl = setterMethod->getSelfDecl(); DeclRefExpr *SelfExpr = @@ -916,7 +1072,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } IC->addPropertyImplementation(PIDecl); if (getLangOpts().ObjCDefaultSynthProperties && - getLangOpts().ObjCNonFragileABI2 && + getLangOpts().ObjCRuntime.isNonFragile() && !IDecl->isObjCRequiresPropertyDefs()) { // Diagnose if an ivar was lazily synthesdized due to a previous // use and if 1) property is @dynamic or 2) property is synthesized @@ -941,7 +1097,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (Synthesize) if (ObjCPropertyImplDecl *PPIDecl = CatImplClass->FindPropertyImplIvarDecl(PropertyIvar)) { - Diag(PropertyLoc, diag::error_duplicate_ivar_use) + Diag(PropertyDiagLoc, diag::error_duplicate_ivar_use) << PropertyId << PPIDecl->getPropertyDecl()->getIdentifier() << PropertyIvar; Diag(PPIDecl->getLocation(), diag::note_previous_use); @@ -949,7 +1105,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (ObjCPropertyImplDecl *PPIDecl = CatImplClass->FindPropertyImplDecl(PropertyId)) { - Diag(PropertyLoc, diag::error_property_implemented) << PropertyId; + Diag(PropertyDiagLoc, diag::error_property_implemented) << PropertyId; Diag(PPIDecl->getLocation(), diag::note_previous_declaration); return 0; } @@ -1030,21 +1186,42 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, ObjCMethodDecl *GetterMethod, SourceLocation Loc) { - if (GetterMethod && - !Context.hasSameType(GetterMethod->getResultType().getNonReferenceType(), - property->getType().getNonReferenceType())) { - AssignConvertType result = Incompatible; - if (property->getType()->isObjCObjectPointerType()) - result = CheckAssignmentConstraints(Loc, GetterMethod->getResultType(), - property->getType()); - if (result != Compatible) { - Diag(Loc, diag::warn_accessor_property_type_mismatch) - << property->getDeclName() - << GetterMethod->getSelector(); - Diag(GetterMethod->getLocation(), diag::note_declared_at); - return true; + if (!GetterMethod) + return false; + QualType GetterType = GetterMethod->getResultType().getNonReferenceType(); + QualType PropertyIvarType = property->getType().getNonReferenceType(); + bool compat = Context.hasSameType(PropertyIvarType, GetterType); + if (!compat) { + if (isa<ObjCObjectPointerType>(PropertyIvarType) && + isa<ObjCObjectPointerType>(GetterType)) + compat = + Context.canAssignObjCInterfaces( + GetterType->getAs<ObjCObjectPointerType>(), + PropertyIvarType->getAs<ObjCObjectPointerType>()); + else if (CheckAssignmentConstraints(Loc, GetterType, PropertyIvarType) + != Compatible) { + Diag(Loc, diag::error_property_accessor_type) + << property->getDeclName() << PropertyIvarType + << GetterMethod->getSelector() << GetterType; + Diag(GetterMethod->getLocation(), diag::note_declared_at); + return true; + } else { + compat = true; + QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType(); + QualType rhsType =Context.getCanonicalType(GetterType).getUnqualifiedType(); + if (lhsType != rhsType && lhsType->isArithmeticType()) + compat = false; } } + + if (!compat) { + Diag(Loc, diag::warn_accessor_property_type_mismatch) + << property->getDeclName() + << GetterMethod->getSelector(); + Diag(GetterMethod->getLocation(), diag::note_declared_at); + return true; + } + return false; } @@ -1059,11 +1236,11 @@ void Sema::ComparePropertiesInBaseAndSuper(ObjCInterfaceDecl *IDecl) { // FIXME: O(N^2) for (ObjCInterfaceDecl::prop_iterator S = SDecl->prop_begin(), E = SDecl->prop_end(); S != E; ++S) { - ObjCPropertyDecl *SuperPDecl = (*S); + ObjCPropertyDecl *SuperPDecl = *S; // Does property in super class has declaration in current class? for (ObjCInterfaceDecl::prop_iterator I = IDecl->prop_begin(), E = IDecl->prop_end(); I != E; ++I) { - ObjCPropertyDecl *PDecl = (*I); + ObjCPropertyDecl *PDecl = *I; if (SuperPDecl->getIdentifier() == PDecl->getIdentifier()) DiagnosePropertyMismatch(PDecl, SuperPDecl, SDecl->getIdentifier()); @@ -1085,29 +1262,29 @@ Sema::MatchOneProtocolPropertiesInClass(Decl *CDecl, if (!CatDecl->IsClassExtension()) for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Pr = (*P); + ObjCPropertyDecl *Pr = *P; ObjCCategoryDecl::prop_iterator CP, CE; // Is this property already in category's list of properties? for (CP = CatDecl->prop_begin(), CE = CatDecl->prop_end(); CP!=CE; ++CP) - if ((*CP)->getIdentifier() == Pr->getIdentifier()) + if (CP->getIdentifier() == Pr->getIdentifier()) break; if (CP != CE) // Property protocol already exist in class. Diagnose any mismatch. - DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier()); + DiagnosePropertyMismatch(*CP, Pr, PDecl->getIdentifier()); } return; } for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Pr = (*P); + ObjCPropertyDecl *Pr = *P; ObjCInterfaceDecl::prop_iterator CP, CE; // Is this property already in class's list of properties? for (CP = IDecl->prop_begin(), CE = IDecl->prop_end(); CP != CE; ++CP) - if ((*CP)->getIdentifier() == Pr->getIdentifier()) + if (CP->getIdentifier() == Pr->getIdentifier()) break; if (CP != CE) // Property protocol already exist in class. Diagnose any mismatch. - DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier()); + DiagnosePropertyMismatch(*CP, Pr, PDecl->getIdentifier()); } } @@ -1223,7 +1400,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl, if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; PropMap[Prop->getIdentifier()] = Prop; } // scan through class's protocols. @@ -1236,7 +1413,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl, if (!CATDecl->IsClassExtension()) for (ObjCContainerDecl::prop_iterator P = CATDecl->prop_begin(), E = CATDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; PropMap[Prop->getIdentifier()] = Prop; } // scan through class's protocols. @@ -1247,7 +1424,7 @@ void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl, else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[Prop->getIdentifier()]; // Exclude property for protocols which conform to class's super-class, // as super-class has to implement the property. @@ -1273,7 +1450,7 @@ static void CollectClassPropertyImplementations(ObjCContainerDecl *CDecl, if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; PropMap[Prop->getIdentifier()] = Prop; } for (ObjCInterfaceDecl::all_protocol_iterator @@ -1284,7 +1461,7 @@ static void CollectClassPropertyImplementations(ObjCContainerDecl *CDecl, else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; if (!PropMap.count(Prop->getIdentifier())) PropMap[Prop->getIdentifier()] = Prop; } @@ -1316,7 +1493,7 @@ ObjCPropertyDecl *Sema::LookupPropertyDecl(const ObjCContainerDecl *CDecl, dyn_cast<ObjCInterfaceDecl>(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; if (Prop->getIdentifier() == II) return Prop; } @@ -1333,7 +1510,7 @@ ObjCPropertyDecl *Sema::LookupPropertyDecl(const ObjCContainerDecl *CDecl, dyn_cast<ObjCProtocolDecl>(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { - ObjCPropertyDecl *Prop = (*P); + ObjCPropertyDecl *Prop = *P; if (Prop->getIdentifier() == II) return Prop; } @@ -1358,8 +1535,8 @@ static IdentifierInfo * getDefaultSynthIvarName(ObjCPropertyDecl *Prop, return &Ctx.Idents.get(ivarName.str()); } -/// DefaultSynthesizeProperties - This routine default synthesizes all -/// properties which must be synthesized in class's @implementation. +/// \brief Default synthesizes all properties which must be synthesized +/// in class's \@implementation. void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCInterfaceDecl *IDecl) { @@ -1402,16 +1579,21 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, // aren't really synthesized at a particular location; they just exist. // Saying that they are located at the @implementation isn't really going // to help users. - ActOnPropertyImplDecl(S, SourceLocation(), SourceLocation(), - true, - /* property = */ Prop->getIdentifier(), - /* ivar = */ getDefaultSynthIvarName(Prop, Context), - SourceLocation()); + ObjCPropertyImplDecl *PIDecl = dyn_cast_or_null<ObjCPropertyImplDecl>( + ActOnPropertyImplDecl(S, SourceLocation(), SourceLocation(), + true, + /* property = */ Prop->getIdentifier(), + /* ivar = */ getDefaultSynthIvarName(Prop, Context), + Prop->getLocation())); + if (PIDecl) { + Diag(Prop->getLocation(), diag::warn_missing_explicit_synthesis); + Diag(IMPDecl->getLocation(), diag::note_while_in_implementation); + } } } void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { - if (!LangOpts.ObjCDefaultSynthProperties || !LangOpts.ObjCNonFragileABI2) + if (!LangOpts.ObjCDefaultSynthProperties || LangOpts.ObjCRuntime.isFragile()) return; ObjCImplementationDecl *IC=dyn_cast_or_null<ObjCImplementationDecl>(D); if (!IC) @@ -1423,7 +1605,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl *CDecl, - const llvm::DenseSet<Selector>& InsMap) { + const SelectorSet &InsMap) { llvm::DenseMap<IdentifierInfo *, ObjCPropertyDecl*> SuperPropMap; if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) CollectSuperClassPropertyImplementations(IDecl, SuperPropMap); @@ -1437,7 +1619,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, for (ObjCImplDecl::propimpl_iterator I = IMPDecl->propimpl_begin(), EI = IMPDecl->propimpl_end(); I != EI; ++I) - PropImplMap.insert((*I)->getPropertyDecl()); + PropImplMap.insert(I->getPropertyDecl()); for (llvm::DenseMap<IdentifierInfo *, ObjCPropertyDecl*>::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { @@ -1455,7 +1637,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, << Prop->getDeclName() << Prop->getGetterName(); Diag(Prop->getLocation(), diag::note_property_declare); - if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2) + if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile()) if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CDecl)) if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) Diag(RID->getLocation(), diag::note_suppressed_class_declare); @@ -1470,7 +1652,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, << Prop->getDeclName() << Prop->getSetterName(); Diag(Prop->getLocation(), diag::note_property_declare); - if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCNonFragileABI2) + if (LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile()) if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CDecl)) if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) Diag(RID->getLocation(), diag::note_suppressed_class_declare); @@ -1487,7 +1669,7 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, for (ObjCContainerDecl::prop_iterator I = IDecl->prop_begin(), E = IDecl->prop_end(); I != E; ++I) { - ObjCPropertyDecl *Property = (*I); + ObjCPropertyDecl *Property = *I; ObjCMethodDecl *GetterMethod = 0; ObjCMethodDecl *SetterMethod = 0; bool LookedUpGetterSetter = false; @@ -1753,11 +1935,24 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, AddInstanceMethodToGlobalPool(GetterMethod); if (SetterMethod) AddInstanceMethodToGlobalPool(SetterMethod); + + ObjCInterfaceDecl *CurrentClass = dyn_cast<ObjCInterfaceDecl>(CD); + if (!CurrentClass) { + if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(CD)) + CurrentClass = Cat->getClassInterface(); + else if (ObjCImplDecl *Impl = dyn_cast<ObjCImplDecl>(CD)) + CurrentClass = Impl->getClassInterface(); + } + if (GetterMethod) + CheckObjCMethodOverrides(GetterMethod, CurrentClass, Sema::RTC_Unknown); + if (SetterMethod) + CheckObjCMethodOverrides(SetterMethod, CurrentClass, Sema::RTC_Unknown); } void Sema::CheckObjCPropertyAttributes(Decl *PDecl, SourceLocation Loc, - unsigned &Attributes) { + unsigned &Attributes, + bool propertyInPrimaryClass) { // FIXME: Improve the reported location. if (!PDecl || PDecl->isInvalidDecl()) return; @@ -1780,9 +1975,18 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, return; } + if (propertyInPrimaryClass) { + // we postpone most property diagnosis until class's implementation + // because, its readonly attribute may be overridden in its class + // extensions making other attributes, which make no sense, to make sense. + if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && + (Attributes & ObjCDeclSpec::DQ_PR_readwrite)) + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "readonly" << "readwrite"; + } // readonly and readwrite/assign/retain/copy conflict. - if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && - (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | + else if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && + (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | @@ -1939,8 +2143,7 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, && getLangOpts().getGC() == LangOptions::GCOnly && PropertyTy->isBlockPointerType()) Diag(Loc, diag::warn_objc_property_copy_missing_on_block); - else if (getLangOpts().ObjCAutoRefCount && - (Attributes & ObjCDeclSpec::DQ_PR_retain) && + else if ((Attributes & ObjCDeclSpec::DQ_PR_retain) && !(Attributes & ObjCDeclSpec::DQ_PR_readonly) && !(Attributes & ObjCDeclSpec::DQ_PR_strong) && PropertyTy->isBlockPointerType()) diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 50230f068dd5..a8744899d70c 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -28,6 +28,7 @@ #include "clang/Basic/PartialDiagnostic.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include <algorithm> @@ -388,13 +389,22 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, const unsigned ToWidth = Ctx.getIntWidth(ToType); if (FromWidth > ToWidth || - (FromWidth == ToWidth && FromSigned != ToSigned)) { + (FromWidth == ToWidth && FromSigned != ToSigned) || + (FromSigned && !ToSigned)) { // Not all values of FromType can be represented in ToType. llvm::APSInt InitializerValue; const Expr *Initializer = IgnoreNarrowingConversion(Converted); - if (Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) { - ConstantValue = APValue(InitializerValue); - + if (!Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) { + // Such conversions on variables are always narrowing. + return NK_Variable_Narrowing; + } + bool Narrowing = false; + if (FromWidth < ToWidth) { + // Negative -> unsigned is narrowing. Otherwise, more bits is never + // narrowing. + if (InitializerValue.isSigned() && InitializerValue.isNegative()) + Narrowing = true; + } else { // Add a bit to the InitializerValue so we don't have to worry about // signed vs. unsigned comparisons. InitializerValue = InitializerValue.extend( @@ -406,13 +416,13 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth()); ConvertedValue.setIsSigned(InitializerValue.isSigned()); // If the result is different, this was a narrowing conversion. - if (ConvertedValue != InitializerValue) { - ConstantType = Initializer->getType(); - return NK_Constant_Narrowing; - } - } else { - // Variables are always narrowings. - return NK_Variable_Narrowing; + if (ConvertedValue != InitializerValue) + Narrowing = true; + } + if (Narrowing) { + ConstantType = Initializer->getType(); + ConstantValue = APValue(InitializerValue); + return NK_Constant_Narrowing; } } return NK_Not_Narrowing; @@ -541,6 +551,7 @@ static MakeDeductionFailureInfo(ASTContext &Context, TemplateDeductionInfo &Info) { OverloadCandidate::DeductionFailureInfo Result; Result.Result = static_cast<unsigned>(TDK); + Result.HasDiagnostic = false; Result.Data = 0; switch (TDK) { case Sema::TDK_Success: @@ -567,6 +578,12 @@ static MakeDeductionFailureInfo(ASTContext &Context, case Sema::TDK_SubstitutionFailure: Result.Data = Info.take(); + if (Info.hasSFINAEDiagnostic()) { + PartialDiagnosticAt *Diag = new (Result.Diagnostic) PartialDiagnosticAt( + SourceLocation(), PartialDiagnostic::NullDiagnostic()); + Info.takeSFINAEDiagnostic(*Diag); + Result.HasDiagnostic = true; + } break; case Sema::TDK_NonDeducedMismatch: @@ -594,8 +611,12 @@ void OverloadCandidate::DeductionFailureInfo::Destroy() { break; case Sema::TDK_SubstitutionFailure: - // FIXME: Destroy the template arugment list? + // FIXME: Destroy the template argument list? Data = 0; + if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) { + Diag->~PartialDiagnosticAt(); + HasDiagnostic = false; + } break; // Unhandled @@ -605,6 +626,13 @@ void OverloadCandidate::DeductionFailureInfo::Destroy() { } } +PartialDiagnosticAt * +OverloadCandidate::DeductionFailureInfo::getSFINAEDiagnostic() { + if (HasDiagnostic) + return static_cast<PartialDiagnosticAt*>(static_cast<void*>(Diagnostic)); + return 0; +} + TemplateParameter OverloadCandidate::DeductionFailureInfo::getTemplateParameter() { switch (static_cast<Sema::TemplateDeductionResult>(Result)) { @@ -707,9 +735,12 @@ OverloadCandidate::DeductionFailureInfo::getSecondArg() { } void OverloadCandidateSet::clear() { - for (iterator i = begin(), e = end(); i != e; ++i) + for (iterator i = begin(), e = end(); i != e; ++i) { for (unsigned ii = 0, ie = i->NumConversions; ii != ie; ++ii) i->Conversions[ii].~ImplicitConversionSequence(); + if (!i->Viable && i->FailureKind == ovl_fail_bad_deduction) + i->DeductionFailure.Destroy(); + } NumInlineSequences = 0; Candidates.clear(); Functions.clear(); @@ -1657,7 +1688,7 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) { // We have already pre-calculated the promotion type, so this is trivial. if (ToType->isIntegerType() && - !RequireCompleteType(From->getLocStart(), FromType, PDiag())) + !RequireCompleteType(From->getLocStart(), FromType, 0)) return Context.hasSameUnqualifiedType(ToType, FromEnumType->getDecl()->getPromotionType()); } @@ -1987,7 +2018,7 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, if (getLangOpts().CPlusPlus && FromPointeeType->isRecordType() && ToPointeeType->isRecordType() && !Context.hasSameUnqualifiedType(FromPointeeType, ToPointeeType) && - !RequireCompleteType(From->getLocStart(), FromPointeeType, PDiag()) && + !RequireCompleteType(From->getLocStart(), FromPointeeType, 0) && IsDerivedFrom(FromPointeeType, ToPointeeType)) { ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType, @@ -2469,7 +2500,7 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, /// for equality of their argument types. Caller has already checked that /// they have same number of arguments. This routine assumes that Objective-C /// pointer types which only differ in their protocol qualifiers are equal. -/// If the parameters are different, ArgPos will have the the parameter index +/// If the parameters are different, ArgPos will have the parameter index /// of the first different parameter. bool Sema::FunctionArgTypesAreEqual(const FunctionProtoType *OldType, const FunctionProtoType *NewType, @@ -2531,13 +2562,17 @@ bool Sema::CheckPointerConversion(Expr *From, QualType ToType, Kind = CK_BitCast; - if (!IsCStyleOrFunctionalCast && - Context.hasSameUnqualifiedType(From->getType(), Context.BoolTy) && - From->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) - DiagRuntimeBehavior(From->getExprLoc(), From, - PDiag(diag::warn_impcast_bool_to_null_pointer) - << ToType << From->getSourceRange()); - + if (!IsCStyleOrFunctionalCast && !FromType->isAnyPointerType() && + From->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull) == + Expr::NPCK_ZeroExpression) { + if (Context.hasSameUnqualifiedType(From->getType(), Context.BoolTy)) + DiagRuntimeBehavior(From->getExprLoc(), From, + PDiag(diag::warn_impcast_bool_to_null_pointer) + << ToType << From->getSourceRange()); + else if (!isUnevaluatedContext()) + Diag(From->getExprLoc(), diag::warn_non_literal_null_pointer) + << ToType << From->getSourceRange(); + } if (const PointerType *ToPtrType = ToType->getAs<PointerType>()) { if (const PointerType *FromPtrType = FromType->getAs<PointerType>()) { QualType FromPointeeType = FromPtrType->getPointeeType(), @@ -2616,7 +2651,7 @@ bool Sema::IsMemberPointerConversion(Expr *From, QualType FromType, QualType ToClass(ToTypePtr->getClass(), 0); if (!Context.hasSameUnqualifiedType(FromClass, ToClass) && - !RequireCompleteType(From->getLocStart(), ToClass, PDiag()) && + !RequireCompleteType(From->getLocStart(), ToClass, 0) && IsDerivedFrom(ToClass, FromClass)) { ConvertedType = Context.getMemberPointerType(FromTypePtr->getPointeeType(), ToClass.getTypePtr()); @@ -2923,7 +2958,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType, S.IsDerivedFrom(From->getType(), ToType))) ConstructorsOnly = true; - S.RequireCompleteType(From->getLocStart(), ToType, S.PDiag()); + S.RequireCompleteType(From->getLocStart(), ToType, 0); // RequireCompleteType may have returned true due to some invalid decl // during template instantiation, but ToType may be complete enough now // to try to recover. @@ -3001,8 +3036,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType, // Enumerate conversion functions, if we're allowed to. if (ConstructorsOnly || isa<InitListExpr>(From)) { - } else if (S.RequireCompleteType(From->getLocStart(), From->getType(), - S.PDiag(0) << From->getSourceRange())) { + } else if (S.RequireCompleteType(From->getLocStart(), From->getType(), 0)) { // No conversion functions from incomplete types. } else if (const RecordType *FromRecordType = From->getType()->getAs<RecordType>()) { @@ -3848,7 +3882,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, ObjCLifetimeConversion = false; if (UnqualT1 == UnqualT2) { // Nothing to do. - } else if (!RequireCompleteType(Loc, OrigT2, PDiag()) && + } else if (!RequireCompleteType(Loc, OrigT2, 0) && IsDerivedFrom(UnqualT2, UnqualT1)) DerivedToBase = true; else if (UnqualT1->isObjCObjectOrInterfaceType() && @@ -4135,7 +4169,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType, // qualifier. // This is also the point where rvalue references and lvalue inits no longer // go together. - if (!isRValRef && !T1.isConstQualified()) + if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) return ICS; // -- If the initializer expression @@ -4313,7 +4347,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, // We need a complete type for what follows. Incomplete types can never be // initialized from init lists. - if (S.RequireCompleteType(From->getLocStart(), ToType, S.PDiag())) + if (S.RequireCompleteType(From->getLocStart(), ToType, 0)) return Result; // C++11 [over.ics.list]p2: @@ -4995,29 +5029,9 @@ static bool isIntegralOrEnumerationType(QualType T, bool AllowScopedEnum) { /// \param Loc The source location of the construct that requires the /// conversion. /// -/// \param FromE The expression we're converting from. -/// -/// \param NotIntDiag The diagnostic to be emitted if the expression does not -/// have integral or enumeration type. -/// -/// \param IncompleteDiag The diagnostic to be emitted if the expression has -/// incomplete class type. +/// \param From The expression we're converting from. /// -/// \param ExplicitConvDiag The diagnostic to be emitted if we're calling an -/// explicit conversion function (because no implicit conversion functions -/// were available). This is a recovery mode. -/// -/// \param ExplicitConvNote The note to be emitted with \p ExplicitConvDiag, -/// showing which conversion was picked. -/// -/// \param AmbigDiag The diagnostic to be emitted if there is more than one -/// conversion function that could convert to integral or enumeration type. -/// -/// \param AmbigNote The note to be emitted with \p AmbigDiag for each -/// usable conversion function. -/// -/// \param ConvDiag The diagnostic to be emitted if we are calling a conversion -/// function, which may be an extension in this case. +/// \param Diagnoser Used to output any diagnostics. /// /// \param AllowScopedEnumerations Specifies whether conversions to scoped /// enumerations should be considered. @@ -5026,13 +5040,7 @@ static bool isIntegralOrEnumerationType(QualType T, bool AllowScopedEnum) { /// successful. ExprResult Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, - const PartialDiagnostic &NotIntDiag, - const PartialDiagnostic &IncompleteDiag, - const PartialDiagnostic &ExplicitConvDiag, - const PartialDiagnostic &ExplicitConvNote, - const PartialDiagnostic &AmbigDiag, - const PartialDiagnostic &AmbigNote, - const PartialDiagnostic &ConvDiag, + ICEConvertDiagnoser &Diagnoser, bool AllowScopedEnumerations) { // We can't perform any more checking for type-dependent expressions. if (From->isTypeDependent()) @@ -5056,13 +5064,25 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, // expression of integral or enumeration type. const RecordType *RecordTy = T->getAs<RecordType>(); if (!RecordTy || !getLangOpts().CPlusPlus) { - if (NotIntDiag.getDiagID()) - Diag(Loc, NotIntDiag) << T << From->getSourceRange(); + if (!Diagnoser.Suppress) + Diagnoser.diagnoseNotInt(*this, Loc, T) << From->getSourceRange(); return Owned(From); } // We must have a complete class type. - if (RequireCompleteType(Loc, T, IncompleteDiag)) + struct TypeDiagnoserPartialDiag : TypeDiagnoser { + ICEConvertDiagnoser &Diagnoser; + Expr *From; + + TypeDiagnoserPartialDiag(ICEConvertDiagnoser &Diagnoser, Expr *From) + : TypeDiagnoser(Diagnoser.Suppress), Diagnoser(Diagnoser), From(From) {} + + virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) { + Diagnoser.diagnoseIncomplete(S, Loc, T) << From->getSourceRange(); + } + } IncompleteDiagnoser(Diagnoser, From); + + if (RequireCompleteType(Loc, T, IncompleteDiagnoser)) return Owned(From); // Look for a conversion to an integral or enumeration type. @@ -5092,7 +5112,7 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, switch (ViableConversions.size()) { case 0: - if (ExplicitConversions.size() == 1 && ExplicitConvDiag.getDiagID()) { + if (ExplicitConversions.size() == 1 && !Diagnoser.Suppress) { DeclAccessPair Found = ExplicitConversions[0]; CXXConversionDecl *Conversion = cast<CXXConversionDecl>(Found->getUnderlyingDecl()); @@ -5104,14 +5124,12 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, std::string TypeStr; ConvTy.getAsStringInternal(TypeStr, getPrintingPolicy()); - Diag(Loc, ExplicitConvDiag) - << T << ConvTy + Diagnoser.diagnoseExplicitConv(*this, Loc, T, ConvTy) << FixItHint::CreateInsertion(From->getLocStart(), "static_cast<" + TypeStr + ">(") << FixItHint::CreateInsertion(PP.getLocForEndOfToken(From->getLocEnd()), ")"); - Diag(Conversion->getLocation(), ExplicitConvNote) - << ConvTy->isEnumeralType() << ConvTy; + Diagnoser.noteExplicitConv(*this, Conversion, ConvTy); // If we aren't in a SFINAE context, build a call to the // explicit conversion function. @@ -5142,12 +5160,12 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, = cast<CXXConversionDecl>(Found->getUnderlyingDecl()); QualType ConvTy = Conversion->getConversionType().getNonReferenceType(); - if (ConvDiag.getDiagID()) { + if (!Diagnoser.SuppressConversion) { if (isSFINAEContext()) return ExprError(); - Diag(Loc, ConvDiag) - << T << ConvTy->isEnumeralType() << ConvTy << From->getSourceRange(); + Diagnoser.diagnoseConversion(*this, Loc, T, ConvTy) + << From->getSourceRange(); } ExprResult Result = BuildCXXMemberCallExpr(From, Found, Conversion, @@ -5163,24 +5181,24 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, } default: - if (!AmbigDiag.getDiagID()) - return Owned(From); + if (Diagnoser.Suppress) + return ExprError(); - Diag(Loc, AmbigDiag) - << T << From->getSourceRange(); + Diagnoser.diagnoseAmbiguous(*this, Loc, T) << From->getSourceRange(); for (unsigned I = 0, N = ViableConversions.size(); I != N; ++I) { CXXConversionDecl *Conv = cast<CXXConversionDecl>(ViableConversions[I]->getUnderlyingDecl()); QualType ConvTy = Conv->getConversionType().getNonReferenceType(); - Diag(Conv->getLocation(), AmbigNote) - << ConvTy->isEnumeralType() << ConvTy; + Diagnoser.noteAmbiguous(*this, Conv, ConvTy); } return Owned(From); } if (!isIntegralOrEnumerationType(From->getType(), AllowScopedEnumerations) && - NotIntDiag.getDiagID()) - Diag(Loc, NotIntDiag) << From->getType() << From->getSourceRange(); + !Diagnoser.Suppress) { + Diagnoser.diagnoseNotInt(*this, Loc, From->getType()) + << From->getSourceRange(); + } return DefaultLvalueConversion(From); } @@ -5190,7 +5208,7 @@ Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, Expr *From, /// @p SuppressUserConversions, then don't allow user-defined /// conversions via constructors or conversion operators. /// -/// \para PartialOverloading true if we are performing "partial" overloading +/// \param PartialOverloading true if we are performing "partial" overloading /// based on an incomplete set of function arguments. This feature is used by /// code completion. void @@ -5906,7 +5924,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, // empty. if (const RecordType *T1Rec = T1->getAs<RecordType>()) { // Complete the type if it can be completed. Otherwise, we're done. - if (RequireCompleteType(OpLoc, T1, PDiag())) + if (RequireCompleteType(OpLoc, T1, 0)) return; LookupResult Operators(*this, OpName, OpLoc, LookupOrdinaryName); @@ -6098,40 +6116,49 @@ BuiltinCandidateTypeSet::AddPointerWithMoreQualifiedTypeVariants(QualType Ty, const PointerType *PointerTy = Ty->getAs<PointerType>(); bool buildObjCPtr = false; if (!PointerTy) { - if (const ObjCObjectPointerType *PTy = Ty->getAs<ObjCObjectPointerType>()) { - PointeeTy = PTy->getPointeeType(); - buildObjCPtr = true; - } - else - llvm_unreachable("type was not a pointer type!"); - } - else + const ObjCObjectPointerType *PTy = Ty->castAs<ObjCObjectPointerType>(); + PointeeTy = PTy->getPointeeType(); + buildObjCPtr = true; + } else { PointeeTy = PointerTy->getPointeeType(); - + } + // Don't add qualified variants of arrays. For one, they're not allowed // (the qualifier would sink to the element type), and for another, the // only overload situation where it matters is subscript or pointer +- int, // and those shouldn't have qualifier variants anyway. if (PointeeTy->isArrayType()) return true; + unsigned BaseCVR = PointeeTy.getCVRQualifiers(); - if (const ConstantArrayType *Array =Context.getAsConstantArrayType(PointeeTy)) - BaseCVR = Array->getElementType().getCVRQualifiers(); bool hasVolatile = VisibleQuals.hasVolatile(); bool hasRestrict = VisibleQuals.hasRestrict(); // Iterate through all strict supersets of BaseCVR. for (unsigned CVR = BaseCVR+1; CVR <= Qualifiers::CVRMask; ++CVR) { if ((CVR | BaseCVR) != CVR) continue; - // Skip over Volatile/Restrict if no Volatile/Restrict found anywhere - // in the types. + // Skip over volatile if no volatile found anywhere in the types. if ((CVR & Qualifiers::Volatile) && !hasVolatile) continue; - if ((CVR & Qualifiers::Restrict) && !hasRestrict) continue; + + // Skip over restrict if no restrict found anywhere in the types, or if + // the type cannot be restrict-qualified. + if ((CVR & Qualifiers::Restrict) && + (!hasRestrict || + (!(PointeeTy->isAnyPointerType() || PointeeTy->isReferenceType())))) + continue; + + // Build qualified pointee type. QualType QPointeeTy = Context.getCVRQualifiedType(PointeeTy, CVR); + + // Build qualified pointer type. + QualType QPointerTy; if (!buildObjCPtr) - PointerTypes.insert(Context.getPointerType(QPointeeTy)); + QPointerTy = Context.getPointerType(QPointeeTy); else - PointerTypes.insert(Context.getObjCObjectPointerType(QPointeeTy)); + QPointerTy = Context.getObjCObjectPointerType(QPointeeTy); + + // Insert qualified pointer type. + PointerTypes.insert(QPointerTy); } return true; @@ -6328,6 +6355,8 @@ static Qualifiers CollectVRQualifiers(ASTContext &Context, Expr* ArgExpr) { // as see them. bool done = false; while (!done) { + if (CanTy.isRestrictQualified()) + VRQuals.addRestrict(); if (const PointerType *ResTypePtr = CanTy->getAs<PointerType>()) CanTy = ResTypePtr->getPointeeType(); else if (const MemberPointerType *ResTypeMPtr = @@ -6337,8 +6366,6 @@ static Qualifiers CollectVRQualifiers(ASTContext &Context, Expr* ArgExpr) { done = true; if (CanTy.isVolatileQualified()) VRQuals.addVolatile(); - if (CanTy.isRestrictQualified()) - VRQuals.addRestrict(); if (VRQuals.hasRestrict() && VRQuals.hasVolatile()) return VRQuals; } @@ -6368,12 +6395,12 @@ class BuiltinOperatorOverloadBuilder { // The "promoted arithmetic types" are the arithmetic // types are that preserved by promotion (C++ [over.built]p2). static const unsigned FirstIntegralType = 3; - static const unsigned LastIntegralType = 18; + static const unsigned LastIntegralType = 20; static const unsigned FirstPromotedIntegralType = 3, - LastPromotedIntegralType = 9; + LastPromotedIntegralType = 11; static const unsigned FirstPromotedArithmeticType = 0, - LastPromotedArithmeticType = 9; - static const unsigned NumArithmeticTypes = 18; + LastPromotedArithmeticType = 11; + static const unsigned NumArithmeticTypes = 20; /// \brief Get the canonical type for a given arithmetic type index. CanQualType getArithmeticType(unsigned index) { @@ -6389,9 +6416,11 @@ class BuiltinOperatorOverloadBuilder { &ASTContext::IntTy, &ASTContext::LongTy, &ASTContext::LongLongTy, + &ASTContext::Int128Ty, &ASTContext::UnsignedIntTy, &ASTContext::UnsignedLongTy, &ASTContext::UnsignedLongLongTy, + &ASTContext::UnsignedInt128Ty, // End of promoted types. &ASTContext::BoolTy, @@ -6404,7 +6433,7 @@ class BuiltinOperatorOverloadBuilder { &ASTContext::UnsignedCharTy, &ASTContext::UnsignedShortTy, // End of integral types. - // FIXME: What about complex? + // FIXME: What about complex? What about half? }; return S.Context.*ArithmeticTypes[index]; } @@ -6423,20 +6452,24 @@ class BuiltinOperatorOverloadBuilder { // *except* when dealing with signed types of higher rank. // (we could precompute SLL x UI for all known platforms, but it's // better not to make any assumptions). + // We assume that int128 has a higher rank than long long on all platforms. enum PromotedType { - Flt, Dbl, LDbl, SI, SL, SLL, UI, UL, ULL, Dep=-1 + Dep=-1, + Flt, Dbl, LDbl, SI, SL, SLL, S128, UI, UL, ULL, U128 }; - static PromotedType ConversionsTable[LastPromotedArithmeticType] + static const PromotedType ConversionsTable[LastPromotedArithmeticType] [LastPromotedArithmeticType] = { - /* Flt*/ { Flt, Dbl, LDbl, Flt, Flt, Flt, Flt, Flt, Flt }, - /* Dbl*/ { Dbl, Dbl, LDbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl }, - /*LDbl*/ { LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl }, - /* SI*/ { Flt, Dbl, LDbl, SI, SL, SLL, UI, UL, ULL }, - /* SL*/ { Flt, Dbl, LDbl, SL, SL, SLL, Dep, UL, ULL }, - /* SLL*/ { Flt, Dbl, LDbl, SLL, SLL, SLL, Dep, Dep, ULL }, - /* UI*/ { Flt, Dbl, LDbl, UI, Dep, Dep, UI, UL, ULL }, - /* UL*/ { Flt, Dbl, LDbl, UL, UL, Dep, UL, UL, ULL }, - /* ULL*/ { Flt, Dbl, LDbl, ULL, ULL, ULL, ULL, ULL, ULL }, +/* Flt*/ { Flt, Dbl, LDbl, Flt, Flt, Flt, Flt, Flt, Flt, Flt, Flt }, +/* Dbl*/ { Dbl, Dbl, LDbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl, Dbl }, +/*LDbl*/ { LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl, LDbl }, +/* SI*/ { Flt, Dbl, LDbl, SI, SL, SLL, S128, UI, UL, ULL, U128 }, +/* SL*/ { Flt, Dbl, LDbl, SL, SL, SLL, S128, Dep, UL, ULL, U128 }, +/* SLL*/ { Flt, Dbl, LDbl, SLL, SLL, SLL, S128, Dep, Dep, ULL, U128 }, +/*S128*/ { Flt, Dbl, LDbl, S128, S128, S128, S128, S128, S128, S128, U128 }, +/* UI*/ { Flt, Dbl, LDbl, UI, Dep, Dep, S128, UI, UL, ULL, U128 }, +/* UL*/ { Flt, Dbl, LDbl, UL, UL, Dep, S128, UL, UL, ULL, U128 }, +/* ULL*/ { Flt, Dbl, LDbl, ULL, ULL, ULL, S128, ULL, ULL, ULL, U128 }, +/*U128*/ { Flt, Dbl, LDbl, U128, U128, U128, U128, U128, U128, U128, U128 }, }; assert(L < LastPromotedArithmeticType); @@ -6466,7 +6499,8 @@ class BuiltinOperatorOverloadBuilder { /// \brief Helper method to factor out the common pattern of adding overloads /// for '++' and '--' builtin operators. void addPlusPlusMinusMinusStyleOverloads(QualType CandidateTy, - bool HasVolatile) { + bool HasVolatile, + bool HasRestrict) { QualType ParamTypes[2] = { S.Context.getLValueReferenceType(CandidateTy), S.Context.IntTy @@ -6489,6 +6523,33 @@ class BuiltinOperatorOverloadBuilder { else S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); } + + // Add restrict version only if there are conversions to a restrict type + // and our candidate type is a non-restrict-qualified pointer. + if (HasRestrict && CandidateTy->isAnyPointerType() && + !CandidateTy.isRestrictQualified()) { + ParamTypes[0] + = S.Context.getLValueReferenceType( + S.Context.getCVRQualifiedType(CandidateTy, Qualifiers::Restrict)); + if (NumArgs == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + else + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + + if (HasVolatile) { + ParamTypes[0] + = S.Context.getLValueReferenceType( + S.Context.getCVRQualifiedType(CandidateTy, + (Qualifiers::Volatile | + Qualifiers::Restrict))); + if (NumArgs == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, + CandidateSet); + else + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + } + } + } public: @@ -6508,13 +6569,13 @@ public: assert(getArithmeticType(FirstPromotedIntegralType) == S.Context.IntTy && "Invalid first promoted integral type"); assert(getArithmeticType(LastPromotedIntegralType - 1) - == S.Context.UnsignedLongLongTy && + == S.Context.UnsignedInt128Ty && "Invalid last promoted integral type"); assert(getArithmeticType(FirstPromotedArithmeticType) == S.Context.FloatTy && "Invalid first promoted arithmetic type"); assert(getArithmeticType(LastPromotedArithmeticType - 1) - == S.Context.UnsignedLongLongTy && + == S.Context.UnsignedInt128Ty && "Invalid last promoted arithmetic type"); } @@ -6543,7 +6604,8 @@ public: Arith < NumArithmeticTypes; ++Arith) { addPlusPlusMinusMinusStyleOverloads( getArithmeticType(Arith), - VisibleTypeConversionsQuals.hasVolatile()); + VisibleTypeConversionsQuals.hasVolatile(), + VisibleTypeConversionsQuals.hasRestrict()); } } @@ -6567,8 +6629,10 @@ public: continue; addPlusPlusMinusMinusStyleOverloads(*Ptr, - (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() && - VisibleTypeConversionsQuals.hasVolatile())); + (!(*Ptr).isVolatileQualified() && + VisibleTypeConversionsQuals.hasVolatile()), + (!(*Ptr).isRestrictQualified() && + VisibleTypeConversionsQuals.hasRestrict())); } } @@ -7026,14 +7090,36 @@ public: S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, /*IsAssigmentOperator=*/ isEqualOp); - if (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() && - VisibleTypeConversionsQuals.hasVolatile()) { + bool NeedVolatile = !(*Ptr).isVolatileQualified() && + VisibleTypeConversionsQuals.hasVolatile(); + if (NeedVolatile) { // volatile version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr)); S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); } + + if (!(*Ptr).isRestrictQualified() && + VisibleTypeConversionsQuals.hasRestrict()) { + // restrict version + ParamTypes[0] + = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr)); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + /*IsAssigmentOperator=*/isEqualOp); + + if (NeedVolatile) { + // volatile restrict version + ParamTypes[0] + = S.Context.getLValueReferenceType( + S.Context.getCVRQualifiedType(*Ptr, + (Qualifiers::Volatile | + Qualifiers::Restrict))); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, + CandidateSet, + /*IsAssigmentOperator=*/isEqualOp); + } + } } if (isEqualOp) { @@ -7054,14 +7140,36 @@ public: S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, /*IsAssigmentOperator=*/true); - if (!S.Context.getCanonicalType(*Ptr).isVolatileQualified() && - VisibleTypeConversionsQuals.hasVolatile()) { + bool NeedVolatile = !(*Ptr).isVolatileQualified() && + VisibleTypeConversionsQuals.hasVolatile(); + if (NeedVolatile) { // volatile version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr)); S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, /*IsAssigmentOperator=*/true); } + + if (!(*Ptr).isRestrictQualified() && + VisibleTypeConversionsQuals.hasRestrict()) { + // restrict version + ParamTypes[0] + = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr)); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, + CandidateSet, /*IsAssigmentOperator=*/true); + + if (NeedVolatile) { + // volatile restrict version + ParamTypes[0] + = S.Context.getLValueReferenceType( + S.Context.getCVRQualifiedType(*Ptr, + (Qualifiers::Volatile | + Qualifiers::Restrict))); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, + CandidateSet, /*IsAssigmentOperator=*/true); + + } + } } } } @@ -7705,13 +7813,11 @@ isBetterOverloadCandidate(Sema &S, /// \brief Computes the best viable function (C++ 13.3.3) /// within an overload candidate set. /// -/// \param CandidateSet the set of candidate functions. -/// -/// \param Loc the location of the function name (or operator symbol) for +/// \param Loc The location of the function name (or operator symbol) for /// which overload resolution occurs. /// -/// \param Best f overload resolution was successful or found a deleted -/// function, Best points to the candidate function found. +/// \param Best If overload resolution was successful or found a deleted +/// function, \p Best points to the candidate function found. /// /// \returns The result of overload resolution. OverloadingResult @@ -8035,12 +8141,22 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { FromIface->isSuperClassOf(ToIface)) BaseToDerivedConversion = 2; } else if (const ReferenceType *ToRefTy = ToTy->getAs<ReferenceType>()) { - if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy) && - !FromTy->isIncompleteType() && - !ToRefTy->getPointeeType()->isIncompleteType() && - S.IsDerivedFrom(ToRefTy->getPointeeType(), FromTy)) - BaseToDerivedConversion = 3; + if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy) && + !FromTy->isIncompleteType() && + !ToRefTy->getPointeeType()->isIncompleteType() && + S.IsDerivedFrom(ToRefTy->getPointeeType(), FromTy)) { + BaseToDerivedConversion = 3; + } else if (ToTy->isLValueReferenceType() && !FromExpr->isLValue() && + ToTy.getNonReferenceType().getCanonicalType() == + FromTy.getNonReferenceType().getCanonicalType()) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_lvalue) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << (unsigned) isObjectArgument << I + 1; + MaybeEmitInheritedConstructorNote(S, Fn); + return; } + } if (BaseToDerivedConversion) { S.Diag(Fn->getLocation(), @@ -8127,9 +8243,14 @@ void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand, std::string Description; OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, Description); - S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity) - << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode - << modeCount << NumFormalArgs; + if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName()) + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one) + << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode + << Fn->getParamDecl(0) << NumFormalArgs; + else + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity) + << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode + << modeCount << NumFormalArgs; MaybeEmitInheritedConstructorNote(S, Fn); } @@ -8232,14 +8353,39 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, return; case Sema::TDK_SubstitutionFailure: { - std::string ArgString; - if (TemplateArgumentList *Args - = Cand->DeductionFailure.getTemplateArgumentList()) - ArgString = S.getTemplateArgumentBindingsText( - Fn->getDescribedFunctionTemplate()->getTemplateParameters(), - *Args); + // Format the template argument list into the argument string. + llvm::SmallString<128> TemplateArgString; + if (TemplateArgumentList *Args = + Cand->DeductionFailure.getTemplateArgumentList()) { + TemplateArgString = " "; + TemplateArgString += S.getTemplateArgumentBindingsText( + Fn->getDescribedFunctionTemplate()->getTemplateParameters(), *Args); + } + + // If this candidate was disabled by enable_if, say so. + PartialDiagnosticAt *PDiag = Cand->DeductionFailure.getSFINAEDiagnostic(); + if (PDiag && PDiag->second.getDiagID() == + diag::err_typename_nested_not_found_enable_if) { + // FIXME: Use the source range of the condition, and the fully-qualified + // name of the enable_if template. These are both present in PDiag. + S.Diag(PDiag->first, diag::note_ovl_candidate_disabled_by_enable_if) + << "'enable_if'" << TemplateArgString; + return; + } + + // Format the SFINAE diagnostic into the argument string. + // FIXME: Add a general mechanism to include a PartialDiagnostic *'s + // formatted message in another diagnostic. + llvm::SmallString<128> SFINAEArgString; + SourceRange R; + if (PDiag) { + SFINAEArgString = ": "; + R = SourceRange(PDiag->first, PDiag->first); + PDiag->second.EmitToString(S.getDiagnostics(), SFINAEArgString); + } + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_substitution_failure) - << ArgString; + << TemplateArgString << SFINAEArgString << R; MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -9190,7 +9336,7 @@ bool Sema::ResolveAndFixSingleFunctionTemplateSpecialization( return true; } - // Fix the expresion to refer to 'fn'. + // Fix the expression to refer to 'fn'. SingleFunctionExpression = Owned(FixOverloadedFunctionReference(SrcExpr.take(), found, fn)); @@ -9692,14 +9838,14 @@ static bool IsOverloaded(const UnresolvedSetImpl &Functions) { /// \param OpcIn The UnaryOperator::Opcode that describes this /// operator. /// -/// \param Functions The set of non-member functions that will be +/// \param Fns The set of non-member functions that will be /// considered by overload resolution. The caller needs to build this /// set based on the context using, e.g., /// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This /// set should not contain any member functions; those will be added /// by CreateOverloadedUnaryOp(). /// -/// \param input The input argument. +/// \param Input The input argument. ExprResult Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, const UnresolvedSetImpl &Fns, @@ -9892,7 +10038,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, /// \param OpcIn The BinaryOperator::Opcode that describes this /// operator. /// -/// \param Functions The set of non-member functions that will be +/// \param Fns The set of non-member functions that will be /// considered by overload resolution. The caller needs to build this /// set based on the context using, e.g., /// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This @@ -10559,7 +10705,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs); - if (CheckFunctionCall(Method, TheCall)) + if (CheckFunctionCall(Method, TheCall, Proto)) return ExprError(); if ((isa<CXXConstructorDecl>(CurContext) || @@ -10610,8 +10756,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Call); if (RequireCompleteType(LParenLoc, Object.get()->getType(), - PDiag(diag::err_incomplete_object_call) - << Object.get()->getSourceRange())) + diag::err_incomplete_object_call, Object.get())) return true; LookupResult R(*this, OpName, LParenLoc, LookupOrdinaryName); @@ -10857,7 +11002,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, // If this is a variadic call, handle args passed through "...". if (Proto->isVariadic()) { // Promote the arguments (C99 6.5.2.2p7). - for (unsigned i = NumArgsInProto; i != NumArgs; i++) { + for (unsigned i = NumArgsInProto; i < NumArgs; i++) { ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, 0); IsError |= Arg.isInvalid(); TheCall->setArg(i + 1, Arg.take()); @@ -10868,7 +11013,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, DiagnoseSentinelCalls(Method, LParenLoc, Args, NumArgs); - if (CheckFunctionCall(Method, TheCall)) + if (CheckFunctionCall(Method, TheCall, Proto)) return true; return MaybeBindToTemporary(TheCall); @@ -10899,8 +11044,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { const RecordType *BaseRecord = Base->getType()->getAs<RecordType>(); if (RequireCompleteType(Loc, Base->getType(), - PDiag(diag::err_typecheck_incomplete_tag) - << Base->getSourceRange())) + diag::err_typecheck_incomplete_tag, Base)) return ExprError(); LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName); @@ -11049,7 +11193,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, if (CheckCallReturnType(FD->getResultType(), UDSuffixLoc, UDL, FD)) return ExprError(); - if (CheckFunctionCall(FD, UDL)) + if (CheckFunctionCall(FD, UDL, NULL)) return ExprError(); return MaybeBindToTemporary(UDL); diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index 0e6632964a95..722ac19be533 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/Initialization.h" #include "clang/AST/ExprObjC.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace sema; @@ -232,7 +233,7 @@ namespace { Expr *op); bool tryBuildGetOfReference(Expr *op, ExprResult &result); - bool findSetter(); + bool findSetter(bool warn=true); bool findGetter(); Expr *rebuildAndCaptureObject(Expr *syntacticBase); @@ -505,7 +506,7 @@ bool ObjCPropertyOpBuilder::findGetter() { /// reference. /// /// \return true if a setter was found, in which case Setter -bool ObjCPropertyOpBuilder::findSetter() { +bool ObjCPropertyOpBuilder::findSetter(bool warn) { // For implicit properties, just trust the lookup we already did. if (RefExpr->isImplicitProperty()) { if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) { @@ -531,6 +532,23 @@ bool ObjCPropertyOpBuilder::findSetter() { // Do a normal method lookup first. if (ObjCMethodDecl *setter = LookupMethodInReceiverType(S, SetterSelector, RefExpr)) { + if (setter->isSynthesized() && warn) + if (const ObjCInterfaceDecl *IFace = + dyn_cast<ObjCInterfaceDecl>(setter->getDeclContext())) { + const StringRef thisPropertyName(prop->getName()); + char front = thisPropertyName.front(); + front = islower(front) ? toupper(front) : tolower(front); + SmallString<100> PropertyName = thisPropertyName; + PropertyName[0] = front; + IdentifierInfo *AltMember = &S.PP.getIdentifierTable().get(PropertyName); + if (ObjCPropertyDecl *prop1 = IFace->FindPropertyDeclaration(AltMember)) + if (prop != prop1 && (prop1->getSetterMethodDecl() == setter)) { + S.Diag(RefExpr->getExprLoc(), diag::error_property_setter_ambiguous_use) + << prop->getName() << prop1->getName() << setter->getSelector(); + S.Diag(prop->getLocation(), diag::note_property_declare); + S.Diag(prop1->getLocation(), diag::note_property_declare); + } + } Setter = setter; return true; } @@ -603,7 +621,7 @@ ExprResult ObjCPropertyOpBuilder::buildGet() { /// value being set as the value of the property operation. ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, bool captureSetValueAsResult) { - bool hasSetter = findSetter(); + bool hasSetter = findSetter(false); assert(hasSetter); (void) hasSetter; if (SyntacticRefExpr) @@ -889,8 +907,7 @@ Sema::ObjCSubscriptKind // We must have a complete class type. if (RequireCompleteType(FromE->getExprLoc(), T, - PDiag(diag::err_objc_index_incomplete_class_type) - << FromE->getSourceRange())) + diag::err_objc_index_incomplete_class_type, FromE)) return OS_Error; // Look for a conversion to an integral, enumeration type, or @@ -938,6 +955,27 @@ Sema::ObjCSubscriptKind return OS_Error; } +/// CheckKeyForObjCARCConversion - This routine suggests bridge casting of CF +/// objects used as dictionary subscript key objects. +static void CheckKeyForObjCARCConversion(Sema &S, QualType ContainerT, + Expr *Key) { + if (ContainerT.isNull()) + return; + // dictionary subscripting. + // - (id)objectForKeyedSubscript:(id)key; + IdentifierInfo *KeyIdents[] = { + &S.Context.Idents.get("objectForKeyedSubscript") + }; + Selector GetterSelector = S.Context.Selectors.getSelector(1, KeyIdents); + ObjCMethodDecl *Getter = S.LookupMethodInObjectType(GetterSelector, ContainerT, + true /*instance*/); + if (!Getter) + return; + QualType T = Getter->param_begin()[0]->getType(); + S.CheckObjCARCConversion(Key->getSourceRange(), + T, Key, Sema::CCK_ImplicitConversion); +} + bool ObjCSubscriptOpBuilder::findAtIndexGetter() { if (AtIndexGetter) return true; @@ -955,8 +993,12 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() { } Sema::ObjCSubscriptKind Res = S.CheckSubscriptingKind(RefExpr->getKeyExpr()); - if (Res == Sema::OS_Error) + if (Res == Sema::OS_Error) { + if (S.getLangOpts().ObjCAutoRefCount) + CheckKeyForObjCARCConversion(S, ResultType, + RefExpr->getKeyExpr()); return false; + } bool arrayRef = (Res == Sema::OS_Array); if (ResultType.isNull()) { @@ -1063,8 +1105,12 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() { Sema::ObjCSubscriptKind Res = S.CheckSubscriptingKind(RefExpr->getKeyExpr()); - if (Res == Sema::OS_Error) + if (Res == Sema::OS_Error) { + if (S.getLangOpts().ObjCAutoRefCount) + CheckKeyForObjCARCConversion(S, ResultType, + RefExpr->getKeyExpr()); return false; + } bool arrayRef = (Res == Sema::OS_Array); if (ResultType.isNull()) { diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 9052278d7337..d22deb2f7f21 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -19,6 +19,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/StmtObjC.h" @@ -28,7 +29,21 @@ #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" using namespace clang; using namespace sema; @@ -150,10 +165,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { if (!E) return; + const Expr *WarnExpr; SourceLocation Loc; SourceRange R1, R2; if (SourceMgr.isInSystemMacro(E->getExprLoc()) || - !E->isUnusedResultAWarning(Loc, R1, R2, Context)) + !E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context)) return; // Okay, we have an unused result. Depending on what the base expression is, @@ -168,7 +184,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { if (DiagnoseUnusedComparison(*this, E)) return; - E = E->IgnoreParenImpCasts(); + E = WarnExpr; if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { if (E->getType()->isVoidType()) return; @@ -226,6 +242,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { } } + if (E->isGLValue() && E->getType().isVolatileQualified()) { + Diag(Loc, diag::warn_unused_volatile) << R1 << R2; + return; + } + DiagRuntimeBehavior(Loc, 0, PDiag(DiagID) << R1 << R2); } @@ -361,12 +382,10 @@ Sema::ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl, } StmtResult Sema::ActOnAttributedStmt(SourceLocation AttrLoc, - const AttrVec &Attrs, + ArrayRef<const Attr*> Attrs, Stmt *SubStmt) { - // Fill in the declaration and return it. Variable length will require to - // change this to AttributedStmt::Create(Context, ....); - // and probably using ArrayRef - AttributedStmt *LS = new (Context) AttributedStmt(AttrLoc, Attrs, SubStmt); + // Fill in the declaration and return it. + AttributedStmt *LS = AttributedStmt::Create(Context, AttrLoc, Attrs, SubStmt); return Owned(LS); } @@ -519,16 +538,56 @@ Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, Expr *Cond, if (!Cond) return StmtError(); + class SwitchConvertDiagnoser : public ICEConvertDiagnoser { + Expr *Cond; + + public: + SwitchConvertDiagnoser(Expr *Cond) + : ICEConvertDiagnoser(false, true), Cond(Cond) { } + + virtual DiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_typecheck_statement_requires_integer) << T; + } + + virtual DiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_switch_incomplete_class_type) + << T << Cond->getSourceRange(); + } + + virtual DiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc, + QualType T, + QualType ConvTy) { + return S.Diag(Loc, diag::err_switch_explicit_conversion) << T << ConvTy; + } + + virtual DiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_switch_conversion) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc, + QualType T) { + return S.Diag(Loc, diag::err_switch_multiple_conversions) << T; + } + + virtual DiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv, + QualType ConvTy) { + return S.Diag(Conv->getLocation(), diag::note_switch_conversion) + << ConvTy->isEnumeralType() << ConvTy; + } + + virtual DiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc, + QualType T, + QualType ConvTy) { + return DiagnosticBuilder::getEmpty(); + } + } SwitchDiagnoser(Cond); + CondResult - = ConvertToIntegralOrEnumerationType(SwitchLoc, Cond, - PDiag(diag::err_typecheck_statement_requires_integer), - PDiag(diag::err_switch_incomplete_class_type) - << Cond->getSourceRange(), - PDiag(diag::err_switch_explicit_conversion), - PDiag(diag::note_switch_conversion), - PDiag(diag::err_switch_multiple_conversions), - PDiag(diag::note_switch_conversion), - PDiag(0), + = ConvertToIntegralOrEnumerationType(SwitchLoc, Cond, SwitchDiagnoser, /*AllowScopedEnumerations*/ true); if (CondResult.isInvalid()) return StmtError(); Cond = CondResult.take(); @@ -609,7 +668,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, = CondExpr->isTypeDependent() || CondExpr->isValueDependent(); unsigned CondWidth = HasDependentValue ? 0 : Context.getIntWidth(CondTypeBeforePromotion); - bool CondIsSigned + bool CondIsSigned = CondTypeBeforePromotion->isSignedIntegerOrEnumerationType(); // Accumulate all of the case values in a vector so that we can sort them @@ -726,8 +785,30 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, if (i != 0 && CaseVals[i].first == CaseVals[i-1].first) { // If we have a duplicate, report it. - Diag(CaseVals[i].second->getLHS()->getLocStart(), - diag::err_duplicate_case) << CaseVals[i].first.toString(10); + // First, determine if either case value has a name + StringRef PrevString, CurrString; + Expr *PrevCase = CaseVals[i-1].second->getLHS()->IgnoreParenCasts(); + Expr *CurrCase = CaseVals[i].second->getLHS()->IgnoreParenCasts(); + if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(PrevCase)) { + PrevString = DeclRef->getDecl()->getName(); + } + if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(CurrCase)) { + CurrString = DeclRef->getDecl()->getName(); + } + llvm::SmallString<16> CaseValStr; + CaseVals[i-1].first.toString(CaseValStr); + + if (PrevString == CurrString) + Diag(CaseVals[i].second->getLHS()->getLocStart(), + diag::err_duplicate_case) << + (PrevString.empty() ? CaseValStr.str() : PrevString); + else + Diag(CaseVals[i].second->getLHS()->getLocStart(), + diag::err_duplicate_case_differing_expr) << + (PrevString.empty() ? CaseValStr.str() : PrevString) << + (CurrString.empty() ? CaseValStr.str() : CurrString) << + CaseValStr; + Diag(CaseVals[i-1].second->getLHS()->getLocStart(), diag::note_duplicate_case_prev); // FIXME: We really want to remove the bogus case stmt from the @@ -904,7 +985,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, << CondTypeBeforePromotion; } - llvm::APSInt Hi = + llvm::APSInt Hi = RI->second->getRHS()->EvaluateKnownConstInt(Context); AdjustAPSInt(Hi, CondWidth, CondIsSigned); while (EI != EIend && EI->first < Hi) @@ -952,12 +1033,12 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, switch (UnhandledNames.size()) { case 0: break; case 1: - Diag(CondExpr->getExprLoc(), TheDefaultStmt + Diag(CondExpr->getExprLoc(), TheDefaultStmt ? diag::warn_def_missing_case1 : diag::warn_missing_case1) << UnhandledNames[0]; break; case 2: - Diag(CondExpr->getExprLoc(), TheDefaultStmt + Diag(CondExpr->getExprLoc(), TheDefaultStmt ? diag::warn_def_missing_case2 : diag::warn_missing_case2) << UnhandledNames[0] << UnhandledNames[1]; break; @@ -990,6 +1071,55 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, return Owned(SS); } +void +Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, + Expr *SrcExpr) { + unsigned DIAG = diag::warn_not_in_enum_assignement; + if (Diags.getDiagnosticLevel(DIAG, SrcExpr->getExprLoc()) + == DiagnosticsEngine::Ignored) + return; + + if (const EnumType *ET = DstType->getAs<EnumType>()) + if (!Context.hasSameType(SrcType, DstType) && + SrcType->isIntegerType()) { + if (!SrcExpr->isTypeDependent() && !SrcExpr->isValueDependent() && + SrcExpr->isIntegerConstantExpr(Context)) { + // Get the bitwidth of the enum value before promotions. + unsigned DstWith = Context.getIntWidth(DstType); + bool DstIsSigned = DstType->isSignedIntegerOrEnumerationType(); + + llvm::APSInt RhsVal = SrcExpr->EvaluateKnownConstInt(Context); + const EnumDecl *ED = ET->getDecl(); + typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64> + EnumValsTy; + EnumValsTy EnumVals; + + // Gather all enum values, set their type and sort them, + // allowing easier comparison with rhs constant. + for (EnumDecl::enumerator_iterator EDI = ED->enumerator_begin(); + EDI != ED->enumerator_end(); ++EDI) { + llvm::APSInt Val = EDI->getInitVal(); + AdjustAPSInt(Val, DstWith, DstIsSigned); + EnumVals.push_back(std::make_pair(Val, *EDI)); + } + if (EnumVals.empty()) + return; + std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals); + EnumValsTy::iterator EIend = + std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals); + + // See which case values aren't in enum. + EnumValsTy::const_iterator EI = EnumVals.begin(); + while (EI != EIend && EI->first < RhsVal) + EI++; + if (EI == EIend || EI->first != RhsVal) { + Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignement) + << DstType; + } + } + } +} + StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond, Decl *CondVar, Stmt *Body) { @@ -1037,6 +1167,215 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body, return Owned(new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen)); } +namespace { + // This visitor will traverse a conditional statement and store all + // the evaluated decls into a vector. Simple is set to true if none + // of the excluded constructs are used. + class DeclExtractor : public EvaluatedExprVisitor<DeclExtractor> { + llvm::SmallPtrSet<VarDecl*, 8> &Decls; + llvm::SmallVector<SourceRange, 10> &Ranges; + bool Simple; +public: + typedef EvaluatedExprVisitor<DeclExtractor> Inherited; + + DeclExtractor(Sema &S, llvm::SmallPtrSet<VarDecl*, 8> &Decls, + llvm::SmallVector<SourceRange, 10> &Ranges) : + Inherited(S.Context), + Decls(Decls), + Ranges(Ranges), + Simple(true) {} + + bool isSimple() { return Simple; } + + // Replaces the method in EvaluatedExprVisitor. + void VisitMemberExpr(MemberExpr* E) { + Simple = false; + } + + // Any Stmt not whitelisted will cause the condition to be marked complex. + void VisitStmt(Stmt *S) { + Simple = false; + } + + void VisitBinaryOperator(BinaryOperator *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); + } + + void VisitCastExpr(CastExpr *E) { + Visit(E->getSubExpr()); + } + + void VisitUnaryOperator(UnaryOperator *E) { + // Skip checking conditionals with derefernces. + if (E->getOpcode() == UO_Deref) + Simple = false; + else + Visit(E->getSubExpr()); + } + + void VisitConditionalOperator(ConditionalOperator *E) { + Visit(E->getCond()); + Visit(E->getTrueExpr()); + Visit(E->getFalseExpr()); + } + + void VisitParenExpr(ParenExpr *E) { + Visit(E->getSubExpr()); + } + + void VisitBinaryConditionalOperator(BinaryConditionalOperator *E) { + Visit(E->getOpaqueValue()->getSourceExpr()); + Visit(E->getFalseExpr()); + } + + void VisitIntegerLiteral(IntegerLiteral *E) { } + void VisitFloatingLiteral(FloatingLiteral *E) { } + void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { } + void VisitCharacterLiteral(CharacterLiteral *E) { } + void VisitGNUNullExpr(GNUNullExpr *E) { } + void VisitImaginaryLiteral(ImaginaryLiteral *E) { } + + void VisitDeclRefExpr(DeclRefExpr *E) { + VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (!VD) return; + + Ranges.push_back(E->getSourceRange()); + + Decls.insert(VD); + } + + }; // end class DeclExtractor + + // DeclMatcher checks to see if the decls are used in a non-evauluated + // context. + class DeclMatcher : public EvaluatedExprVisitor<DeclMatcher> { + llvm::SmallPtrSet<VarDecl*, 8> &Decls; + bool FoundDecl; + +public: + typedef EvaluatedExprVisitor<DeclMatcher> Inherited; + + DeclMatcher(Sema &S, llvm::SmallPtrSet<VarDecl*, 8> &Decls, Stmt *Statement) : + Inherited(S.Context), Decls(Decls), FoundDecl(false) { + if (!Statement) return; + + Visit(Statement); + } + + void VisitReturnStmt(ReturnStmt *S) { + FoundDecl = true; + } + + void VisitBreakStmt(BreakStmt *S) { + FoundDecl = true; + } + + void VisitGotoStmt(GotoStmt *S) { + FoundDecl = true; + } + + void VisitCastExpr(CastExpr *E) { + if (E->getCastKind() == CK_LValueToRValue) + CheckLValueToRValueCast(E->getSubExpr()); + else + Visit(E->getSubExpr()); + } + + void CheckLValueToRValueCast(Expr *E) { + E = E->IgnoreParenImpCasts(); + + if (isa<DeclRefExpr>(E)) { + return; + } + + if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) { + Visit(CO->getCond()); + CheckLValueToRValueCast(CO->getTrueExpr()); + CheckLValueToRValueCast(CO->getFalseExpr()); + return; + } + + if (BinaryConditionalOperator *BCO = + dyn_cast<BinaryConditionalOperator>(E)) { + CheckLValueToRValueCast(BCO->getOpaqueValue()->getSourceExpr()); + CheckLValueToRValueCast(BCO->getFalseExpr()); + return; + } + + Visit(E); + } + + void VisitDeclRefExpr(DeclRefExpr *E) { + if (VarDecl *VD = dyn_cast<VarDecl>(E->getDecl())) + if (Decls.count(VD)) + FoundDecl = true; + } + + bool FoundDeclInUse() { return FoundDecl; } + + }; // end class DeclMatcher + + void CheckForLoopConditionalStatement(Sema &S, Expr *Second, + Expr *Third, Stmt *Body) { + // Condition is empty + if (!Second) return; + + if (S.Diags.getDiagnosticLevel(diag::warn_variables_not_in_loop_body, + Second->getLocStart()) + == DiagnosticsEngine::Ignored) + return; + + PartialDiagnostic PDiag = S.PDiag(diag::warn_variables_not_in_loop_body); + llvm::SmallPtrSet<VarDecl*, 8> Decls; + llvm::SmallVector<SourceRange, 10> Ranges; + DeclExtractor DE(S, Decls, Ranges); + DE.Visit(Second); + + // Don't analyze complex conditionals. + if (!DE.isSimple()) return; + + // No decls found. + if (Decls.size() == 0) return; + + // Don't warn on volatile, static, or global variables. + for (llvm::SmallPtrSet<VarDecl*, 8>::iterator I = Decls.begin(), + E = Decls.end(); + I != E; ++I) + if ((*I)->getType().isVolatileQualified() || + (*I)->hasGlobalStorage()) return; + + if (DeclMatcher(S, Decls, Second).FoundDeclInUse() || + DeclMatcher(S, Decls, Third).FoundDeclInUse() || + DeclMatcher(S, Decls, Body).FoundDeclInUse()) + return; + + // Load decl names into diagnostic. + if (Decls.size() > 4) + PDiag << 0; + else { + PDiag << Decls.size(); + for (llvm::SmallPtrSet<VarDecl*, 8>::iterator I = Decls.begin(), + E = Decls.end(); + I != E; ++I) + PDiag << (*I)->getDeclName(); + } + + // Load SourceRanges into diagnostic if there is room. + // Otherwise, load the SourceRange of the conditional expression. + if (Ranges.size() <= PartialDiagnostic::MaxArguments) + for (llvm::SmallVector<SourceRange, 10>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) + PDiag << *I; + else + PDiag << Second->getSourceRange(); + + S.Diag(Ranges.begin()->getBegin(), PDiag); + } + +} // end namespace + StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Stmt *First, FullExprArg second, Decl *secondVar, @@ -1059,6 +1398,8 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, } } + CheckForLoopConditionalStatement(*this, second.get(), third.get(), Body); + ExprResult SecondResult(second.release()); VarDecl *ConditionVar = 0; if (secondVar) { @@ -1103,8 +1444,9 @@ StmtResult Sema::ActOnForEachLValueExpr(Expr *E) { } ExprResult -Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { - assert(collection); +Sema::CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { + if (!collection) + return ExprError(); // Bail out early if we've got a type-dependent expression. if (collection->isTypeDependent()) return Owned(collection); @@ -1130,12 +1472,12 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { // If we have a forward-declared type, we can't do this check. // Under ARC, it is an error not to have a forward-declared class. - if (iface && + if (iface && RequireCompleteType(forLoc, QualType(objectType, 0), getLangOpts().ObjCAutoRefCount - ? PDiag(diag::err_arc_collection_forward) - << collection->getSourceRange() - : PDiag(0))) { + ? diag::err_arc_collection_forward + : 0, + collection)) { // Otherwise, if we have any useful type information, check that // the type declares the appropriate method. } else if (iface || !objectType->qual_empty()) { @@ -1151,7 +1493,7 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { // If there's an interface, look in both the public and private APIs. if (iface) { method = iface->lookupInstanceMethod(selector); - if (!method) method = LookupPrivateInstanceMethod(selector, iface); + if (!method) method = iface->lookupPrivateMethod(selector); } // Also check protocol qualifiers. @@ -1175,8 +1517,12 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { StmtResult Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, SourceLocation LParenLoc, - Stmt *First, Expr *Second, - SourceLocation RParenLoc, Stmt *Body) { + Stmt *First, Expr *collection, + SourceLocation RParenLoc) { + + ExprResult CollectionExprResult = + CheckObjCForCollectionOperand(ForLoc, collection); + if (First) { QualType FirstType; if (DeclStmt *DS = dyn_cast<DeclStmt>(First)) { @@ -1204,11 +1550,15 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, if (!FirstType->isDependentType() && !FirstType->isObjCObjectPointerType() && !FirstType->isBlockPointerType()) - Diag(ForLoc, diag::err_selector_element_type) - << FirstType << First->getSourceRange(); + return StmtError(Diag(ForLoc, diag::err_selector_element_type) + << FirstType << First->getSourceRange()); } - return Owned(new (Context) ObjCForCollectionStmt(First, Second, Body, + if (CollectionExprResult.isInvalid()) + return StmtError(); + + return Owned(new (Context) ObjCForCollectionStmt(First, + CollectionExprResult.take(), 0, ForLoc, RParenLoc)); } @@ -1252,7 +1602,7 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, // In ARC, infer lifetime. // FIXME: ARC may want to turn this into 'const __unsafe_unretained' if // we're doing the equivalent of fast iteration. - if (SemaRef.getLangOpts().ObjCAutoRefCount && + if (SemaRef.getLangOpts().ObjCAutoRefCount && SemaRef.inferObjCARCLifetime(Decl)) Decl->setInvalidDecl(); @@ -1343,6 +1693,11 @@ static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S, } +static bool ObjCEnumerationCollection(Expr *Collection) { + return !Collection->isTypeDependent() + && Collection->getType()->getAs<ObjCObjectPointerType>() != 0; +} + /// ActOnCXXForRangeStmt - Check and build a C++0x for-range statement. /// /// C++0x [stmt.ranged]: @@ -1368,6 +1723,10 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation LParenLoc, if (!First || !Range) return StmtError(); + if (ObjCEnumerationCollection(Range)) + return ActOnObjCForCollectionStmt(ForLoc, LParenLoc, First, Range, + RParenLoc); + DeclStmt *DS = dyn_cast<DeclStmt>(First); assert(DS && "first part of for range not a decl stmt"); @@ -1442,7 +1801,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, QualType RangeType = Range->getType(); if (RequireCompleteType(RangeLoc, RangeType, - PDiag(diag::err_for_range_incomplete_type))) + diag::err_for_range_incomplete_type)) return StmtError(); // Build auto __begin = begin-expr, __end = end-expr. @@ -1618,6 +1977,17 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, ColonLoc, RParenLoc)); } +/// FinishObjCForCollectionStmt - Attach the body to a objective-C foreach +/// statement. +StmtResult Sema::FinishObjCForCollectionStmt(Stmt *S, Stmt *B) { + if (!S || !B) + return StmtError(); + ObjCForCollectionStmt * ForStmt = cast<ObjCForCollectionStmt>(S); + + ForStmt->setBody(B); + return S; +} + /// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement. /// This is a separate step from ActOnCXXForRangeStmt because analysis of the /// body cannot be performed until after the type of the range variable is @@ -1626,6 +1996,9 @@ StmtResult Sema::FinishCXXForRangeStmt(Stmt *S, Stmt *B) { if (!S || !B) return StmtError(); + if (isa<ObjCForCollectionStmt>(S)) + return FinishObjCForCollectionStmt(S, B); + CXXForRangeStmt *ForStmt = cast<CXXForRangeStmt>(S); ForStmt->setBody(B); @@ -1723,7 +2096,7 @@ const VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, // ... the expression is the name of a non-volatile automatic object // (other than a function or catch-clause parameter)) ... const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens()); - if (!DR) + if (!DR || DR->refersToEnclosingLocal()) return 0; const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) @@ -1776,8 +2149,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, if (AllowNRVO && (NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true))) { ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, - Value->getType(), CK_LValueToRValue, - Value, VK_XValue); + Value->getType(), CK_NoOp, Value, VK_XValue); Expr *InitExpr = &AsRvalue; InitializationKind Kind @@ -1812,8 +2184,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, // Promote "AsRvalue" to the heap, since we now need this // expression node to persist. Value = ImplicitCastExpr::Create(Context, Value->getType(), - CK_LValueToRValue, Value, 0, - VK_XValue); + CK_NoOp, Value, 0, VK_XValue); // Complete type-checking the initialization of the return type // using the constructor we found. @@ -1840,8 +2211,12 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // [expr.prim.lambda]p4 in C++11; block literals follow a superset of those // rules which allows multiple return statements. CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction()); + QualType FnRetType = CurCap->ReturnType; + + // For blocks/lambdas with implicit return types, we check each return + // statement individually, and deduce the common return type when the block + // or lambda is completed. if (CurCap->HasImplicitReturnType) { - QualType ReturnT; if (RetValExp && !isa<InitListExpr>(RetValExp)) { ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp); if (Result.isInvalid()) @@ -1849,10 +2224,10 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { RetValExp = Result.take(); if (!RetValExp->isTypeDependent()) - ReturnT = RetValExp->getType(); + FnRetType = RetValExp->getType(); else - ReturnT = Context.DependentTy; - } else { + FnRetType = CurCap->ReturnType = Context.DependentTy; + } else { if (RetValExp) { // C++11 [expr.lambda.prim]p4 bans inferring the result from an // initializer list, because it is not an expression (even @@ -1861,21 +2236,14 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { << RetValExp->getSourceRange(); } - ReturnT = Context.VoidTy; - } - // We require the return types to strictly match here. - if (!CurCap->ReturnType.isNull() && - !CurCap->ReturnType->isDependentType() && - !ReturnT->isDependentType() && - !Context.hasSameType(ReturnT, CurCap->ReturnType)) { - Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) - << ReturnT << CurCap->ReturnType - << (getCurLambda() != 0); - return StmtError(); + FnRetType = Context.VoidTy; } - CurCap->ReturnType = ReturnT; + + // Although we'll properly infer the type of the block once it's completed, + // make sure we provide a return type now for better error recovery. + if (CurCap->ReturnType.isNull()) + CurCap->ReturnType = FnRetType; } - QualType FnRetType = CurCap->ReturnType; assert(!FnRetType.isNull()); if (BlockScopeInfo *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) { @@ -1943,10 +2311,12 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { ReturnStmt *Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, NRVOCandidate); - // If we need to check for the named return value optimization, save the - // return statement in our scope for later processing. - if (getLangOpts().CPlusPlus && FnRetType->isRecordType() && - !CurContext->isDependentContext()) + // If we need to check for the named return value optimization, + // or if we need to infer the return type, + // save the return statement in our scope for later processing. + if (CurCap->HasImplicitReturnType || + (getLangOpts().CPlusPlus && FnRetType->isRecordType() && + !CurContext->isDependentContext())) FunctionScopes.back()->Returns.push_back(Result); return Owned(Result); @@ -1957,7 +2327,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // Check for unexpanded parameter packs. if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); - + if (isa<CapturingScopeInfo>(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); @@ -1973,7 +2343,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { FnRetType = MD->getResultType(); if (MD->hasRelatedResultType() && MD->getClassInterface()) { // In the implementation of a method with a related return type, the - // type used to type-check the validity of return statements within the + // type used to type-check the validity of return statements within the // method body is a pointer to the type of the class being implemented. RelatedRetType = Context.getObjCInterfaceType(MD->getClassInterface()); RelatedRetType = Context.getObjCObjectPointerType(RelatedRetType); @@ -2064,7 +2434,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // FIXME: The diagnostics here don't really describe what is happening. InitializedEntity Entity = InitializedEntity::InitializeTemporary(RelatedRetType); - + ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(), RetValExp); if (Res.isInvalid()) { @@ -2108,7 +2478,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (getLangOpts().CPlusPlus && FnRetType->isRecordType() && !CurContext->isDependentContext()) FunctionScopes.back()->Returns.push_back(Result); - + return Owned(Result); } @@ -2147,18 +2517,17 @@ static bool CheckAsmLValue(const Expr *E, Sema &S) { /// isOperandMentioned - Return true if the specified operand # is mentioned /// anywhere in the decomposed asm string. -static bool isOperandMentioned(unsigned OpNo, +static bool isOperandMentioned(unsigned OpNo, ArrayRef<AsmStmt::AsmStringPiece> AsmStrPieces) { for (unsigned p = 0, e = AsmStrPieces.size(); p != e; ++p) { const AsmStmt::AsmStringPiece &Piece = AsmStrPieces[p]; if (!Piece.isOperand()) continue; - + // If this is a reference to the input and if the input was the smaller // one, then we have to reject this asm. if (Piece.getOperandNo() == OpNo) return true; } - return false; } @@ -2343,7 +2712,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, // then we can promote the smaller one to a larger input and the asm string // won't notice. bool SmallerValueMentioned = false; - + // If this is a reference to the input and if the input was the smaller // one, then we have to reject this asm. if (isOperandMentioned(InputOpNo, Pieces)) { @@ -2364,7 +2733,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, if (!SmallerValueMentioned && InputDomain != AD_Other && OutputConstraintInfos[TiedTo].allowsRegister()) continue; - + // Either both of the operands were mentioned or the smaller one was // mentioned. One more special case that we'll allow: if the tied input is // integer, unmentioned, and is a constant, then we'll allow truncating it @@ -2379,7 +2748,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, NS->setInputExpr(i, InputExpr); continue; } - + Diag(InputExpr->getLocStart(), diag::err_asm_tying_incompatible_types) << InTy << OutTy << OutputExpr->getSourceRange() @@ -2390,6 +2759,213 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, return Owned(NS); } +// needSpaceAsmToken - This function handles whitespace around asm punctuation. +// Returns true if a space should be emitted. +static inline bool needSpaceAsmToken(Token currTok) { + static Token prevTok; + + // No need for space after prevToken. + switch(prevTok.getKind()) { + default: + break; + case tok::l_square: + case tok::r_square: + case tok::l_brace: + case tok::r_brace: + case tok::colon: + prevTok = currTok; + return false; + } + + // No need for a space before currToken. + switch(currTok.getKind()) { + default: + break; + case tok::l_square: + case tok::r_square: + case tok::l_brace: + case tok::r_brace: + case tok::comma: + case tok::colon: + prevTok = currTok; + return false; + } + prevTok = currTok; + return true; +} + +static void patchMSAsmStrings(Sema &SemaRef, bool &IsSimple, + SourceLocation AsmLoc, + ArrayRef<Token> AsmToks, + ArrayRef<unsigned> LineEnds, + const TargetInfo &TI, + std::vector<std::string> &AsmStrings) { + assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); + + // Assume simple asm stmt until we parse a non-register identifer. + IsSimple = true; + + for (unsigned i = 0, e = LineEnds.size(); i != e; ++i) { + SmallString<512> Asm; + + // Check the operands. + for (unsigned j = (i == 0) ? 0 : LineEnds[i-1], e = LineEnds[i]; j != e; ++j) { + + IdentifierInfo *II; + if (j == 0 || (i > 0 && j == LineEnds[i-1])) { + II = AsmToks[j].getIdentifierInfo(); + Asm = II->getName().str(); + continue; + } + + if (needSpaceAsmToken(AsmToks[j])) + Asm += " "; + + switch (AsmToks[j].getKind()) { + default: + //llvm_unreachable("Unknown token."); + break; + case tok::comma: Asm += ","; break; + case tok::colon: Asm += ":"; break; + case tok::l_square: Asm += "["; break; + case tok::r_square: Asm += "]"; break; + case tok::l_brace: Asm += "{"; break; + case tok::r_brace: Asm += "}"; break; + case tok::numeric_constant: { + SmallString<32> TokenBuf; + TokenBuf.resize(32); + bool StringInvalid = false; + Asm += SemaRef.PP.getSpelling(AsmToks[j], TokenBuf, &StringInvalid); + assert (!StringInvalid && "Expected valid string!"); + break; + } + case tok::identifier: { + II = AsmToks[j].getIdentifierInfo(); + StringRef Name = II->getName(); + + // Valid registers don't need modification. + if (TI.isValidGCCRegisterName(Name)) { + Asm += Name; + break; + } + + // TODO: Lookup the identifier. + IsSimple = false; + } + } // AsmToks[i].getKind() + } + AsmStrings[i] = Asm.c_str(); + } +} + +// Build the unmodified MSAsmString. +static std::string buildMSAsmString(Sema &SemaRef, + ArrayRef<Token> AsmToks, + ArrayRef<unsigned> LineEnds) { + assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); + SmallString<512> Asm; + SmallString<512> TokenBuf; + TokenBuf.resize(512); + unsigned AsmLineNum = 0; + for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) { + const char *ThisTokBuf = &TokenBuf[0]; + bool StringInvalid = false; + unsigned ThisTokLen = + Lexer::getSpelling(AsmToks[i], ThisTokBuf, SemaRef.getSourceManager(), + SemaRef.getLangOpts(), &StringInvalid); + if (i && (!AsmLineNum || i != LineEnds[AsmLineNum-1]) && + needSpaceAsmToken(AsmToks[i])) + Asm += ' '; + Asm += StringRef(ThisTokBuf, ThisTokLen); + if (i + 1 == LineEnds[AsmLineNum] && i + 1 != AsmToks.size()) { + Asm += '\n'; + ++AsmLineNum; + } + } + return Asm.c_str(); +} + +StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc, + ArrayRef<Token> AsmToks, + ArrayRef<unsigned> LineEnds, + SourceLocation EndLoc) { + // MS-style inline assembly is not fully supported, so emit a warning. + Diag(AsmLoc, diag::warn_unsupported_msasm); + SmallVector<StringRef,4> Clobbers; + + // Empty asm statements don't need to instantiate the AsmParser, etc. + if (AsmToks.empty()) { + StringRef AsmString; + MSAsmStmt *NS = + new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true, + /* IsVolatile */ true, AsmToks, LineEnds, + AsmString, Clobbers, EndLoc); + return Owned(NS); + } + + std::string AsmString = buildMSAsmString(*this, AsmToks, LineEnds); + + bool IsSimple; + std::vector<std::string> PatchedAsmStrings; + PatchedAsmStrings.resize(LineEnds.size()); + + // Rewrite operands to appease the AsmParser. + patchMSAsmStrings(*this, IsSimple, AsmLoc, AsmToks, LineEnds, + Context.getTargetInfo(), PatchedAsmStrings); + + // patchMSAsmStrings doesn't correctly patch non-simple asm statements. + if (!IsSimple) { + MSAsmStmt *NS = + new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true, + /* IsVolatile */ true, AsmToks, LineEnds, + AsmString, Clobbers, EndLoc); + return Owned(NS); + } + + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + + // Get the target specific parser. + std::string Error; + const std::string &TT = Context.getTargetInfo().getTriple().getTriple(); + const llvm::Target *TheTarget(llvm::TargetRegistry::lookupTarget(TT, Error)); + + OwningPtr<llvm::MCAsmInfo> MAI(TheTarget->createMCAsmInfo(TT)); + OwningPtr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT)); + OwningPtr<llvm::MCObjectFileInfo> MOFI(new llvm::MCObjectFileInfo()); + OwningPtr<llvm::MCSubtargetInfo> + STI(TheTarget->createMCSubtargetInfo(TT, "", "")); + + for (unsigned i = 0, e = PatchedAsmStrings.size(); i != e; ++i) { + llvm::SourceMgr SrcMgr; + llvm::MCContext Ctx(*MAI, *MRI, MOFI.get(), &SrcMgr); + llvm::MemoryBuffer *Buffer = + llvm::MemoryBuffer::getMemBuffer(PatchedAsmStrings[i], "<inline asm>"); + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); + + OwningPtr<llvm::MCStreamer> Str; + OwningPtr<llvm::MCAsmParser> + Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); + OwningPtr<llvm::MCTargetAsmParser> + TargetParser(TheTarget->createMCAsmParser(*STI, *Parser)); + // Change to the Intel dialect. + Parser->setAssemblerDialect(1); + Parser->setTargetParser(*TargetParser.get()); + + // TODO: Start parsing. + } + + MSAsmStmt *NS = + new (Context) MSAsmStmt(Context, AsmLoc, IsSimple, /* IsVolatile */ true, + AsmToks, LineEnds, AsmString, Clobbers, EndLoc); + + return Owned(NS); +} + StmtResult Sema::ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, Decl *Parm, @@ -2420,15 +2996,13 @@ Sema::ActOnObjCAtTryStmt(SourceLocation AtLoc, Stmt *Try, Finally)); } -StmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc, - Expr *Throw) { +StmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw) { if (Throw) { - Throw = MaybeCreateExprWithCleanups(Throw); ExprResult Result = DefaultLvalueConversion(Throw); if (Result.isInvalid()) return StmtError(); - Throw = Result.take(); + Throw = MaybeCreateExprWithCleanups(Result.take()); QualType ThrowType = Throw->getType(); // Make sure the expression type is an ObjC pointer or "void *". if (!ThrowType->isDependentType() && @@ -2458,7 +3032,6 @@ Sema::ActOnObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw, if (!AtCatchParent) return StmtError(Diag(AtLoc, diag::error_rethrow_used_outside_catch)); } - return BuildObjCAtThrowStmt(AtLoc, Throw); } @@ -2646,17 +3219,17 @@ StmtResult Sema::BuildMSDependentExistsStmt(SourceLocation KeywordLoc, Stmt *Nested) { return new (Context) MSDependentExistsStmt(KeywordLoc, IsIfExists, - QualifierLoc, NameInfo, + QualifierLoc, NameInfo, cast<CompoundStmt>(Nested)); } -StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc, +StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc, bool IsIfExists, - CXXScopeSpec &SS, + CXXScopeSpec &SS, UnqualifiedId &Name, Stmt *Nested) { - return BuildMSDependentExistsStmt(KeywordLoc, IsIfExists, + return BuildMSDependentExistsStmt(KeywordLoc, IsIfExists, SS.getWithLocInContext(Context), GetNameFromUnqualifiedId(Name), Nested); diff --git a/lib/Sema/SemaStmtAttr.cpp b/lib/Sema/SemaStmtAttr.cpp index 21c329758ebc..3c15b7a8afad 100644 --- a/lib/Sema/SemaStmtAttr.cpp +++ b/lib/Sema/SemaStmtAttr.cpp @@ -15,29 +15,55 @@ #include "TargetAttributesSema.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/StringExtras.h" + using namespace clang; using namespace sema; +static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A, + SourceRange Range) { + if (!isa<NullStmt>(St)) { + S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) + << St->getLocStart(); + if (isa<SwitchCase>(St)) { + SourceLocation L = Lexer::getLocForEndOfToken(Range.getEnd(), 0, + S.getSourceManager(), S.getLangOpts()); + S.Diag(L, diag::note_fallthrough_insert_semi_fixit) + << FixItHint::CreateInsertion(L, ";"); + } + return 0; + } + if (S.getCurFunction()->SwitchStack.empty()) { + S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); + return 0; + } + return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context); +} + -static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A) { +static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A, + SourceRange Range) { switch (A.getKind()) { + case AttributeList::AT_FallThrough: + return handleFallThroughAttr(S, St, A, Range); default: // if we're here, then we parsed an attribute, but didn't recognize it as a // statement attribute => it is declaration attribute - S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) << - A.getName()->getName(); + S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) + << A.getName()->getName() << St->getLocStart(); return 0; } } StmtResult Sema::ProcessStmtAttributes(Stmt *S, AttributeList *AttrList, SourceRange Range) { - AttrVec Attrs; + SmallVector<const Attr*, 8> Attrs; for (const AttributeList* l = AttrList; l; l = l->getNext()) { - if (Attr *a = ProcessStmtAttribute(*this, S, *l)) + if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range)) Attrs.push_back(a); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 51ce2a1345a5..c8e45016671d 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -354,12 +354,14 @@ void Sema::LookupTemplateName(LookupResult &Found, return; } - if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope) { - // C++ [basic.lookup.classref]p1: + if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope && + !(getLangOpts().CPlusPlus0x && !Found.empty())) { + // C++03 [basic.lookup.classref]p1: // [...] If the lookup in the class of the object expression finds a // template, the name is also looked up in the context of the entire // postfix-expression and [...] // + // Note: C++11 does not perform this second lookup. LookupResult FoundOuter(*this, Found.getLookupName(), Found.getNameLoc(), LookupOrdinaryName); LookupName(FoundOuter, S); @@ -743,7 +745,7 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, } /// ActOnTemplateTemplateParameter - Called when a C++ template template -/// parameter (e.g. T in template <template <typename> class T> class array) +/// parameter (e.g. T in template <template \<typename> class T> class array) /// has been parsed. S is the current scope. Decl *Sema::ActOnTemplateTemplateParameter(Scope* S, SourceLocation TmpLoc, @@ -865,9 +867,13 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, return true; } - // Find any previous declaration with this name. + // Find any previous declaration with this name. For a friend with no + // scope explicitly specified, we only look for tag declarations (per + // C++11 [basic.lookup.elab]p2). DeclContext *SemanticContext; - LookupResult Previous(*this, Name, NameLoc, LookupOrdinaryName, + LookupResult Previous(*this, Name, NameLoc, + (SS.isEmpty() && TUK == TUK_Friend) + ? LookupTagName : LookupOrdinaryName, ForRedeclaration); if (SS.isNotEmpty() && !SS.isInvalid()) { SemanticContext = computeDeclContext(SS, true); @@ -893,7 +899,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, Invalid = true; } else if (TUK != TUK_Friend && TUK != TUK_Reference) diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc); - + LookupQualifiedName(Previous, SemanticContext); } else { SemanticContext = CurContext; @@ -948,22 +954,32 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // declaration. PrevDecl = PrevClassTemplate = 0; SemanticContext = OutermostContext; - } - } - if (CurContext->isDependentContext()) { - // If this is a dependent context, we don't want to link the friend - // class template to the template in scope, because that would perform - // checking of the template parameter lists that can't be performed - // until the outer context is instantiated. - PrevDecl = PrevClassTemplate = 0; + // Check that the chosen semantic context doesn't already contain a + // declaration of this name as a non-tag type. + LookupResult Previous(*this, Name, NameLoc, LookupOrdinaryName, + ForRedeclaration); + DeclContext *LookupContext = SemanticContext; + while (LookupContext->isTransparentContext()) + LookupContext = LookupContext->getLookupParent(); + LookupQualifiedName(Previous, LookupContext); + + if (Previous.isAmbiguous()) + return true; + + if (Previous.begin() != Previous.end()) + PrevDecl = (*Previous.begin())->getUnderlyingDecl(); + } } } else if (PrevDecl && !isDeclInScope(PrevDecl, SemanticContext, S)) PrevDecl = PrevClassTemplate = 0; if (PrevClassTemplate) { - // Ensure that the template parameter lists are compatible. - if (!TemplateParameterListsAreEqual(TemplateParams, + // Ensure that the template parameter lists are compatible. Skip this check + // for a friend in a dependent context: the template parameter list itself + // could be dependent. + if (!(TUK == TUK_Friend && CurContext->isDependentContext()) && + !TemplateParameterListsAreEqual(TemplateParams, PrevClassTemplate->getTemplateParameters(), /*Complain=*/true, TPL_TemplateMatch)) @@ -1012,8 +1028,10 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // Check the template parameter list of this declaration, possibly // merging in the template parameter list from the previous class - // template declaration. - if (CheckTemplateParameterList(TemplateParams, + // template declaration. Skip this check for a friend in a dependent + // context, because the template parameter list might be dependent. + if (!(TUK == TUK_Friend && CurContext->isDependentContext()) && + CheckTemplateParameterList(TemplateParams, PrevClassTemplate? PrevClassTemplate->getTemplateParameters() : 0, (SS.isSet() && SemanticContext && SemanticContext->isRecord() && @@ -1025,9 +1043,9 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (SS.isSet()) { // If the name of the template was qualified, we must be defining the // template out-of-line. - if (!SS.isInvalid() && !Invalid && !PrevClassTemplate && - !(TUK == TUK_Friend && CurContext->isDependentContext())) { - Diag(NameLoc, diag::err_member_def_does_not_match) + if (!SS.isInvalid() && !Invalid && !PrevClassTemplate) { + Diag(NameLoc, TUK == TUK_Friend ? diag::err_friend_decl_does_not_match + : diag::err_member_def_does_not_match) << Name << SemanticContext << SS.getRange(); Invalid = true; } @@ -1046,8 +1064,10 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. - AddAlignmentAttributesForRecord(NewClass); - AddMsStructLayoutForRecord(NewClass); + if (TUK == TUK_Definition) { + AddAlignmentAttributesForRecord(NewClass); + AddMsStructLayoutForRecord(NewClass); + } ClassTemplateDecl *NewTemplate = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, @@ -1084,6 +1104,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Attr) ProcessDeclAttributeList(S, NewClass, Attr); + AddPushedVisibilityAttribute(NewClass); + if (TUK != TUK_Friend) PushOnScopeChains(NewTemplate, S); else { @@ -1116,6 +1138,11 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, NewTemplate->setInvalidDecl(); NewClass->setInvalidDecl(); } + if (PrevClassTemplate) + mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); + + ActOnDocumentableDecl(NewTemplate); + return NewTemplate; } @@ -1972,6 +1999,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, TemplateArgLists.addOuterTemplateArguments(0, 0); InstantiatingTemplate Inst(*this, TemplateLoc, Template); + if (Inst) + return QualType(); CanonType = SubstType(Pattern->getUnderlyingType(), TemplateArgLists, AliasTemplate->getLocation(), AliasTemplate->getDeclName()); @@ -2420,6 +2449,43 @@ bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, return true; } + case TemplateArgument::Expression: { + // We have a template type parameter but the template argument is an + // expression; see if maybe it is missing the "typename" keyword. + CXXScopeSpec SS; + DeclarationNameInfo NameInfo; + + if (DeclRefExpr *ArgExpr = dyn_cast<DeclRefExpr>(Arg.getAsExpr())) { + SS.Adopt(ArgExpr->getQualifierLoc()); + NameInfo = ArgExpr->getNameInfo(); + } else if (DependentScopeDeclRefExpr *ArgExpr = + dyn_cast<DependentScopeDeclRefExpr>(Arg.getAsExpr())) { + SS.Adopt(ArgExpr->getQualifierLoc()); + NameInfo = ArgExpr->getNameInfo(); + } else if (CXXDependentScopeMemberExpr *ArgExpr = + dyn_cast<CXXDependentScopeMemberExpr>(Arg.getAsExpr())) { + if (ArgExpr->isImplicitAccess()) { + SS.Adopt(ArgExpr->getQualifierLoc()); + NameInfo = ArgExpr->getMemberNameInfo(); + } + } + + if (NameInfo.getName().isIdentifier()) { + LookupResult Result(*this, NameInfo, LookupOrdinaryName); + LookupParsedName(Result, CurScope, &SS); + + if (Result.getAsSingle<TypeDecl>() || + Result.getResultKind() == + LookupResult::NotFoundInCurrentInstantiation) { + // FIXME: Add a FixIt and fix up the template argument for recovery. + SourceLocation Loc = AL.getSourceRange().getBegin(); + Diag(Loc, diag::err_template_arg_must_be_type_suggest); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + } + // fallthrough + } default: { // We have a template type parameter but the template argument // is not a type. @@ -2492,9 +2558,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef, = SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs); Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc, - Template, Converted.data(), - Converted.size(), + Template, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Inst) + return 0; Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext()); ArgType = SemaRef.SubstType(ArgType, AllTemplateArgs, @@ -2541,9 +2608,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef, = SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs); Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc, - Template, Converted.data(), - Converted.size(), + Template, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Inst) + return ExprError(); Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext()); EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); @@ -2590,9 +2658,10 @@ SubstDefaultTemplateArgument(Sema &SemaRef, = SemaRef.getTemplateInstantiationArgs(Template, &TemplateArgs); Sema::InstantiatingTemplate Inst(SemaRef, TemplateLoc, - Template, Converted.data(), - Converted.size(), + Template, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Inst) + return TemplateName(); Sema::ContextRAII SavedContext(SemaRef, Template->getDeclContext()); // Substitute into the nested-name-specifier first, @@ -2724,8 +2793,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, !Template->getDeclContext()->isDependentContext()) { // Do substitution on the type of the non-type template parameter. InstantiatingTemplate Inst(*this, TemplateLoc, Template, - NTTP, Converted.data(), Converted.size(), + NTTP, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Inst) + return true; TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted.data(), Converted.size()); @@ -2856,8 +2927,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, // Set up a template instantiation context. LocalInstantiationScope Scope(*this); InstantiatingTemplate Inst(*this, TemplateLoc, Template, - TempParm, Converted.data(), Converted.size(), + TempParm, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Inst) + return true; TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted.data(), Converted.size()); @@ -3086,9 +3159,11 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // Introduce an instantiation record that describes where we are using // the default template argument. - InstantiatingTemplate Instantiating(*this, RAngleLoc, Template, *Param, - Converted.data(), Converted.size(), + InstantiatingTemplate Instantiating(*this, RAngleLoc, Template, + *Param, Converted, SourceRange(TemplateLoc, RAngleLoc)); + if (Instantiating) + return true; // Check the default template argument. if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, @@ -3574,6 +3649,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, if (ParamType->isPointerType() || ParamType->isNullPtrType()) { switch (isNullPointerValueTemplateArgument(S, Param, ParamType, Arg)) { case NPV_NullPointer: + S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); Converted = TemplateArgument((Decl *)0); return false; @@ -3870,6 +3946,7 @@ static bool CheckTemplateArgumentPointerToMember(Sema &S, case NPV_Error: return true; case NPV_NullPointer: + S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); Converted = TemplateArgument((Decl *)0); return false; case NPV_NotNullPointer: @@ -4066,7 +4143,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, IntegerType = Enum->getDecl()->getIntegerType(); Value = Value.extOrTrunc(Context.getTypeSize(IntegerType)); - Converted = TemplateArgument(Value, Context.getCanonicalType(ParamType)); + Converted = TemplateArgument(Context, Value, + Context.getCanonicalType(ParamType)); return ArgResult; } @@ -4093,8 +4171,20 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Diag(Param->getLocation(), diag::note_template_param_here); return ExprError(); } else if (!Arg->isValueDependent()) { - Arg = VerifyIntegerConstantExpression(Arg, &Value, - PDiag(diag::err_template_arg_not_ice) << ArgType, false).take(); + class TmplArgICEDiagnoser : public VerifyICEDiagnoser { + QualType T; + + public: + TmplArgICEDiagnoser(QualType T) : T(T) { } + + virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, + SourceRange SR) { + S.Diag(Loc, diag::err_template_arg_not_ice) << T << SR; + } + } Diagnoser(ArgType); + + Arg = VerifyIntegerConstantExpression(Arg, &Value, Diagnoser, + false).take(); if (!Arg) return ExprError(); } @@ -4180,7 +4270,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, } } - Converted = TemplateArgument(Value, + Converted = TemplateArgument(Context, Value, ParamType->isEnumeralType() ? Context.getCanonicalType(ParamType) : IntegerType); @@ -4305,6 +4395,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return ExprError(); case NPV_NullPointer: + Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); Converted = TemplateArgument((Decl *)0); return Owned(Arg);; } @@ -4497,13 +4588,13 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, Kind = CharacterLiteral::Ascii; return Owned(new (Context) CharacterLiteral( - Arg.getAsIntegral()->getZExtValue(), + Arg.getAsIntegral().getZExtValue(), Kind, T, Loc)); } if (T->isBooleanType()) return Owned(new (Context) CXXBoolLiteralExpr( - Arg.getAsIntegral()->getBoolValue(), + Arg.getAsIntegral().getBoolValue(), T, Loc)); if (T->isNullPtrType()) @@ -4518,7 +4609,7 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, else BT = T; - Expr *E = IntegerLiteral::Create(Context, *Arg.getAsIntegral(), BT, Loc); + Expr *E = IntegerLiteral::Create(Context, Arg.getAsIntegral(), BT, Loc); if (T->isEnumeralType()) { // FIXME: This is a hack. We need a better way to handle substituted // non-type template parameters. @@ -5041,7 +5132,7 @@ static bool CheckNonTypeClassTemplatePartialSpecializationArgs(Sema &S, /// \param TemplateParams the template parameters of the primary class /// template. /// -/// \param TemplateArg the template arguments of the class template +/// \param TemplateArgs the template arguments of the class template /// partial specialization. /// /// \returns true if there was an error, false otherwise. @@ -5481,7 +5572,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, Decl *Sema::ActOnTemplateDeclarator(Scope *S, MultiTemplateParamsArg TemplateParameterLists, Declarator &D) { - return HandleDeclarator(S, D, move(TemplateParameterLists)); + Decl *NewDecl = HandleDeclarator(S, D, move(TemplateParameterLists)); + ActOnDocumentableDecl(NewDecl); + return NewDecl; } Decl *Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope, @@ -5719,14 +5812,17 @@ Sema::CheckSpecializationInstantiationRedecl(SourceLocation NewLoc, } /// \brief Perform semantic analysis for the given dependent function -/// template specialization. The only possible way to get a dependent -/// function template specialization is with a friend declaration, -/// like so: +/// template specialization. +/// +/// The only possible way to get a dependent function template specialization +/// is with a friend declaration, like so: /// -/// template <class T> void foo(T); -/// template <class T> class A { +/// \code +/// template \<class T> void foo(T); +/// template \<class T> class A { /// friend void foo<>(T); /// }; +/// \endcode /// /// There really isn't any useful analysis we can do here, so we /// just store the information. @@ -6890,6 +6986,42 @@ Sema::ActOnTypenameType(Scope *S, } +/// Determine whether this failed name lookup should be treated as being +/// disabled by a usage of std::enable_if. +static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, + SourceRange &CondRange) { + // We must be looking for a ::type... + if (!II.isStr("type")) + return false; + + // ... within an explicitly-written template specialization... + if (!NNS || !NNS.getNestedNameSpecifier()->getAsType()) + return false; + TypeLoc EnableIfTy = NNS.getTypeLoc(); + TemplateSpecializationTypeLoc *EnableIfTSTLoc = + dyn_cast<TemplateSpecializationTypeLoc>(&EnableIfTy); + if (!EnableIfTSTLoc || EnableIfTSTLoc->getNumArgs() == 0) + return false; + const TemplateSpecializationType *EnableIfTST = + cast<TemplateSpecializationType>(EnableIfTSTLoc->getTypePtr()); + + // ... which names a complete class template declaration... + const TemplateDecl *EnableIfDecl = + EnableIfTST->getTemplateName().getAsTemplateDecl(); + if (!EnableIfDecl || EnableIfTST->isIncompleteType()) + return false; + + // ... called "enable_if". + const IdentifierInfo *EnableIfII = + EnableIfDecl->getDeclName().getAsIdentifierInfo(); + if (!EnableIfII || !EnableIfII->isStr("enable_if")) + return false; + + // Assume the first template argument is the condition. + CondRange = EnableIfTSTLoc->getArgLoc(0).getSourceRange(); + return true; +} + /// \brief Build the type that describes a C++ typename specifier, /// e.g., "typename T::type". QualType @@ -6926,9 +7058,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, unsigned DiagID = 0; Decl *Referenced = 0; switch (Result.getResultKind()) { - case LookupResult::NotFound: + case LookupResult::NotFound: { + // If we're looking up 'type' within a template named 'enable_if', produce + // a more specific diagnostic. + SourceRange CondRange; + if (isEnableIf(QualifierLoc, II, CondRange)) { + Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) + << Ctx << CondRange; + return QualType(); + } + DiagID = diag::err_typename_nested_not_found; break; + } case LookupResult::FoundUnresolvedValue: { // We found a using declaration that is a value. Most likely, the using diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index d68e4642fc43..9500ec3219d4 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -137,8 +137,17 @@ DeduceTemplateArguments(Sema &S, /// of a non-type template parameter, return the declaration of that /// non-type template parameter. static NonTypeTemplateParmDecl *getDeducedParameterFromExpr(Expr *E) { - if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E)) - E = IC->getSubExpr(); + // If we are within an alias template, the expression may have undergone + // any number of parameter substitutions already. + while (1) { + if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E)) + E = IC->getSubExpr(); + else if (SubstNonTypeTemplateParmExpr *Subst = + dyn_cast<SubstNonTypeTemplateParmExpr>(E)) + E = Subst->getReplacement(); + else + break; + } if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) return dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()); @@ -193,7 +202,7 @@ checkDeducedTemplateArguments(ASTContext &Context, if (Y.getKind() == TemplateArgument::Expression || Y.getKind() == TemplateArgument::Declaration || (Y.getKind() == TemplateArgument::Integral && - hasSameExtendedValue(*X.getAsIntegral(), *Y.getAsIntegral()))) + hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()))) return DeducedTemplateArgument(X, X.wasDeducedFromArrayBound() && Y.wasDeducedFromArrayBound()); @@ -293,7 +302,8 @@ DeduceNonTypeTemplateArgument(Sema &S, assert(NTTP->getDepth() == 0 && "Cannot deduce non-type template argument with depth > 0"); - DeducedTemplateArgument NewDeduced(Value, ValueType, DeducedFromArrayBound); + DeducedTemplateArgument NewDeduced(S.Context, Value, ValueType, + DeducedFromArrayBound); DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[NTTP->getIndex()], NewDeduced); @@ -1595,7 +1605,7 @@ DeduceTemplateArguments(Sema &S, case TemplateArgument::Integral: if (Arg.getKind() == TemplateArgument::Integral) { - if (hasSameExtendedValue(*Param.getAsIntegral(), *Arg.getAsIntegral())) + if (hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral())) return Sema::TDK_Success; Info.FirstArg = Param; @@ -1618,7 +1628,7 @@ DeduceTemplateArguments(Sema &S, = getDeducedParameterFromExpr(Param.getAsExpr())) { if (Arg.getKind() == TemplateArgument::Integral) return DeduceNonTypeTemplateArgument(S, NTTP, - *Arg.getAsIntegral(), + Arg.getAsIntegral(), Arg.getIntegralType(), /*ArrayBound=*/false, Info, Deduced); @@ -1867,7 +1877,7 @@ static bool isSameTemplateArg(ASTContext &Context, Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer(); case TemplateArgument::Integral: - return *X.getAsIntegral() == *Y.getAsIntegral(); + return X.getAsIntegral() == Y.getAsIntegral(); case TemplateArgument::Expression: { llvm::FoldingSetNodeID XID, YID; @@ -1898,7 +1908,7 @@ static bool isSameTemplateArg(ASTContext &Context, /// /// \param S The semantic analysis object. /// -/// \param The template argument we are producing template argument +/// \param Arg The template argument we are producing template argument /// location information for. /// /// \param NTTPType For a declaration template argument, the type of @@ -2171,8 +2181,9 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial, TemplateArgs, Info, Deduced)) return Result; + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end()); InstantiatingTemplate Inst(*this, Partial->getLocation(), Partial, - Deduced.data(), Deduced.size(), Info); + DeducedArgs, Info); if (Inst) return TDK_InstantiationDepth; @@ -2198,7 +2209,7 @@ static bool isSimpleTemplateIdType(QualType T) { /// \param FunctionTemplate the function template into which the explicit /// template arguments will be substituted. /// -/// \param ExplicitTemplateArguments the explicitly-specified template +/// \param ExplicitTemplateArgs the explicitly-specified template /// arguments. /// /// \param Deduced the deduced template arguments, which will be populated @@ -2256,8 +2267,9 @@ Sema::SubstituteExplicitTemplateArguments( // Enter a new template instantiation context where we check the // explicitly-specified template arguments against this function template, // and then substitute them into the function parameter types. + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end()); InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(), - FunctionTemplate, Deduced.data(), Deduced.size(), + FunctionTemplate, DeducedArgs, ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution, Info); if (Inst) @@ -2424,6 +2436,16 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, Qualifiers AQuals = A.getQualifiers(); Qualifiers DeducedAQuals = DeducedA.getQualifiers(); + + // Under Objective-C++ ARC, the deduced type may have implicitly been + // given strong lifetime. If so, update the original qualifiers to + // include this strong lifetime. + if (S.getLangOpts().ObjCAutoRefCount && + DeducedAQuals.getObjCLifetime() == Qualifiers::OCL_Strong && + AQuals.getObjCLifetime() == Qualifiers::OCL_None) { + AQuals.setObjCLifetime(Qualifiers::OCL_Strong); + } + if (AQuals == DeducedAQuals) { // Qualifiers match; there's nothing to do. } else if (!DeducedAQuals.compatiblyIncludes(AQuals)) { @@ -2502,8 +2524,9 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, // Enter a new template instantiation context while we instantiate the // actual function declaration. + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end()); InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(), - FunctionTemplate, Deduced.data(), Deduced.size(), + FunctionTemplate, DeducedArgs, ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution, Info); if (Inst) @@ -2825,9 +2848,7 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S, QualType PointeeType = ParamRefType->getPointeeType(); // If the argument has incomplete array type, try to complete it's type. - if (ArgType->isIncompleteArrayType() && - !S.RequireCompleteExprType(Arg, S.PDiag(), - std::make_pair(SourceLocation(), S.PDiag()))) + if (ArgType->isIncompleteArrayType() && !S.RequireCompleteExprType(Arg, 0)) ArgType = Arg->getType(); // [C++0x] If P is an rvalue reference to a cv-unqualified @@ -2966,7 +2987,7 @@ DeduceTemplateArgumentByListElement(Sema &S, /// \param FunctionTemplate the function template for which we are performing /// template argument deduction. /// -/// \param ExplicitTemplateArguments the explicit template arguments provided +/// \param ExplicitTemplateArgs the explicit template arguments provided /// for this call. /// /// \param Args the function call arguments @@ -3226,7 +3247,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, /// \param FunctionTemplate the function template for which we are performing /// template argument deduction. /// -/// \param ExplicitTemplateArguments the explicitly-specified template +/// \param ExplicitTemplateArgs the explicitly-specified template /// arguments. /// /// \param ArgFunctionType the function type that will be used as the @@ -3409,7 +3430,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, /// \param FunctionTemplate the function template for which we are performing /// template argument deduction. /// -/// \param ExplicitTemplateArguments the explicitly-specified template +/// \param ExplicitTemplateArgs the explicitly-specified template /// arguments. /// /// \param Specialization if template argument deduction was successful, @@ -3465,6 +3486,41 @@ namespace { return E; } }; + + /// Determine whether the specified type (which contains an 'auto' type + /// specifier) is dependent. This is not trivial, because the 'auto' specifier + /// itself claims to be type-dependent. + bool isDependentAutoType(QualType Ty) { + while (1) { + QualType Pointee = Ty->getPointeeType(); + if (!Pointee.isNull()) { + Ty = Pointee; + } else if (const MemberPointerType *MPT = Ty->getAs<MemberPointerType>()){ + if (MPT->getClass()->isDependentType()) + return true; + Ty = MPT->getPointeeType(); + } else if (const FunctionProtoType *FPT = Ty->getAs<FunctionProtoType>()){ + for (FunctionProtoType::arg_type_iterator I = FPT->arg_type_begin(), + E = FPT->arg_type_end(); + I != E; ++I) + if ((*I)->isDependentType()) + return true; + Ty = FPT->getResultType(); + } else if (Ty->isDependentSizedArrayType()) { + return true; + } else if (const ArrayType *AT = Ty->getAsArrayTypeUnsafe()) { + Ty = AT->getElementType(); + } else if (Ty->getAs<DependentSizedExtVectorType>()) { + return true; + } else if (const VectorType *VT = Ty->getAs<VectorType>()) { + Ty = VT->getElementType(); + } else { + break; + } + } + assert(Ty->getAs<AutoType>() && "didn't find 'auto' in auto type"); + return false; + } } /// \brief Deduce the type for an auto type-specifier (C++0x [dcl.spec.auto]p6) @@ -3487,7 +3543,7 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, Init = result.take(); } - if (Init->isTypeDependent()) { + if (Init->isTypeDependent() || isDependentAutoType(Type->getType())) { Result = Type; return DAR_Succeeded; } @@ -3518,10 +3574,10 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, TemplateDeductionInfo Info(Context, Loc); - InitListExpr * InitList = dyn_cast<InitListExpr>(Init); + InitListExpr *InitList = dyn_cast<InitListExpr>(Init); if (InitList) { for (unsigned i = 0, e = InitList->getNumInits(); i < e; ++i) { - if (DeduceTemplateArgumentByListElement(*this, &TemplateParams, + if (DeduceTemplateArgumentByListElement(*this, &TemplateParams, TemplArg, InitList->getInit(i), Info, Deduced, TDF)) @@ -3532,7 +3588,7 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, FuncParam, InitType, Init, TDF)) return DAR_Failed; - + if (DeduceTemplateArgumentsByTypeMatch(*this, &TemplateParams, FuncParam, InitType, Info, Deduced, TDF)) return DAR_Failed; @@ -4075,8 +4131,9 @@ Sema::getMoreSpecializedPartialSpecialization( /*PartialOrdering=*/true, /*RefParamComparisons=*/0); if (Better1) { + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end()); InstantiatingTemplate Inst(*this, PS2->getLocation(), PS2, - Deduced.data(), Deduced.size(), Info); + DeducedArgs, Info); Better1 = !::FinishTemplateArgumentDeduction(*this, PS2, PS1->getTemplateArgs(), Deduced, Info); @@ -4091,8 +4148,9 @@ Sema::getMoreSpecializedPartialSpecialization( /*PartialOrdering=*/true, /*RefParamComparisons=*/0); if (Better2) { + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),Deduced.end()); InstantiatingTemplate Inst(*this, PS1->getLocation(), PS1, - Deduced.data(), Deduced.size(), Info); + DeducedArgs, Info); Better2 = !::FinishTemplateArgumentDeduction(*this, PS1, PS2->getTemplateArgs(), Deduced, Info); @@ -4123,9 +4181,17 @@ MarkUsedTemplateParameters(ASTContext &Ctx, if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E)) E = Expansion->getPattern(); - // Skip through any implicit casts we added while type-checking. - while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - E = ICE->getSubExpr(); + // Skip through any implicit casts we added while type-checking, and any + // substitutions performed by template alias expansion. + while (1) { + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) + E = ICE->getSubExpr(); + else if (const SubstNonTypeTemplateParmExpr *Subst = + dyn_cast<SubstNonTypeTemplateParmExpr>(E)) + E = Subst->getReplacement(); + else + break; + } // FIXME: if !OnlyDeduced, we have to walk the whole subexpression to // find other occurrences of template parameters. @@ -4456,13 +4522,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, } } -/// \brief Mark the template parameters can be deduced by the given +/// \brief Mark which template parameters can be deduced from a given /// template argument list. /// /// \param TemplateArgs the template argument list from which template /// parameters will be deduced. /// -/// \param Deduced a bit vector whose elements will be set to \c true +/// \param Used a bit vector whose elements will be set to \c true /// to indicate when the corresponding template parameter will be /// deduced. void diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 128dc2f7ff40..c7cbc41b3035 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -156,11 +156,11 @@ bool Sema::ActiveTemplateInstantiation::isInstantiationRecord() const { case ExceptionSpecInstantiation: case DefaultTemplateArgumentInstantiation: case DefaultFunctionArgumentInstantiation: - return true; - case ExplicitTemplateArgumentSubstitution: case DeducedTemplateArgumentSubstitution: case PriorTemplateArgumentSubstitution: + return true; + case DefaultTemplateArgumentChecking: return false; } @@ -214,12 +214,11 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, } } -Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, - SourceLocation PointOfInstantiation, - TemplateDecl *Template, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - SourceRange InstantiationRange) +Sema::InstantiatingTemplate:: +InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + TemplateDecl *Template, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) @@ -232,35 +231,33 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, = ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation; Inst.PointOfInstantiation = PointOfInstantiation; Inst.Entity = reinterpret_cast<uintptr_t>(Template); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); Inst.InstantiationRange = InstantiationRange; SemaRef.InNonInstantiationSFINAEContext = false; SemaRef.ActiveTemplateInstantiations.push_back(Inst); } } -Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, - SourceLocation PointOfInstantiation, - FunctionTemplateDecl *FunctionTemplate, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - ActiveTemplateInstantiation::InstantiationKind Kind, - sema::TemplateDeductionInfo &DeductionInfo, - SourceRange InstantiationRange) +Sema::InstantiatingTemplate:: +InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + FunctionTemplateDecl *FunctionTemplate, + ArrayRef<TemplateArgument> TemplateArgs, + ActiveTemplateInstantiation::InstantiationKind Kind, + sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) { - Invalid = CheckInstantiationDepth(PointOfInstantiation, - InstantiationRange); + Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange); if (!Invalid) { ActiveTemplateInstantiation Inst; Inst.Kind = Kind; Inst.PointOfInstantiation = PointOfInstantiation; Inst.Entity = reinterpret_cast<uintptr_t>(FunctionTemplate); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); Inst.DeductionInfo = &DeductionInfo; Inst.InstantiationRange = InstantiationRange; SemaRef.InNonInstantiationSFINAEContext = false; @@ -271,54 +268,49 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, } } -Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, - SourceLocation PointOfInstantiation, - ClassTemplatePartialSpecializationDecl *PartialSpec, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - sema::TemplateDeductionInfo &DeductionInfo, - SourceRange InstantiationRange) +Sema::InstantiatingTemplate:: +InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + ClassTemplatePartialSpecializationDecl *PartialSpec, + ArrayRef<TemplateArgument> TemplateArgs, + sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) { - Invalid = false; - - ActiveTemplateInstantiation Inst; - Inst.Kind = ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution; - Inst.PointOfInstantiation = PointOfInstantiation; - Inst.Entity = reinterpret_cast<uintptr_t>(PartialSpec); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; - Inst.DeductionInfo = &DeductionInfo; - Inst.InstantiationRange = InstantiationRange; - SemaRef.InNonInstantiationSFINAEContext = false; - SemaRef.ActiveTemplateInstantiations.push_back(Inst); - - assert(!Inst.isInstantiationRecord()); - ++SemaRef.NonInstantiationEntries; + Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange); + if (!Invalid) { + ActiveTemplateInstantiation Inst; + Inst.Kind = ActiveTemplateInstantiation::DeducedTemplateArgumentSubstitution; + Inst.PointOfInstantiation = PointOfInstantiation; + Inst.Entity = reinterpret_cast<uintptr_t>(PartialSpec); + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); + Inst.DeductionInfo = &DeductionInfo; + Inst.InstantiationRange = InstantiationRange; + SemaRef.InNonInstantiationSFINAEContext = false; + SemaRef.ActiveTemplateInstantiations.push_back(Inst); + } } -Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, - SourceLocation PointOfInstantiation, - ParmVarDecl *Param, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - SourceRange InstantiationRange) +Sema::InstantiatingTemplate:: +InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + ParmVarDecl *Param, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) { Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange); - if (!Invalid) { ActiveTemplateInstantiation Inst; Inst.Kind = ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation; Inst.PointOfInstantiation = PointOfInstantiation; Inst.Entity = reinterpret_cast<uintptr_t>(Param); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); Inst.InstantiationRange = InstantiationRange; SemaRef.InNonInstantiationSFINAEContext = false; SemaRef.ActiveTemplateInstantiations.push_back(Inst); @@ -327,66 +319,57 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, Sema::InstantiatingTemplate:: InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - NamedDecl *Template, - NonTypeTemplateParmDecl *Param, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - SourceRange InstantiationRange) + NamedDecl *Template, NonTypeTemplateParmDecl *Param, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) { - Invalid = false; - - ActiveTemplateInstantiation Inst; - Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution; - Inst.PointOfInstantiation = PointOfInstantiation; - Inst.Template = Template; - Inst.Entity = reinterpret_cast<uintptr_t>(Param); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; - Inst.InstantiationRange = InstantiationRange; - SemaRef.InNonInstantiationSFINAEContext = false; - SemaRef.ActiveTemplateInstantiations.push_back(Inst); - - assert(!Inst.isInstantiationRecord()); - ++SemaRef.NonInstantiationEntries; + Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange); + if (!Invalid) { + ActiveTemplateInstantiation Inst; + Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution; + Inst.PointOfInstantiation = PointOfInstantiation; + Inst.Template = Template; + Inst.Entity = reinterpret_cast<uintptr_t>(Param); + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); + Inst.InstantiationRange = InstantiationRange; + SemaRef.InNonInstantiationSFINAEContext = false; + SemaRef.ActiveTemplateInstantiations.push_back(Inst); + } } Sema::InstantiatingTemplate:: InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - NamedDecl *Template, - TemplateTemplateParmDecl *Param, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - SourceRange InstantiationRange) + NamedDecl *Template, TemplateTemplateParmDecl *Param, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) { - Invalid = false; - ActiveTemplateInstantiation Inst; - Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution; - Inst.PointOfInstantiation = PointOfInstantiation; - Inst.Template = Template; - Inst.Entity = reinterpret_cast<uintptr_t>(Param); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; - Inst.InstantiationRange = InstantiationRange; - SemaRef.InNonInstantiationSFINAEContext = false; - SemaRef.ActiveTemplateInstantiations.push_back(Inst); - - assert(!Inst.isInstantiationRecord()); - ++SemaRef.NonInstantiationEntries; + Invalid = CheckInstantiationDepth(PointOfInstantiation, InstantiationRange); + if (!Invalid) { + ActiveTemplateInstantiation Inst; + Inst.Kind = ActiveTemplateInstantiation::PriorTemplateArgumentSubstitution; + Inst.PointOfInstantiation = PointOfInstantiation; + Inst.Template = Template; + Inst.Entity = reinterpret_cast<uintptr_t>(Param); + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); + Inst.InstantiationRange = InstantiationRange; + SemaRef.InNonInstantiationSFINAEContext = false; + SemaRef.ActiveTemplateInstantiations.push_back(Inst); + } } Sema::InstantiatingTemplate:: InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - TemplateDecl *Template, - NamedDecl *Param, - const TemplateArgument *TemplateArgs, - unsigned NumTemplateArgs, - SourceRange InstantiationRange) + TemplateDecl *Template, NamedDecl *Param, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) : SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext( SemaRef.InNonInstantiationSFINAEContext) @@ -398,8 +381,8 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, Inst.PointOfInstantiation = PointOfInstantiation; Inst.Template = Template; Inst.Entity = reinterpret_cast<uintptr_t>(Param); - Inst.TemplateArgs = TemplateArgs; - Inst.NumTemplateArgs = NumTemplateArgs; + Inst.TemplateArgs = TemplateArgs.data(); + Inst.NumTemplateArgs = TemplateArgs.size(); Inst.InstantiationRange = InstantiationRange; SemaRef.InNonInstantiationSFINAEContext = false; SemaRef.ActiveTemplateInstantiations.push_back(Inst); @@ -638,8 +621,13 @@ llvm::Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { ++Active) { switch(Active->Kind) { - case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation: case ActiveTemplateInstantiation::TemplateInstantiation: + // An instantiation of an alias template may or may not be a SFINAE + // context, depending on what else is on the stack. + if (isa<TypeAliasTemplateDecl>(reinterpret_cast<Decl *>(Active->Entity))) + break; + // Fall through. + case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation: case ActiveTemplateInstantiation::ExceptionSpecInstantiation: // This is a template instantiation, so there is no SFINAE. return llvm::Optional<TemplateDeductionInfo *>(); @@ -850,10 +838,23 @@ namespace { return move(Result); } + ExprResult TransformLambdaExpr(LambdaExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E); + } + + ExprResult TransformLambdaScope(LambdaExpr *E, + CXXMethodDecl *CallOperator) { + CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(), + TSK_ImplicitInstantiation); + return TreeTransform<TemplateInstantiator>:: + TransformLambdaScope(E, CallOperator); + } + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, - const TemplateArgument &arg); + TemplateArgument arg); }; } @@ -1088,7 +1089,11 @@ TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) { unsigned Length = PredefinedExpr::ComputeName(IT, currentDecl).length(); llvm::APInt LengthI(32, Length + 1); - QualType ResTy = getSema().Context.CharTy.withConst(); + QualType ResTy; + if (IT == PredefinedExpr::LFunction) + ResTy = getSema().Context.WCharTy.withConst(); + else + ResTy = getSema().Context.CharTy.withConst(); ResTy = getSema().Context.getConstantArrayType(ResTy, LengthI, ArrayType::Normal, 0); PredefinedExpr *PE = @@ -1138,10 +1143,18 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( NonTypeTemplateParmDecl *parm, SourceLocation loc, - const TemplateArgument &arg) { + TemplateArgument arg) { ExprResult result; QualType type; + // If the argument is a pack expansion, the parameter must actually be a + // parameter pack, and we should substitute the pattern itself, producing + // an expression which contains an unexpanded parameter pack. + if (arg.isPackExpansion()) { + assert(parm->isParameterPack() && "pack expansion for non-pack"); + arg = arg.getPackExpansionPattern(); + } + // The template argument itself might be an expression, in which // case we just return that expression. if (arg.getKind() == TemplateArgument::Expression) { @@ -1378,7 +1391,7 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType( /// substituted. If this type is not dependent, it will be returned /// immediately. /// -/// \param TemplateArgs the template arguments that will be +/// \param Args the template arguments that will be /// substituted for the top-level template parameters within T. /// /// \param Loc the location in the source code where this substitution @@ -1590,6 +1603,9 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, NewParm->setUnparsedDefaultArg(); UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm); } else if (Expr *Arg = OldParm->getDefaultArg()) + // FIXME: if we non-lazily instantiated non-dependent default args for + // non-dependent parameter types we could remove a bunch of duplicate + // conversion warnings for such arguments. NewParm->setUninstantiatedDefaultArg(Arg); NewParm->setHasInheritedDefaultArg(OldParm->hasInheritedDefaultArg()); @@ -1820,8 +1836,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, const MultiLevelTemplateArgumentList &TemplateArgs, TemplateSpecializationKind TSK, bool Complain) { - bool Invalid = false; - CXXRecordDecl *PatternDef = cast_or_null<CXXRecordDecl>(Pattern->getDefinition()); if (DiagnoseUninstantiableTemplate(*this, PointOfInstantiation, Instantiation, @@ -1867,7 +1881,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, // Do substitution on the base class specifiers. if (SubstBaseSpecifiers(Instantiation, Pattern, TemplateArgs)) - Invalid = true; + Instantiation->setInvalidDecl(); TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs); SmallVector<Decl*, 4> Fields; @@ -1893,7 +1907,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, continue; if ((*Member)->isInvalidDecl()) { - Invalid = true; + Instantiation->setInvalidDecl(); continue; } @@ -1917,14 +1931,22 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, MSInfo->setTemplateSpecializationKind(TSK_ImplicitInstantiation); MSInfo->setPointOfInstantiation(PointOfInstantiation); } + } else if (StaticAssertDecl *SA = dyn_cast<StaticAssertDecl>(NewMember)) { + if (SA->isFailed()) { + // A static_assert failed. Bail out; instantiating this + // class is probably not meaningful. + Instantiation->setInvalidDecl(); + break; + } } if (NewMember->isInvalidDecl()) - Invalid = true; + Instantiation->setInvalidDecl(); } else { // FIXME: Eventually, a NULL return will mean that one of the - // instantiations was a semantic disaster, and we'll want to set Invalid = - // true. For now, we expect to skip some members that we can't yet handle. + // instantiations was a semantic disaster, and we'll want to mark the + // declaration invalid. + // For now, we expect to skip some members that we can't yet handle. } } @@ -1934,7 +1956,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, CheckCompletedCXXClass(Instantiation); // Attach any in-class member initializers now the class is complete. - { + if (!FieldsWithMemberInitializers.empty()) { // C++11 [expr.prim.general]p4: // Otherwise, if a member-declarator declares a non-static data member // (9.2) of a class X, the expression this is a prvalue of type "pointer @@ -1955,9 +1977,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Expr *Init = NewInit.take(); assert(Init && "no-argument initializer in class"); assert(!isa<ParenListExpr>(Init) && "call-style init in class"); - ActOnCXXInClassMemberInitializer(NewField, - Init->getSourceRange().getBegin(), - Init); + ActOnCXXInClassMemberInitializer(NewField, Init->getLocStart(), Init); } } } @@ -1976,8 +1996,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Instantiator.disableLateAttributeInstantiation(); LateAttrs.clear(); - if (!FieldsWithMemberInitializers.empty()) - ActOnFinishDelayedMemberInitializers(Instantiation); + ActOnFinishDelayedMemberInitializers(Instantiation); if (TSK == TSK_ImplicitInstantiation) { Instantiation->setLocation(Pattern->getLocation()); @@ -1985,9 +2004,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Instantiation->setRBraceLoc(Pattern->getRBraceLoc()); } - if (Instantiation->isInvalidDecl()) - Invalid = true; - else { + if (!Instantiation->isInvalidDecl()) { + // Perform any dependent diagnostics from the pattern. + PerformDependentDiagnostics(Pattern, TemplateArgs); + // Instantiate any out-of-line class template partial // specializations now. for (TemplateDeclInstantiator::delayed_partial_spec_iterator @@ -1997,7 +2017,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, if (!Instantiator.InstantiateClassTemplatePartialSpecialization( P->first, P->second)) { - Invalid = true; + Instantiation->setInvalidDecl(); break; } } @@ -2006,7 +2026,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, // Exit the scope of this instantiation. SavedContext.pop(); - if (!Invalid) { + if (!Instantiation->isInvalidDecl()) { Consumer.HandleTagDeclDefinition(Instantiation); // Always emit the vtable for an explicit instantiation definition @@ -2015,7 +2035,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, MarkVTableUsed(PointOfInstantiation, Instantiation, true); } - return Invalid; + return Instantiation->isInvalidDecl(); } /// \brief Instantiate the definition of an enum from a given pattern. diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index c7bd99c1ca6a..bdbe71dd8aba 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -79,14 +79,16 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, ExprResult Result = SubstExpr(Aligned->getAlignmentExpr(), TemplateArgs); if (!Result.isInvalid()) - AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs<Expr>()); + AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs<Expr>(), + Aligned->getIsMSDeclSpec()); } else { TypeSourceInfo *Result = SubstType(Aligned->getAlignmentType(), TemplateArgs, Aligned->getLocation(), DeclarationName()); if (Result) - AddAlignedAttr(Aligned->getLocation(), New, Result); + AddAlignedAttr(Aligned->getLocation(), New, Result, + Aligned->getIsMSDeclSpec()); } continue; } @@ -102,7 +104,8 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } else { Attr *NewAttr = sema::instantiateTemplateAttribute(TmplAttr, Context, *this, TemplateArgs); - New->addAttr(NewAttr); + if (NewAttr) + New->addAttr(NewAttr); } } } @@ -338,9 +341,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { // We already have an initializer in the class. } else if (D->getInit()) { if (Var->isStaticDataMember() && !D->isOutOfLine()) - SemaRef.PushExpressionEvaluationContext(Sema::ConstantEvaluated); + SemaRef.PushExpressionEvaluationContext(Sema::ConstantEvaluated, D); else - SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated); + SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated, D); // Instantiate the initializer. ExprResult Init = SemaRef.SubstInitializer(D->getInit(), TemplateArgs, @@ -429,8 +432,8 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { D->getLocation(), D->isMutable(), BitWidth, - D->hasInClassInitializer(), - D->getTypeSpecStartLoc(), + D->getInClassInitStyle(), + D->getInnerLocStart(), D->getAccess(), 0); if (!Field) { @@ -549,12 +552,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { if (InstantiatedAssertExpr.isInvalid()) return 0; - ExprResult Message(D->getMessage()); - D->getMessage(); - return SemaRef.ActOnStaticAssertDeclaration(D->getLocation(), + return SemaRef.BuildStaticAssertDeclaration(D->getLocation(), InstantiatedAssertExpr.get(), - Message.get(), - D->getRParenLoc()); + D->getMessage(), + D->getRParenLoc(), + D->isFailed()); } Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { @@ -933,8 +935,6 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { if (!Instantiated) return 0; - Instantiated->setAccess(D->getAccess()); - // Link the instantiated function template declaration to the function // template from which it was instantiated. FunctionTemplateDecl *InstTemplate @@ -952,8 +952,12 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { InstTemplate->setInstantiatedFromMemberTemplate(D); // Make declarations visible in the appropriate context. - if (!isFriend) + if (!isFriend) { Owner->addDecl(InstTemplate); + } else if (InstTemplate->getDeclContext()->isRecord() && + !D->getPreviousDecl()) { + SemaRef.CheckFriendAccess(InstTemplate); + } return InstTemplate; } @@ -1524,7 +1528,15 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, if (D->isPure()) SemaRef.CheckPureMethod(Method, SourceRange()); - Method->setAccess(D->getAccess()); + // Propagate access. For a non-friend declaration, the access is + // whatever we're propagating from. For a friend, it should be the + // previous declaration we just found. + if (isFriend && Method->getPreviousDecl()) + Method->setAccess(Method->getPreviousDecl()->getAccess()); + else + Method->setAccess(D->getAccess()); + if (FunctionTemplate) + FunctionTemplate->setAccess(Method->getAccess()); SemaRef.CheckOverrideControl(Method); @@ -1534,18 +1546,28 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, if (D->isDeletedAsWritten()) Method->setDeletedAsWritten(); + // If there's a function template, let our caller handle it. if (FunctionTemplate) { - // If there's a function template, let our caller handle it. + // do nothing + + // Don't hide a (potentially) valid declaration with an invalid one. } else if (Method->isInvalidDecl() && !Previous.empty()) { - // Don't hide a (potentially) valid declaration with an invalid one. - } else { - NamedDecl *DeclToAdd = (TemplateParams - ? cast<NamedDecl>(FunctionTemplate) - : Method); - if (isFriend) - Record->makeDeclVisibleInContext(DeclToAdd); - else if (!IsClassScopeSpecialization) - Owner->addDecl(DeclToAdd); + // do nothing + + // Otherwise, check access to friends and make them visible. + } else if (isFriend) { + // We only need to re-check access for methods which we didn't + // manage to match during parsing. + if (!D->getPreviousDecl()) + SemaRef.CheckFriendAccess(Method); + + Record->makeDeclVisibleInContext(Method); + + // Otherwise, add the declaration. We don't need to do this for + // class-scope specializations because we'll have matched them with + // the appropriate template. + } else if (!IsClassScopeSpecialization) { + Owner->addDecl(Method); } if (D->isExplicitlyDefaulted()) { @@ -1949,13 +1971,22 @@ Decl * TemplateDeclInstantiator Decl *TemplateDeclInstantiator::VisitClassScopeFunctionSpecializationDecl( ClassScopeFunctionSpecializationDecl *Decl) { CXXMethodDecl *OldFD = Decl->getSpecialization(); - CXXMethodDecl *NewFD = cast<CXXMethodDecl>(VisitCXXMethodDecl(OldFD, 0, true)); + CXXMethodDecl *NewFD = cast<CXXMethodDecl>(VisitCXXMethodDecl(OldFD, + 0, true)); LookupResult Previous(SemaRef, NewFD->getNameInfo(), Sema::LookupOrdinaryName, Sema::ForRedeclaration); + TemplateArgumentListInfo TemplateArgs; + TemplateArgumentListInfo* TemplateArgsPtr = 0; + if (Decl->hasExplicitTemplateArgs()) { + TemplateArgs = Decl->templateArgs(); + TemplateArgsPtr = &TemplateArgs; + } + SemaRef.LookupQualifiedName(Previous, SemaRef.CurContext); - if (SemaRef.CheckFunctionTemplateSpecialization(NewFD, 0, Previous)) { + if (SemaRef.CheckFunctionTemplateSpecialization(NewFD, TemplateArgsPtr, + Previous)) { NewFD->setInvalidDecl(); return NewFD; } @@ -2165,35 +2196,31 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, TypeLoc NewTL = NewTInfo->getTypeLoc().IgnoreParens(); FunctionProtoTypeLoc *NewProtoLoc = cast<FunctionProtoTypeLoc>(&NewTL); assert(NewProtoLoc && "Missing prototype?"); - unsigned NewIdx = 0, NumNewParams = NewProtoLoc->getNumArgs(); + unsigned NewIdx = 0; for (unsigned OldIdx = 0, NumOldParams = OldProtoLoc->getNumArgs(); OldIdx != NumOldParams; ++OldIdx) { ParmVarDecl *OldParam = OldProtoLoc->getArg(OldIdx); - if (!OldParam->isParameterPack() || - // FIXME: Is this right? OldParam could expand to an empty parameter - // pack and the next parameter could be an unexpanded parameter pack - (NewIdx < NumNewParams && - NewProtoLoc->getArg(NewIdx)->isParameterPack())) { + LocalInstantiationScope *Scope = SemaRef.CurrentInstantiationScope; + + llvm::Optional<unsigned> NumArgumentsInExpansion; + if (OldParam->isParameterPack()) + NumArgumentsInExpansion = + SemaRef.getNumArgumentsInExpansion(OldParam->getType(), + TemplateArgs); + if (!NumArgumentsInExpansion) { // Simple case: normal parameter, or a parameter pack that's // instantiated to a (still-dependent) parameter pack. ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++); Params.push_back(NewParam); - SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, - NewParam); - continue; - } - - // Parameter pack: make the instantiation an argument pack. - SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack( - OldParam); - unsigned NumArgumentsInExpansion - = SemaRef.getNumArgumentsInExpansion(OldParam->getType(), - TemplateArgs); - while (NumArgumentsInExpansion--) { - ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++); - Params.push_back(NewParam); - SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(OldParam, - NewParam); + Scope->InstantiatedLocal(OldParam, NewParam); + } else { + // Parameter pack expansion: make the instantiation an argument pack. + Scope->MakeInstantiatedLocalArgPack(OldParam); + for (unsigned I = 0; I != *NumArgumentsInExpansion; ++I) { + ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++); + Params.push_back(NewParam); + Scope->InstantiatedLocalPackArg(OldParam, NewParam); + } } } } @@ -2237,9 +2264,11 @@ static void addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function, // Expand the parameter pack. Scope.MakeInstantiatedLocalArgPack(PatternParam); - unsigned NumArgumentsInExpansion + llvm::Optional<unsigned> NumArgumentsInExpansion = S.getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); - for (unsigned Arg = 0; Arg < NumArgumentsInExpansion; ++Arg) { + assert(NumArgumentsInExpansion && + "should only be called when all template arguments are known"); + for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) { ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); FunctionParam->setDeclName(PatternParam->getDeclName()); Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam); @@ -2354,9 +2383,10 @@ static void InstantiateExceptionSpec(Sema &SemaRef, FunctionDecl *New, NoexceptExpr = E.take(); if (!NoexceptExpr->isTypeDependent() && !NoexceptExpr->isValueDependent()) - NoexceptExpr = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr, - 0, SemaRef.PDiag(diag::err_noexcept_needs_constant_expression), - /*AllowFold*/ false).take(); + NoexceptExpr + = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr, + 0, diag::err_noexcept_needs_constant_expression, + /*AllowFold*/ false).take(); } } @@ -2385,8 +2415,17 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl, InstantiatingTemplate::ExceptionSpecification()); - if (Inst) + if (Inst) { + // We hit the instantiation depth limit. Clear the exception specification + // so that our callers don't have to cope with EST_Uninstantiated. + FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_None; + Decl->setType(Context.getFunctionType(Proto->getResultType(), + Proto->arg_type_begin(), + Proto->getNumArgs(), + EPI)); return; + } // Enter the scope of this instantiation. We don't use // PushDeclContext because we don't have a scope. @@ -2411,7 +2450,7 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, bool TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl) { - if (Tmpl->isDeletedAsWritten()) + if (Tmpl->isDeleted()) New->setDeletedAsWritten(); // If we are performing substituting explicitly-specified template arguments @@ -2433,7 +2472,6 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, (void) FunTmpl; ActiveInst.Kind = ActiveInstType::TemplateInstantiation; ActiveInst.Entity = reinterpret_cast<uintptr_t>(New); - --SemaRef.NonInstantiationEntries; } } @@ -2452,6 +2490,8 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *ExceptionSpecTemplate = Tmpl; if (EPI.ExceptionSpecType == EST_Uninstantiated) ExceptionSpecTemplate = EPI.ExceptionSpecTemplate; + assert(EPI.ExceptionSpecType != EST_Unevaluated && + "instantiating implicitly-declared special member"); // Mark the function has having an uninstantiated exception specification. const FunctionProtoType *NewProto @@ -3238,8 +3278,8 @@ DeclContext *Sema::FindInstantiatedContext(SourceLocation Loc, DeclContext* DC, /// /// In the instantiation of X<int>::getKind(), we need to map the /// EnumConstantDecl for KnownValue (which refers to -/// X<T>::<Kind>::KnownValue) to its instantiation -/// (X<int>::<Kind>::KnownValue). InstantiateCurrentDeclRef() performs +/// X<T>::\<Kind>\::KnownValue) to its instantiation +/// (X<int>::\<Kind>\::KnownValue). InstantiateCurrentDeclRef() performs /// this mapping from within the instantiation of X<int>. NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, const MultiLevelTemplateArgumentList &TemplateArgs) { @@ -3422,7 +3462,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, void Sema::PerformPendingInstantiations(bool LocalOnly) { // Load pending instantiations from the external source. if (!LocalOnly && ExternalSource) { - SmallVector<std::pair<ValueDecl *, SourceLocation>, 4> Pending; + SmallVector<PendingImplicitInstantiation, 4> Pending; ExternalSource->ReadPendingInstantiations(Pending); PendingInstantiations.insert(PendingInstantiations.begin(), Pending.begin(), Pending.end()); diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index a40100c7a726..aece90b78590 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -12,6 +12,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/AST/Expr.h" @@ -34,10 +35,12 @@ namespace { SmallVectorImpl<UnexpandedParameterPack> &Unexpanded; + bool InLambda; + public: explicit CollectUnexpandedParameterPacksVisitor( SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) - : Unexpanded(Unexpanded) { } + : Unexpanded(Unexpanded), InLambda(false) { } bool shouldWalkTypesOfTypeLocs() const { return false; } @@ -107,17 +110,17 @@ namespace { /// \brief Suppress traversal into statements and expressions that /// do not contain unexpanded parameter packs. bool TraverseStmt(Stmt *S) { - if (Expr *E = dyn_cast_or_null<Expr>(S)) - if (E->containsUnexpandedParameterPack()) - return inherited::TraverseStmt(E); + Expr *E = dyn_cast_or_null<Expr>(S); + if ((E && E->containsUnexpandedParameterPack()) || InLambda) + return inherited::TraverseStmt(S); - return true; + return true; } /// \brief Suppress traversal into types that do not contain /// unexpanded parameter packs. bool TraverseType(QualType T) { - if (!T.isNull() && T->containsUnexpandedParameterPack()) + if ((!T.isNull() && T->containsUnexpandedParameterPack()) || InLambda) return inherited::TraverseType(T); return true; @@ -126,8 +129,9 @@ namespace { /// \brief Suppress traversel into types with location information /// that do not contain unexpanded parameter packs. bool TraverseTypeLoc(TypeLoc TL) { - if (!TL.getType().isNull() && - TL.getType()->containsUnexpandedParameterPack()) + if ((!TL.getType().isNull() && + TL.getType()->containsUnexpandedParameterPack()) || + InLambda) return inherited::TraverseTypeLoc(TL); return true; @@ -136,10 +140,10 @@ namespace { /// \brief Suppress traversal of non-parameter declarations, since /// they cannot contain unexpanded parameter packs. bool TraverseDecl(Decl *D) { - if (D && isa<ParmVarDecl>(D)) + if ((D && isa<ParmVarDecl>(D)) || InLambda) return inherited::TraverseDecl(D); - return true; + return true; } /// \brief Suppress traversal of template argument pack expansions. @@ -157,17 +161,57 @@ namespace { return inherited::TraverseTemplateArgumentLoc(ArgLoc); } + + /// \brief Note whether we're traversing a lambda containing an unexpanded + /// parameter pack. In this case, the unexpanded pack can occur anywhere, + /// including all the places where we normally wouldn't look. Within a + /// lambda, we don't propagate the 'contains unexpanded parameter pack' bit + /// outside an expression. + bool TraverseLambdaExpr(LambdaExpr *Lambda) { + // The ContainsUnexpandedParameterPack bit on a lambda is always correct, + // even if it's contained within another lambda. + if (!Lambda->containsUnexpandedParameterPack()) + return true; + + bool WasInLambda = InLambda; + InLambda = true; + + // If any capture names a function parameter pack, that pack is expanded + // when the lambda is expanded. + for (LambdaExpr::capture_iterator I = Lambda->capture_begin(), + E = Lambda->capture_end(); I != E; ++I) + if (VarDecl *VD = I->getCapturedVar()) + if (VD->isParameterPack()) + Unexpanded.push_back(std::make_pair(VD, I->getLocation())); + + inherited::TraverseLambdaExpr(Lambda); + + InLambda = WasInLambda; + return true; + } }; } /// \brief Diagnose all of the unexpanded parameter packs in the given /// vector. -void +bool Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, UnexpandedParameterPackContext UPPC, ArrayRef<UnexpandedParameterPack> Unexpanded) { if (Unexpanded.empty()) - return; + return false; + + // If we are within a lambda expression, that lambda contains an unexpanded + // parameter pack, and we are done. + // FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it + // later. + for (unsigned N = FunctionScopes.size(); N; --N) { + if (sema::LambdaScopeInfo *LSI = + dyn_cast<sema::LambdaScopeInfo>(FunctionScopes[N-1])) { + LSI->ContainsUnexpandedParameterPack = true; + return false; + } + } SmallVector<SourceLocation, 4> Locations; SmallVector<IdentifierInfo *, 4> Names; @@ -200,6 +244,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, for (unsigned I = 0, N = Locations.size(); I != N; ++I) DB << SourceRange(Locations[I]); + return true; } bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, @@ -215,8 +260,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc( T->getTypeLoc()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(Expr *E, @@ -230,8 +274,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E, SmallVector<UnexpandedParameterPack, 2> Unexpanded; CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS, @@ -247,9 +290,8 @@ bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseNestedNameSpecifier(SS.getScopeRep()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(), - UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(), + UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo, @@ -284,8 +326,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseType(NameInfo.getName().getCXXNameType()); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, @@ -299,8 +340,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseTemplateName(Template); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded); } bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg, @@ -313,8 +353,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg, CollectUnexpandedParameterPacksVisitor(Unexpanded) .TraverseTemplateArgumentLoc(Arg); assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs"); - DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded); - return true; + return DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded); } void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg, @@ -597,12 +636,13 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, return false; } -unsigned Sema::getNumArgumentsInExpansion(QualType T, +llvm::Optional<unsigned> Sema::getNumArgumentsInExpansion(QualType T, const MultiLevelTemplateArgumentList &TemplateArgs) { QualType Pattern = cast<PackExpansionType>(T)->getPattern(); SmallVector<UnexpandedParameterPack, 2> Unexpanded; CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(Pattern); + llvm::Optional<unsigned> Result; for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { // Compute the depth and index for this parameter pack. unsigned Depth; @@ -621,9 +661,14 @@ unsigned Sema::getNumArgumentsInExpansion(QualType T, llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = CurrentInstantiationScope->findInstantiationOf( Unexpanded[I].first.get<NamedDecl *>()); - if (Instantiation->is<DeclArgumentPack *>()) - return Instantiation->get<DeclArgumentPack *>()->size(); - + if (Instantiation->is<Decl*>()) + // The pattern refers to an unexpanded pack. We're not ready to expand + // this pack yet. + return llvm::Optional<unsigned>(); + + unsigned Size = Instantiation->get<DeclArgumentPack *>()->size(); + assert((!Result || *Result == Size) && "inconsistent pack sizes"); + Result = Size; continue; } @@ -631,13 +676,17 @@ unsigned Sema::getNumArgumentsInExpansion(QualType T, } if (Depth >= TemplateArgs.getNumLevels() || !TemplateArgs.hasTemplateArgument(Depth, Index)) - continue; + // The pattern refers to an unknown template argument. We're not ready to + // expand this pack yet. + return llvm::Optional<unsigned>(); // Determine the size of the argument pack. - return TemplateArgs(Depth, Index).pack_size(); + unsigned Size = TemplateArgs(Depth, Index).pack_size(); + assert((!Result || *Result == Size) && "inconsistent pack sizes"); + Result = Size; } - llvm_unreachable("No unexpanded parameter packs in type expansion."); + return Result; } bool Sema::containsUnexpandedParameterPacks(Declarator &D) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 1400e7e5c419..20fe036348ba 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -35,19 +35,19 @@ using namespace clang; /// isOmittedBlockReturnType - Return true if this declarator is missing a -/// return type because this is a omitted return type on a block literal. +/// return type because this is a omitted return type on a block literal. static bool isOmittedBlockReturnType(const Declarator &D) { if (D.getContext() != Declarator::BlockLiteralContext || D.getDeclSpec().hasTypeSpecifier()) return false; - + if (D.getNumTypeObjects() == 0) return true; // ^{ ... } - + if (D.getNumTypeObjects() == 1 && D.getTypeObject(0).Kind == DeclaratorChunk::Function) return true; // ^(int X, float Y) { ... } - + return false; } @@ -59,12 +59,12 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, unsigned diagID = 0; switch (attr.getKind()) { - case AttributeList::AT_objc_gc: + case AttributeList::AT_ObjCGC: diagID = diag::warn_pointer_attribute_wrong_type; useExpansionLoc = true; break; - case AttributeList::AT_objc_ownership: + case AttributeList::AT_ObjCOwnership: diagID = diag::warn_objc_object_attribute_wrong_type; useExpansionLoc = true; break; @@ -93,19 +93,19 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, // objc_gc applies to Objective-C pointers or, otherwise, to the // smallest available pointer type (i.e. 'void*' in 'void**'). #define OBJC_POINTER_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_objc_gc: \ - case AttributeList::AT_objc_ownership + case AttributeList::AT_ObjCGC: \ + case AttributeList::AT_ObjCOwnership // Function type attributes. #define FUNCTION_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_noreturn: \ - case AttributeList::AT_cdecl: \ - case AttributeList::AT_fastcall: \ - case AttributeList::AT_stdcall: \ - case AttributeList::AT_thiscall: \ - case AttributeList::AT_pascal: \ - case AttributeList::AT_regparm: \ - case AttributeList::AT_pcs \ + case AttributeList::AT_NoReturn: \ + case AttributeList::AT_CDecl: \ + case AttributeList::AT_FastCall: \ + case AttributeList::AT_StdCall: \ + case AttributeList::AT_ThisCall: \ + case AttributeList::AT_Pascal: \ + case AttributeList::AT_Regparm: \ + case AttributeList::AT_Pcs \ namespace { /// An object which stores processing state for the entire @@ -284,9 +284,9 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, static bool handleObjCPointerTypeAttr(TypeProcessingState &state, AttributeList &attr, QualType &type) { - if (attr.getKind() == AttributeList::AT_objc_gc) + if (attr.getKind() == AttributeList::AT_ObjCGC) return handleObjCGCTypeAttr(state, attr, type); - assert(attr.getKind() == AttributeList::AT_objc_ownership); + assert(attr.getKind() == AttributeList::AT_ObjCOwnership); return handleObjCOwnershipTypeAttr(state, attr, type); } @@ -412,7 +412,7 @@ static void distributeFunctionTypeAttr(TypeProcessingState &state, continue; } } - + diagnoseBadTypeAttribute(state.getSema(), attr, type); } @@ -505,7 +505,7 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType); break; - case AttributeList::AT_ns_returns_retained: + case AttributeList::AT_NSReturnsRetained: if (!state.getSema().getLangOpts().ObjCAutoRefCount) break; // fallthrough @@ -554,7 +554,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, // ...and *prepend* it to the declarator. declarator.AddInnermostTypeInfo(DeclaratorChunk::getFunction( /*proto*/ true, - /*variadic*/ false, SourceLocation(), + /*variadic*/ false, + /*ambiguous*/ false, SourceLocation(), /*args*/ 0, 0, /*type quals*/ 0, /*ref-qualifier*/true, SourceLocation(), @@ -573,7 +574,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, /// \brief Convert the specified declspec to the appropriate type /// object. -/// \param D the declarator containing the declaration specifier. +/// \param state Specifies the declarator containing the declaration specifier +/// to be converted, along with other associated processing state. /// \returns The type described by the declaration specifiers. This function /// never returns null. static QualType ConvertDeclSpecToType(TypeProcessingState &state) { @@ -586,7 +588,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { SourceLocation DeclLoc = declarator.getIdentifierLoc(); if (DeclLoc.isInvalid()) DeclLoc = DS.getLocStart(); - + ASTContext &Context = S.Context; QualType Result; @@ -639,7 +641,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { Result = Context.getObjCObjectPointerType(Result); break; } - + // If this is a missing declspec in a block literal return context, then it // is inferred from the return statements inside the block. // The declspec is always missing in a lambda expr context; it is either @@ -695,7 +697,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { case DeclSpec::TSW_long: Result = Context.LongTy; break; case DeclSpec::TSW_longlong: Result = Context.LongLongTy; - + // long long is a C99 feature. if (!S.getLangOpts().C99) S.Diag(DS.getTypeSpecWidthLoc(), @@ -710,7 +712,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { case DeclSpec::TSW_long: Result = Context.UnsignedLongTy; break; case DeclSpec::TSW_longlong: Result = Context.UnsignedLongLongTy; - + // long long is a C99 feature. if (!S.getLangOpts().C99) S.Diag(DS.getTypeSpecWidthLoc(), @@ -762,10 +764,10 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // If the type is deprecated or unavailable, diagnose it. S.DiagnoseUseOfDecl(D, DS.getTypeSpecTypeNameLoc()); - + assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 && DS.getTypeSpecSign() == 0 && "No qualifiers on tag names!"); - + // TypeQuals handled by caller. Result = Context.getTypeDeclType(D); @@ -858,7 +860,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { Result = Context.IntTy; declarator.setInvalidType(true); } - break; + break; case DeclSpec::TST_auto: { // TypeQuals handled by caller. @@ -878,7 +880,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { Result = Context.IntTy; declarator.setInvalidType(true); } - break; + break; case DeclSpec::TST_error: Result = Context.IntTy; @@ -983,15 +985,15 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // C90 6.5.3 constraints: "The same type qualifier shall not appear more // than once in the same specifier-list or qualifier-list, either directly // or via one or more typedefs." - if (!S.getLangOpts().C99 && !S.getLangOpts().CPlusPlus + if (!S.getLangOpts().C99 && !S.getLangOpts().CPlusPlus && TypeQuals & Result.getCVRQualifiers()) { if (TypeQuals & DeclSpec::TQ_const && Result.isConstQualified()) { - S.Diag(DS.getConstSpecLoc(), diag::ext_duplicate_declspec) + S.Diag(DS.getConstSpecLoc(), diag::ext_duplicate_declspec) << "const"; } if (TypeQuals & DeclSpec::TQ_volatile && Result.isVolatileQualified()) { - S.Diag(DS.getVolatileSpecLoc(), diag::ext_duplicate_declspec) + S.Diag(DS.getVolatileSpecLoc(), diag::ext_duplicate_declspec) << "volatile"; } @@ -1036,7 +1038,7 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, if (!PTy->getPointeeType()->isIncompleteOrObjectType()) { DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee; ProblemTy = T->getAs<PointerType>()->getPointeeType(); - } + } } else if (!Ty->isDependentType()) { // FIXME: this deserves a proper diagnostic DiagID = diag::err_typecheck_invalid_restrict_invalid_pointee; @@ -1083,7 +1085,7 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type, // If we are in an unevaluated context, like sizeof, skip adding a // qualification. - } else if (S.ExprEvalContexts.back().Context == Sema::Unevaluated) { + } else if (S.isUnevaluatedContext()) { return type; // If that failed, give an error and recover using __strong. __strong @@ -1157,14 +1159,14 @@ QualType Sema::BuildPointerType(QualType T, QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, SourceLocation Loc, DeclarationName Entity) { - assert(Context.getCanonicalType(T) != Context.OverloadTy && + assert(Context.getCanonicalType(T) != Context.OverloadTy && "Unresolved overloaded function type"); - + // C++0x [dcl.ref]p6: - // If a typedef (7.1.3), a type template-parameter (14.3.1), or a - // decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a - // type T, an attempt to create the type "lvalue reference to cv TR" creates - // the type "lvalue reference to T", while an attempt to create the type + // If a typedef (7.1.3), a type template-parameter (14.3.1), or a + // decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a + // type T, an attempt to create the type "lvalue reference to cv TR" creates + // the type "lvalue reference to T", while an attempt to create the type // "rvalue reference to cv TR" creates the type TR. bool LValueRef = SpelledAsLValue || T->getAs<LValueReferenceType>(); @@ -1205,9 +1207,20 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) { // If the size is an ICE, it certainly isn't a VLA. If we're in a GNU mode // (like gnu99, but not c99) accept any evaluatable value as an extension. - return S.VerifyIntegerConstantExpression( - ArraySize, &SizeVal, S.PDiag(), S.LangOpts.GNUMode, - S.PDiag(diag::ext_vla_folded_to_constant)).isInvalid(); + class VLADiagnoser : public Sema::VerifyICEDiagnoser { + public: + VLADiagnoser() : Sema::VerifyICEDiagnoser(true) {} + + virtual void diagnoseNotICE(Sema &S, SourceLocation Loc, SourceRange SR) { + } + + virtual void diagnoseFold(Sema &S, SourceLocation Loc, SourceRange SR) { + S.Diag(Loc, diag::ext_vla_folded_to_constant) << SR; + } + } Diagnoser; + + return S.VerifyIntegerConstantExpression(ArraySize, &SizeVal, Diagnoser, + S.LangOpts.GNUMode).isInvalid(); } @@ -1219,9 +1232,7 @@ static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) { /// /// \param ArraySize Expression describing the size of the array. /// -/// \param Loc The location of the entity whose type involves this -/// array type or, if there is no such entity, the location of the -/// type that will have array type. +/// \param Brackets The range from the opening '[' to the closing ']'. /// /// \param Entity The name of the entity that involves the array /// type, if known. @@ -1236,25 +1247,30 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (getLangOpts().CPlusPlus) { // C++ [dcl.array]p1: // T is called the array element type; this type shall not be a reference - // type, the (possibly cv-qualified) type void, a function type or an + // type, the (possibly cv-qualified) type void, a function type or an // abstract class type. // + // C++ [dcl.array]p3: + // When several "array of" specifications are adjacent, [...] only the + // first of the constant expressions that specify the bounds of the arrays + // may be omitted. + // // Note: function types are handled in the common path with C. if (T->isReferenceType()) { Diag(Loc, diag::err_illegal_decl_array_of_references) << getPrintableNameForEntity(Entity) << T; return QualType(); } - - if (T->isVoidType()) { + + if (T->isVoidType() || T->isIncompleteArrayType()) { Diag(Loc, diag::err_illegal_decl_array_incomplete_type) << T; return QualType(); } - - if (RequireNonAbstractType(Brackets.getBegin(), T, + + if (RequireNonAbstractType(Brackets.getBegin(), T, diag::err_array_of_abstract_type)) return QualType(); - + } else { // C99 6.7.5.2p1: If the element type is an incomplete or function type, // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]()) @@ -1350,7 +1366,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (ConstVal == 0) { // GCC accepts zero sized static arrays. We allow them when // we're not in a SFINAE context. - Diag(ArraySize->getLocStart(), + Diag(ArraySize->getLocStart(), isSFINAEContext()? diag::err_typecheck_zero_array_size : diag::ext_typecheck_zero_array_size) << ArraySize->getSourceRange(); @@ -1361,9 +1377,9 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, << ArraySize->getSourceRange(); ASM = ArrayType::Normal; } - } else if (!T->isDependentType() && !T->isVariablyModifiedType() && + } else if (!T->isDependentType() && !T->isVariablyModifiedType() && !T->isIncompleteType()) { - // Is the array too large? + // Is the array too large? unsigned ActiveSizeBits = ConstantArrayType::getNumAddressingBits(Context, T, ConstVal); if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context)) @@ -1371,7 +1387,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, << ConstVal.toString(10) << ArraySize->getSourceRange(); } - + T = Context.getConstantArrayType(T, ConstVal, ASM, Quals); } // If this is not C99, extwarn about VLA's and C99 array size modifiers. @@ -1379,13 +1395,13 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (T->isVariableArrayType()) { // Prohibit the use of non-POD types in VLAs. QualType BaseT = Context.getBaseElementType(T); - if (!T->isDependentType() && + if (!T->isDependentType() && !BaseT.isPODType(Context) && !BaseT->isObjCLifetimeType()) { Diag(Loc, diag::err_vla_non_pod) << BaseT; return QualType(); - } + } // Prohibit the use of VLAs during template argument deduction. else if (isSFINAEContext()) { Diag(Loc, diag::err_vla_in_sfinae); @@ -1480,7 +1496,7 @@ QualType Sema::BuildFunctionType(QualType T, SourceLocation Loc, DeclarationName Entity, FunctionType::ExtInfo Info) { if (T->isArrayType() || T->isFunctionType()) { - Diag(Loc, diag::err_func_returning_array_function) + Diag(Loc, diag::err_func_returning_array_function) << T->isFunctionType() << T; return QualType(); } @@ -1581,18 +1597,14 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class, /// /// \param T The type to which we'll be building a block pointer. /// -/// \param CVR The cvr-qualifiers to be applied to the block pointer type. -/// -/// \param Loc The location of the entity whose type involves this -/// block pointer type or, if there is no such entity, the location of the -/// type that will have block pointer type. +/// \param Loc The source location, used for diagnostics. /// /// \param Entity The name of the entity that involves the block pointer /// type, if known. /// /// \returns A suitable block pointer type, if there are no /// errors. Otherwise, returns a NULL type. -QualType Sema::BuildBlockPointerType(QualType T, +QualType Sema::BuildBlockPointerType(QualType T, SourceLocation Loc, DeclarationName Entity) { if (!T->isFunctionType()) { @@ -1688,7 +1700,7 @@ static void inferARCWriteback(TypeProcessingState &state, // Otherwise, modify the type in-place. Qualifiers qs; - + if (declSpecType->isObjCARCImplicitlyUnretainedType()) qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone); else @@ -1711,7 +1723,7 @@ static void inferARCWriteback(TypeProcessingState &state, return; for (const AttributeList *attr = chunk.getAttrs(); attr; attr = attr->getNext()) - if (attr->getKind() == AttributeList::AT_objc_ownership) + if (attr->getKind() == AttributeList::AT_ObjCOwnership) return; transferARCOwnershipToDeclaratorChunk(state, Qualifiers::OCL_Autoreleasing, @@ -1786,7 +1798,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case UnqualifiedId::IK_LiteralOperatorId: case UnqualifiedId::IK_TemplateId: T = ConvertDeclSpecToType(state); - + if (!D.isInvalidType() && D.getDeclSpec().isTypeSpecOwned()) { OwnedTagDecl = cast<TagDecl>(D.getDeclSpec().getRepAsDecl()); // Owned declaration is embedded in declarator. @@ -1798,14 +1810,16 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case UnqualifiedId::IK_ConstructorTemplateId: case UnqualifiedId::IK_DestructorName: // Constructors and destructors don't have return types. Use - // "void" instead. + // "void" instead. T = SemaRef.Context.VoidTy; + if (AttributeList *attrs = D.getDeclSpec().getAttributes().getList()) + processTypeAttrs(state, T, true, attrs); break; case UnqualifiedId::IK_ConversionFunctionId: // The result type of a conversion function is the type that it // converts to. - T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId, + T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId, &ReturnTypeInfo); break; } @@ -1890,7 +1904,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, DeclaratorChunk &DeclType = D.getTypeObject(chunkIndex); if (DeclType.Kind == DeclaratorChunk::Function) { const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun; - if (FTI.TrailingReturnType) { + if (FTI.hasTrailingReturnType()) { Error = -1; break; } @@ -2029,6 +2043,102 @@ static void checkQualifiedFunction(Sema &S, QualType T, << getFunctionQualifiersAsString(T->castAs<FunctionProtoType>()); } +/// Produce an approprioate diagnostic for an ambiguity between a function +/// declarator and a C++ direct-initializer. +static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, + DeclaratorChunk &DeclType, QualType RT) { + const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun; + assert(FTI.isAmbiguous && "no direct-initializer / function ambiguity"); + + // If the return type is void there is no ambiguity. + if (RT->isVoidType()) + return; + + // An initializer for a non-class type can have at most one argument. + if (!RT->isRecordType() && FTI.NumArgs > 1) + return; + + // An initializer for a reference must have exactly one argument. + if (RT->isReferenceType() && FTI.NumArgs != 1) + return; + + // Only warn if this declarator is declaring a function at block scope, and + // doesn't have a storage class (such as 'extern') specified. + if (!D.isFunctionDeclarator() || + D.getFunctionDefinitionKind() != FDK_Declaration || + !S.CurContext->isFunctionOrMethod() || + D.getDeclSpec().getStorageClassSpecAsWritten() + != DeclSpec::SCS_unspecified) + return; + + // Inside a condition, a direct initializer is not permitted. We allow one to + // be parsed in order to give better diagnostics in condition parsing. + if (D.getContext() == Declarator::ConditionContext) + return; + + SourceRange ParenRange(DeclType.Loc, DeclType.EndLoc); + + S.Diag(DeclType.Loc, + FTI.NumArgs ? diag::warn_parens_disambiguated_as_function_declaration + : diag::warn_empty_parens_are_function_decl) + << ParenRange; + + // If the declaration looks like: + // T var1, + // f(); + // and name lookup finds a function named 'f', then the ',' was + // probably intended to be a ';'. + if (!D.isFirstDeclarator() && D.getIdentifier()) { + FullSourceLoc Comma(D.getCommaLoc(), S.SourceMgr); + FullSourceLoc Name(D.getIdentifierLoc(), S.SourceMgr); + if (Comma.getFileID() != Name.getFileID() || + Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { + LookupResult Result(S, D.getIdentifier(), SourceLocation(), + Sema::LookupOrdinaryName); + if (S.LookupName(Result, S.getCurScope())) + S.Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) + << FixItHint::CreateReplacement(D.getCommaLoc(), ";") + << D.getIdentifier(); + } + } + + if (FTI.NumArgs > 0) { + // For a declaration with parameters, eg. "T var(T());", suggest adding parens + // around the first parameter to turn the declaration into a variable + // declaration. + SourceRange Range = FTI.ArgInfo[0].Param->getSourceRange(); + SourceLocation B = Range.getBegin(); + SourceLocation E = S.PP.getLocForEndOfToken(Range.getEnd()); + // FIXME: Maybe we should suggest adding braces instead of parens + // in C++11 for classes that don't have an initializer_list constructor. + S.Diag(B, diag::note_additional_parens_for_variable_declaration) + << FixItHint::CreateInsertion(B, "(") + << FixItHint::CreateInsertion(E, ")"); + } else { + // For a declaration without parameters, eg. "T var();", suggest replacing the + // parens with an initializer to turn the declaration into a variable + // declaration. + const CXXRecordDecl *RD = RT->getAsCXXRecordDecl(); + + // Empty parens mean value-initialization, and no parens mean + // default initialization. These are equivalent if the default + // constructor is user-provided or if zero-initialization is a + // no-op. + if (RD && RD->hasDefinition() && + (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) + S.Diag(DeclType.Loc, diag::note_empty_parens_default_ctor) + << FixItHint::CreateRemoval(ParenRange); + else { + std::string Init = S.getFixItZeroInitializerForType(RT); + if (Init.empty() && S.LangOpts.CPlusPlus0x) + Init = "{}"; + if (!Init.empty()) + S.Diag(DeclType.Loc, diag::note_empty_parens_zero_initialize) + << FixItHint::CreateReplacement(ParenRange, Init); + } + } +} + static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -2165,12 +2275,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // trailing-return-type is only required if we're declaring a function, // and not, for instance, a pointer to a function. if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && - !FTI.TrailingReturnType && chunkIndex == 0) { + !FTI.hasTrailingReturnType() && chunkIndex == 0) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_auto_missing_trailing_return); T = Context.IntTy; D.setInvalidType(true); - } else if (FTI.TrailingReturnType) { + } else if (FTI.hasTrailingReturnType()) { // T must be exactly 'auto' at this point. See CWG issue 681. if (isa<ParenType>(T)) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), @@ -2184,10 +2294,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, << T << D.getDeclSpec().getSourceRange(); D.setInvalidType(true); } - - T = S.GetTypeFromParser( - ParsedType::getFromOpaquePtr(FTI.TrailingReturnType), - &TInfo); + T = S.GetTypeFromParser(FTI.getTrailingReturnType(), &TInfo); + if (T.isNull()) { + // An error occurred parsing the trailing return type. + T = Context.IntTy; + D.setInvalidType(true); + } } } @@ -2259,6 +2371,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, << (D.getContext() == Declarator::AliasDeclContext || D.getContext() == Declarator::AliasTemplateContext); + // If we see "T var();" or "T var(T());" at block scope, it is probably + // an attempt to initialize a variable, not a function declaration. + if (FTI.isAmbiguous) + warnAboutAmbiguousFunction(S, D, DeclType, T); + if (!FTI.NumArgs && !FTI.isVariadic && !LangOpts.CPlusPlus) { // Simple void foo(), where the incoming T is the result type. T = Context.getFunctionNoProtoType(T); @@ -2270,7 +2387,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, bool Overloadable = false; for (const AttributeList *Attrs = D.getAttributes(); Attrs; Attrs = Attrs->getNext()) { - if (Attrs->getKind() == AttributeList::AT_overloadable) { + if (Attrs->getKind() == AttributeList::AT_Overloadable) { Overloadable = true; break; } @@ -2290,12 +2407,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, FunctionProtoType::ExtProtoInfo EPI; EPI.Variadic = FTI.isVariadic; - EPI.HasTrailingReturn = FTI.TrailingReturnType; + EPI.HasTrailingReturn = FTI.hasTrailingReturnType(); EPI.TypeQuals = FTI.TypeQuals; EPI.RefQualifier = !FTI.hasRefQualifier()? RQ_None : FTI.RefQualifierIsLValueRef? RQ_LValue : RQ_RValue; - + // Otherwise, we have a function with an argument list that is // potentially variadic. SmallVector<QualType, 16> ArgTys; @@ -2311,7 +2428,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, assert(!ArgTy.isNull() && "Couldn't parse type?"); // Adjust the parameter type. - assert((ArgTy == Context.getAdjustedParameterType(ArgTy)) && + assert((ArgTy == Context.getAdjustedParameterType(ArgTy)) && "Unadjusted type?"); // Look for 'void'. void is allowed only as a single argument to a @@ -2374,7 +2491,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, SmallVector<ParsedType, 2> DynamicExceptions; SmallVector<SourceRange, 2> DynamicExceptionRanges; Expr *NoexceptExpr = 0; - + if (FTI.getExceptionSpecType() == EST_Dynamic) { // FIXME: It's rather inefficient to have to split into two vectors // here. @@ -2388,14 +2505,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) { NoexceptExpr = FTI.NoexceptExpr; } - + S.checkExceptionSpecification(FTI.getExceptionSpecType(), DynamicExceptions, DynamicExceptionRanges, NoexceptExpr, Exceptions, EPI); - + if (FTI.getExceptionSpecType() == EST_None && ImplicitlyNoexcept && chunkIndex == 0) { // Only the outermost chunk is marked noexcept, of course. @@ -2475,7 +2592,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, const FunctionProtoType *FnTy = T->getAs<FunctionProtoType>(); assert(FnTy && "Why oh why is there not a FunctionProtoType here?"); - // C++ 8.3.5p4: + // C++ 8.3.5p4: // A cv-qualifier-seq shall only be part of the function type // for a nonstatic member function, the function type to which a pointer // to member refers, or the top-level function type of a function typedef @@ -2503,7 +2620,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // Rebuild function type adding a 'const' qualifier. FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo(); EPI.TypeQuals |= DeclSpec::TQ_const; - T = Context.getFunctionType(FnTy->getResultType(), + T = Context.getFunctionType(FnTy->getResultType(), FnTy->arg_type_begin(), FnTy->getNumArgs(), EPI); } @@ -2540,7 +2657,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // RemovalLocs.push_back(Chunk.Fun.getRestrictQualifierLoc()); if (!RemovalLocs.empty()) { std::sort(RemovalLocs.begin(), RemovalLocs.end(), - SourceManager::LocBeforeThanCompare(S.getSourceManager())); + BeforeThanCompare<SourceLocation>(S.getSourceManager())); RemovalRange = SourceRange(RemovalLocs.front(), RemovalLocs.back()); Loc = RemovalLocs.front(); } @@ -2556,7 +2673,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, EPI.TypeQuals = 0; EPI.RefQualifier = RQ_None; - T = Context.getFunctionType(FnTy->getResultType(), + T = Context.getFunctionType(FnTy->getResultType(), FnTy->arg_type_begin(), FnTy->getNumArgs(), EPI); } @@ -2572,31 +2689,31 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // C++0x [dcl.constexpr]p9: // A constexpr specifier used in an object declaration declares the object - // as const. + // as const. if (D.getDeclSpec().isConstexprSpecified() && T->isObjectType()) { T.addConst(); } - // If there was an ellipsis in the declarator, the declaration declares a + // If there was an ellipsis in the declarator, the declaration declares a // parameter pack whose type may be a pack expansion type. if (D.hasEllipsis() && !T.isNull()) { // C++0x [dcl.fct]p13: - // A declarator-id or abstract-declarator containing an ellipsis shall + // A declarator-id or abstract-declarator containing an ellipsis shall // only be used in a parameter-declaration. Such a parameter-declaration // is a parameter pack (14.5.3). [...] switch (D.getContext()) { case Declarator::PrototypeContext: // C++0x [dcl.fct]p13: - // [...] When it is part of a parameter-declaration-clause, the - // parameter pack is a function parameter pack (14.5.3). The type T + // [...] When it is part of a parameter-declaration-clause, the + // parameter pack is a function parameter pack (14.5.3). The type T // of the declarator-id of the function parameter pack shall contain - // a template parameter pack; each template parameter pack in T is + // a template parameter pack; each template parameter pack in T is // expanded by the function parameter pack. // // We represent function parameter packs as function parameters whose // type is a pack expansion. if (!T->containsUnexpandedParameterPack()) { - S.Diag(D.getEllipsisLoc(), + S.Diag(D.getEllipsisLoc(), diag::err_function_parameter_pack_without_parameter_packs) << T << D.getSourceRange(); D.setEllipsisLoc(SourceLocation()); @@ -2604,10 +2721,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, T = Context.getPackExpansionType(T, llvm::Optional<unsigned>()); } break; - + case Declarator::TemplateParamContext: // C++0x [temp.param]p15: - // If a template-parameter is a [...] is a parameter-declaration that + // If a template-parameter is a [...] is a parameter-declaration that // declares a parameter pack (8.3.5), then the template-parameter is a // template parameter pack (14.5.3). // @@ -2622,7 +2739,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, ? diag::warn_cxx98_compat_variadic_templates : diag::ext_variadic_templates); break; - + case Declarator::FileContext: case Declarator::KNRTypeListContext: case Declarator::ObjCParameterContext: // FIXME: special diagnostic here? @@ -2675,7 +2792,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { if (D.isPrototypeContext() && getLangOpts().ObjCAutoRefCount) inferARCWriteback(state, T); - + return GetFullTypeForDeclarator(state, T, ReturnTypeInfo); } @@ -2700,7 +2817,7 @@ static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state, DeclaratorChunk &chunk = D.getTypeObject(chunkIndex); for (const AttributeList *attr = chunk.getAttrs(); attr; attr = attr->getNext()) - if (attr->getKind() == AttributeList::AT_objc_ownership) + if (attr->getKind() == AttributeList::AT_ObjCOwnership) return; const char *attrStr = 0; @@ -2718,14 +2835,13 @@ static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state, .create(&S.Context.Idents.get("objc_ownership"), SourceLocation(), /*scope*/ 0, SourceLocation(), &S.Context.Idents.get(attrStr), SourceLocation(), - /*args*/ 0, 0, - /*declspec*/ false, /*C++0x*/ false); + /*args*/ 0, 0, AttributeList::AS_GNU); spliceAttrIntoList(*attr, chunk.getAttrListRef()); // TODO: mark whether we did this inference? } -/// \brief Used for transfering ownership in casts resulting in l-values. +/// \brief Used for transferring ownership in casts resulting in l-values. static void transferARCOwnership(TypeProcessingState &state, QualType &declSpecTy, Qualifiers::ObjCLifetime ownership) { @@ -2763,7 +2879,7 @@ static void transferARCOwnership(TypeProcessingState &state, if (inner == -1) return; - DeclaratorChunk &chunk = D.getTypeObject(inner); + DeclaratorChunk &chunk = D.getTypeObject(inner); if (chunk.Kind == DeclaratorChunk::Pointer) { if (declSpecTy->isObjCRetainableType()) return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership); @@ -2797,33 +2913,33 @@ TypeSourceInfo *Sema::GetTypeForDeclaratorCast(Declarator &D, QualType FromTy) { static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { switch (kind) { case AttributedType::attr_address_space: - return AttributeList::AT_address_space; + return AttributeList::AT_AddressSpace; case AttributedType::attr_regparm: - return AttributeList::AT_regparm; + return AttributeList::AT_Regparm; case AttributedType::attr_vector_size: - return AttributeList::AT_vector_size; + return AttributeList::AT_VectorSize; case AttributedType::attr_neon_vector_type: - return AttributeList::AT_neon_vector_type; + return AttributeList::AT_NeonVectorType; case AttributedType::attr_neon_polyvector_type: - return AttributeList::AT_neon_polyvector_type; + return AttributeList::AT_NeonPolyVectorType; case AttributedType::attr_objc_gc: - return AttributeList::AT_objc_gc; + return AttributeList::AT_ObjCGC; case AttributedType::attr_objc_ownership: - return AttributeList::AT_objc_ownership; + return AttributeList::AT_ObjCOwnership; case AttributedType::attr_noreturn: - return AttributeList::AT_noreturn; + return AttributeList::AT_NoReturn; case AttributedType::attr_cdecl: - return AttributeList::AT_cdecl; + return AttributeList::AT_CDecl; case AttributedType::attr_fastcall: - return AttributeList::AT_fastcall; + return AttributeList::AT_FastCall; case AttributedType::attr_stdcall: - return AttributeList::AT_stdcall; + return AttributeList::AT_StdCall; case AttributedType::attr_thiscall: - return AttributeList::AT_thiscall; + return AttributeList::AT_ThisCall; case AttributedType::attr_pascal: - return AttributeList::AT_pascal; + return AttributeList::AT_Pascal; case AttributedType::attr_pcs: - return AttributeList::AT_pcs; + return AttributeList::AT_Pcs; } llvm_unreachable("unexpected attribute kind!"); } @@ -2856,7 +2972,7 @@ namespace { const DeclSpec &DS; public: - TypeSpecLocFiller(ASTContext &Context, const DeclSpec &DS) + TypeSpecLocFiller(ASTContext &Context, const DeclSpec &DS) : Context(Context), DS(DS) {} void VisitAttributedTypeLoc(AttributedTypeLoc TL) { @@ -2871,6 +2987,10 @@ namespace { } void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { TL.setNameLoc(DS.getTypeSpecTypeLoc()); + // FIXME. We should have DS.getTypeSpecTypeEndLoc(). But, it requires + // addition field. What we have is good enough for dispay of location + // of 'fixit' on interface name. + TL.setNameEndLoc(DS.getLocEnd()); } void VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { // Handle the base type, which might not have been written explicitly. @@ -3000,7 +3120,7 @@ namespace { void VisitAtomicTypeLoc(AtomicTypeLoc TL) { TL.setKWLoc(DS.getTypeSpecTypeLoc()); TL.setParensRange(DS.getTypeofParensRange()); - + TypeSourceInfo *TInfo = 0; Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); @@ -3104,7 +3224,7 @@ namespace { assert(Chunk.Kind == DeclaratorChunk::Function); TL.setLocalRangeBegin(Chunk.Loc); TL.setLocalRangeEnd(Chunk.EndLoc); - TL.setTrailingReturn(!!Chunk.Fun.TrailingReturnType); + TL.setTrailingReturn(Chunk.Fun.hasTrailingReturnType()); const DeclaratorChunk::FunctionTypeInfo &FTI = Chunk.Fun; for (unsigned i = 0, e = TL.getNumArgs(), tpi = 0; i != e; ++i) { @@ -3142,9 +3262,9 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T, // Handle parameter packs whose type is a pack expansion. if (isa<PackExpansionType>(T)) { cast<PackExpansionTypeLoc>(CurrTL).setEllipsisLoc(D.getEllipsisLoc()); - CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); + CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); } - + for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { while (isa<AttributedTypeLoc>(CurrTL)) { AttributedTypeLoc TL = cast<AttributedTypeLoc>(CurrTL); @@ -3155,7 +3275,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T, DeclaratorLocFiller(Context, D.getTypeObject(i)).Visit(CurrTL); CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); } - + // If we have different source information for the return type, use // that. This really only applies to C++ conversion functions. if (ReturnTypeInfo) { @@ -3165,7 +3285,7 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T, } else { TypeSpecLocFiller(Context, D.getDeclSpec()).Visit(CurrTL); } - + return TInfo; } @@ -3174,7 +3294,7 @@ ParsedType Sema::CreateParsedType(QualType T, TypeSourceInfo *TInfo) { // FIXME: LocInfoTypes are "transient", only needed for passing to/from Parser // and Sema during declaration parsing. Try deallocating/caching them when // it's appropriate, instead of allocating them and keeping them around. - LocInfoType *LocT = (LocInfoType*)BumpAlloc.Allocate(sizeof(LocInfoType), + LocInfoType *LocT = (LocInfoType*)BumpAlloc.Allocate(sizeof(LocInfoType), TypeAlignment); new (LocT) LocInfoType(T, TInfo); assert(LocT->getTypeClass() != T->getTypeClass() && @@ -3304,13 +3424,13 @@ static bool hasDirectOwnershipQualifier(QualType type) { // X *__strong (...) } else if (const ParenType *paren = dyn_cast<ParenType>(type)) { type = paren->getInnerType(); - + // That's it for things we want to complain about. In particular, // we do not want to look through typedefs, typeof(expr), // typeof(type), or any other way that the type is somehow // abstracted. } else { - + return false; } } @@ -3439,8 +3559,8 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, attr.setInvalid(); return true; } - - // Forbid __weak for class objects marked as + + // Forbid __weak for class objects marked as // objc_arc_weak_reference_unavailable if (lifetime == Qualifiers::OCL_Weak) { QualType T = type; @@ -3450,12 +3570,12 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl(); if (Class->isArcWeakrefUnavailable()) { S.Diag(AttrLoc, diag::err_arc_unsupported_weak_class); - S.Diag(ObjT->getInterfaceDecl()->getLocation(), + S.Diag(ObjT->getInterfaceDecl()->getLocation(), diag::note_class_declared); } } } - + return true; } @@ -3654,7 +3774,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, FunctionTypeUnwrapper unwrapped(S, type); - if (attr.getKind() == AttributeList::AT_noreturn) { + if (attr.getKind() == AttributeList::AT_NoReturn) { if (S.CheckNoReturnAttr(attr)) return true; @@ -3670,7 +3790,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, // ns_returns_retained is not always a type attribute, but if we got // here, we're treating it as one right now. - if (attr.getKind() == AttributeList::AT_ns_returns_retained) { + if (attr.getKind() == AttributeList::AT_NSReturnsRetained) { assert(S.getLangOpts().ObjCAutoRefCount && "ns_returns_retained treated as type attribute in non-ARC"); if (attr.getNumArgs()) return true; @@ -3685,7 +3805,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } - if (attr.getKind() == AttributeList::AT_regparm) { + if (attr.getKind() == AttributeList::AT_Regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) return true; @@ -3705,7 +3825,7 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } - FunctionType::ExtInfo EI = + FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withRegParm(value); type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); return true; @@ -3860,11 +3980,11 @@ static void HandleVectorSizeAttr(QualType& CurType, const AttributeList &Attr, /// \brief Process the OpenCL-like ext_vector_type attribute when it occurs on /// a type. -static void HandleExtVectorTypeAttr(QualType &CurType, - const AttributeList &Attr, +static void HandleExtVectorTypeAttr(QualType &CurType, + const AttributeList &Attr, Sema &S) { Expr *sizeExpr; - + // Special case where the argument is a template id. if (Attr.getParameterName()) { CXXScopeSpec SS; @@ -3876,7 +3996,7 @@ static void HandleExtVectorTypeAttr(QualType &CurType, id, false, false); if (Size.isInvalid()) return; - + sizeExpr = Size.get(); } else { // check the attribute arguments. @@ -3886,7 +4006,7 @@ static void HandleExtVectorTypeAttr(QualType &CurType, } sizeExpr = Attr.getArg(0); } - + // Create the vector type. QualType T = S.BuildExtVectorType(CurType, sizeExpr, Attr.getLoc()); if (!T.isNull()) @@ -3973,12 +4093,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, switch (attr.getKind()) { default: break; - case AttributeList::AT_may_alias: + case AttributeList::AT_MayAlias: // FIXME: This attribute needs to actually be handled, but if we ignore // it it breaks large amounts of Linux software. attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_address_space: + case AttributeList::AT_AddressSpace: HandleAddressSpaceTypeAttribute(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; @@ -3987,35 +4107,42 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, distributeObjCPointerTypeAttr(state, attr, type); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_vector_size: + case AttributeList::AT_VectorSize: HandleVectorSizeAttr(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_ext_vector_type: + case AttributeList::AT_ExtVectorType: if (state.getDeclarator().getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef) HandleExtVectorTypeAttr(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_neon_vector_type: + case AttributeList::AT_NeonVectorType: HandleNeonVectorTypeAttr(type, attr, state.getSema(), VectorType::NeonVector, "neon_vector_type"); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_neon_polyvector_type: + case AttributeList::AT_NeonPolyVectorType: HandleNeonVectorTypeAttr(type, attr, state.getSema(), VectorType::NeonPolyVector, "neon_polyvector_type"); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_opencl_image_access: + case AttributeList::AT_OpenCLImageAccess: HandleOpenCLImageAccessAttribute(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; - case AttributeList::AT_ns_returns_retained: + case AttributeList::AT_Win64: + case AttributeList::AT_Ptr32: + case AttributeList::AT_Ptr64: + // FIXME: don't ignore these + attr.setUsedAsTypeAttr(); + break; + + case AttributeList::AT_NSReturnsRetained: if (!state.getSema().getLangOpts().ObjCAutoRefCount) - break; + break; // fallthrough into the function attrs FUNCTION_TYPE_ATTRS_CASELIST: @@ -4043,14 +4170,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, /// case of a reference type, the referred-to type). /// /// \param E The expression whose type is required to be complete. -/// \param PD The partial diagnostic that will be printed out if the type cannot -/// be completed. +/// \param Diagnoser The object that will emit a diagnostic if the type is +/// incomplete. /// /// \returns \c true if the type of \p E is incomplete and diagnosed, \c false /// otherwise. -bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, - std::pair<SourceLocation, - PartialDiagnostic> Note) { +bool Sema::RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser){ QualType T = E->getType(); // Fast path the case where the type is already complete. @@ -4065,7 +4190,7 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) { if (Var->isStaticDataMember() && Var->getInstantiatedFromStaticDataMember()) { - + MemberSpecializationInfo *MSInfo = Var->getMemberSpecializationInfo(); assert(MSInfo && "Missing member specialization information?"); if (MSInfo->getTemplateSpecializationKind() @@ -4073,15 +4198,15 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, // If we don't already have a point of instantiation, this is it. if (MSInfo->getPointOfInstantiation().isInvalid()) { MSInfo->setPointOfInstantiation(E->getLocStart()); - - // This is a modification of an existing AST node. Notify + + // This is a modification of an existing AST node. Notify // listeners. if (ASTMutationListener *L = getASTMutationListener()) L->StaticDataMemberInstantiated(Var); } - + InstantiateStaticDataMemberDefinition(E->getExprLoc(), Var); - + // Update the type to the newly instantiated definition's type both // here and within the expression. if (VarDecl *Def = Var->getDefinition()) { @@ -4091,7 +4216,7 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, E->setType(T); } } - + // We still go on to try to complete the type independently, as it // may also require instantiations or diagnostics if it remains // incomplete. @@ -4107,7 +4232,26 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, if (const ReferenceType *Ref = T->getAs<ReferenceType>()) T = Ref->getPointeeType(); - return RequireCompleteType(E->getExprLoc(), T, PD, Note); + return RequireCompleteType(E->getExprLoc(), T, Diagnoser); +} + +namespace { + struct TypeDiagnoserDiag : Sema::TypeDiagnoser { + unsigned DiagID; + + TypeDiagnoserDiag(unsigned DiagID) + : Sema::TypeDiagnoser(DiagID == 0), DiagID(DiagID) {} + + virtual void diagnose(Sema &S, SourceLocation Loc, QualType T) { + if (Suppressed) return; + S.Diag(Loc, DiagID) << T; + } + }; +} + +bool Sema::RequireCompleteExprType(Expr *E, unsigned DiagID) { + TypeDiagnoserDiag Diagnoser(DiagID); + return RequireCompleteExprType(E, Diagnoser); } /// @brief Ensure that the type T is a complete type. @@ -4125,17 +4269,10 @@ bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, /// /// @param T The type that this routine is examining for completeness. /// -/// @param PD The partial diagnostic that will be printed out if T is not a -/// complete type. -/// /// @returns @c true if @p T is incomplete and a diagnostic was emitted, /// @c false otherwise. bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, - const PartialDiagnostic &PD, - std::pair<SourceLocation, - PartialDiagnostic> Note) { - unsigned diag = PD.getDiagID(); - + TypeDiagnoser &Diagnoser) { // FIXME: Add this assertion to make sure we always get instantiation points. // assert(!Loc.isInvalid() && "Invalid location in RequireCompleteType"); // FIXME: Add this assertion to help us flush out problems with @@ -4148,9 +4285,9 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, NamedDecl *Def = 0; if (!T->isIncompleteType(&Def)) { // If we know about the definition but it is not visible, complain. - if (diag != 0 && Def && !LookupResult::isVisible(Def)) { + if (!Diagnoser.Suppressed && Def && !LookupResult::isVisible(Def)) { // Suppress this error outside of a SFINAE context if we've already - // emitted the error once for this type. There's no usefulness in + // emitted the error once for this type. There's no usefulness in // repeating the diagnostic. // FIXME: Add a Fix-It that imports the corresponding module or includes // the header. @@ -4159,13 +4296,13 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, Diag(Def->getLocation(), diag::note_previous_definition); } } - + return false; } const TagType *Tag = T->getAs<TagType>(); const ObjCInterfaceType *IFace = 0; - + if (Tag) { // Avoid diagnosing invalid decls as incomplete. if (Tag->getDecl()->isInvalidDecl()) @@ -4182,7 +4319,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, // Avoid diagnosing invalid decls as incomplete. if (IFace->getDecl()->isInvalidDecl()) return true; - + // Give the external AST source a chance to complete the type. if (IFace->getDecl()->hasExternalLexicalStorage()) { Context.getExternalSource()->CompleteType(IFace->getDecl()); @@ -4190,7 +4327,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, return false; } } - + // If we have a class template specialization or a class member of a // class template specialization, or an array with known size of such, // try to instantiate it. @@ -4204,7 +4341,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared) return InstantiateClassTemplateSpecialization(Loc, ClassTemplateSpec, TSK_ImplicitInstantiation, - /*Complain=*/diag != 0); + /*Complain=*/!Diagnoser.Suppressed); } else if (CXXRecordDecl *Rec = dyn_cast<CXXRecordDecl>(Record->getDecl())) { CXXRecordDecl *Pattern = Rec->getInstantiatedFromMemberClass(); @@ -4216,21 +4353,17 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, return InstantiateClass(Loc, Rec, Pattern, getTemplateInstantiationArgs(Rec), TSK_ImplicitInstantiation, - /*Complain=*/diag != 0); + /*Complain=*/!Diagnoser.Suppressed); } } } - if (diag == 0) + if (Diagnoser.Suppressed) return true; - + // We have an incomplete type. Produce a diagnostic. - Diag(Loc, PD) << T; - - // If we have a note, produce it. - if (!Note.first.isInvalid()) - Diag(Note.first, Note.second); - + Diagnoser.diagnose(*this, Loc, T); + // If the type was a forward declaration of a class/struct/union // type, produce a note. if (Tag && !Tag->getDecl()->isInvalidDecl()) @@ -4238,7 +4371,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, Tag->isBeingDefined() ? diag::note_type_being_defined : diag::note_forward_declaration) << QualType(Tag, 0); - + // If the Objective-C class was a forward declaration, produce a note. if (IFace && !IFace->getDecl()->isInvalidDecl()) Diag(IFace->getDecl()->getLocation(), diag::note_forward_class); @@ -4247,15 +4380,9 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, } bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, - const PartialDiagnostic &PD) { - return RequireCompleteType(Loc, T, PD, - std::make_pair(SourceLocation(), PDiag(0))); -} - -bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, unsigned DiagID) { - return RequireCompleteType(Loc, T, PDiag(DiagID), - std::make_pair(SourceLocation(), PDiag(0))); + TypeDiagnoserDiag Diagnoser(DiagID); + return RequireCompleteType(Loc, T, Diagnoser); } /// @brief Ensure that the type T is a literal type. @@ -4272,13 +4399,12 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, /// /// @param T The type that this routine is examining for literalness. /// -/// @param PD The partial diagnostic that will be printed out if T is not a -/// literal type. +/// @param Diagnoser Emits a diagnostic if T is not a literal type. /// /// @returns @c true if @p T is not a literal type and a diagnostic was emitted, /// @c false otherwise. bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, - const PartialDiagnostic &PD) { + TypeDiagnoser &Diagnoser) { assert(!T->isDependentType() && "type should not be dependent"); QualType ElemType = Context.getBaseElementType(T); @@ -4287,10 +4413,10 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, if (T->isLiteralType()) return false; - if (PD.getDiagID() == 0) + if (Diagnoser.Suppressed) return true; - Diag(Loc, PD) << T; + Diagnoser.diagnose(*this, Loc, T); if (T->isVariableArrayType()) return true; @@ -4301,9 +4427,13 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); - // FIXME: Better diagnostic for incomplete class? - if (!RD->isCompleteDefinition()) + // A partially-defined class type can't be a literal type, because a literal + // class type must have a trivial destructor (which can't be checked until + // the class definition is complete). + if (!RD->isCompleteDefinition()) { + RequireCompleteType(Loc, ElemType, diag::note_non_literal_incomplete, T); return true; + } // If the class has virtual base classes, then it's not an aggregate, and // cannot have any constexpr constructors or a trivial default constructor, @@ -4331,11 +4461,11 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, } for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { - if (!(*I)->getType()->isLiteralType() || - (*I)->getType().isVolatileQualified()) { - Diag((*I)->getLocation(), diag::note_non_literal_field) - << RD << (*I) << (*I)->getType() - << (*I)->getType().isVolatileQualified(); + if (!I->getType()->isLiteralType() || + I->getType().isVolatileQualified()) { + Diag(I->getLocation(), diag::note_non_literal_field) + << RD << *I << I->getType() + << I->getType().isVolatileQualified(); return true; } } @@ -4355,6 +4485,11 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, return true; } +bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, unsigned DiagID) { + TypeDiagnoserDiag Diagnoser(DiagID); + return RequireLiteralType(Loc, T, Diagnoser); +} + /// \brief Retrieve a version of the type 'T' that is elaborated by Keyword /// and qualified by the nested-name-specifier contained in SS. QualType Sema::getElaboratedType(ElaboratedTypeKeyword Keyword, @@ -4396,8 +4531,8 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { // The type denoted by decltype(e) is defined as follows: // // - if e is an unparenthesized id-expression or an unparenthesized class - // member access (5.2.5), decltype(e) is the type of the entity named - // by e. If there is no such entity, or if e names a set of overloaded + // member access (5.2.5), decltype(e) is the type of the entity named + // by e. If there is no such entity, or if e names a set of overloaded // functions, the program is ill-formed; if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { if (const ValueDecl *VD = dyn_cast<ValueDecl>(DRE->getDecl())) @@ -4407,7 +4542,7 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { if (const FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) return FD->getType(); } - + // C++11 [expr.lambda.prim]p18: // Every occurrence of decltype((x)) where x is a possibly // parenthesized id-expression that names an entity of automatic @@ -4433,16 +4568,16 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { // [...] QualType T = E->getType(); switch (E->getValueKind()) { - // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the + // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the // type of e; case VK_XValue: T = S.Context.getRValueReferenceType(T); break; - // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the + // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the // type of e; case VK_LValue: T = S.Context.getLValueReferenceType(T); break; // - otherwise, decltype(e) is the type of e. case VK_RValue: break; } - + return T; } @@ -4450,7 +4585,7 @@ QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc) { ExprResult ER = CheckPlaceholderExpr(E); if (ER.isInvalid()) return QualType(); E = ER.take(); - + return Context.getDecltypeType(E, getDecltypeForExpr(*this, E)); } @@ -4482,8 +4617,7 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) { if (!T->isDependentType()) { // FIXME: It isn't entirely clear whether incomplete atomic types // are allowed or not; for simplicity, ban them for the moment. - if (RequireCompleteType(Loc, T, - PDiag(diag::err_atomic_specifier_bad_type) << 0)) + if (RequireCompleteType(Loc, T, diag::err_atomic_specifier_bad_type, 0)) return QualType(); int DisallowedKind = -1; diff --git a/lib/Sema/TargetAttributesSema.cpp b/lib/Sema/TargetAttributesSema.cpp index 8b19be74dd43..25ace950e077 100644 --- a/lib/Sema/TargetAttributesSema.cpp +++ b/lib/Sema/TargetAttributesSema.cpp @@ -151,6 +151,18 @@ static void HandleX86ForceAlignArgPointerAttr(Decl *D, S.Context)); } +DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range) { + if (D->hasAttr<DLLExportAttr>()) { + Diag(Range.getBegin(), diag::warn_attribute_ignored) << "dllimport"; + return NULL; + } + + if (D->hasAttr<DLLImportAttr>()) + return NULL; + + return ::new (Context) DLLImportAttr(Range, Context); +} + static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { // check the attribute arguments. if (Attr.getNumArgs() != 0) { @@ -159,13 +171,8 @@ static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { } // Attribute can be applied only to functions or variables. - if (isa<VarDecl>(D)) { - D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context)); - return; - } - FunctionDecl *FD = dyn_cast<FunctionDecl>(D); - if (!FD) { + if (!FD && !isa<VarDecl>(D)) { // Apparently Visual C++ thinks it is okay to not emit a warning // in this case, so only emit a warning when -fms-extensions is not // specified. @@ -177,27 +184,26 @@ static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { // Currently, the dllimport attribute is ignored for inlined functions. // Warning is emitted. - if (FD->isInlineSpecified()) { + if (FD && FD->isInlineSpecified()) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport"; return; } - // The attribute is also overridden by a subsequent declaration as dllexport. - // Warning is emitted. - for (AttributeList *nextAttr = Attr.getNext(); nextAttr; - nextAttr = nextAttr->getNext()) { - if (nextAttr->getKind() == AttributeList::AT_dllexport) { - S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport"; - return; - } - } + DLLImportAttr *NewAttr = S.mergeDLLImportAttr(D, Attr.getRange()); + if (NewAttr) + D->addAttr(NewAttr); +} - if (D->getAttr<DLLExportAttr>()) { - S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport"; - return; +DLLExportAttr *Sema::mergeDLLExportAttr(Decl *D, SourceRange Range) { + if (DLLImportAttr *Import = D->getAttr<DLLImportAttr>()) { + Diag(Import->getLocation(), diag::warn_attribute_ignored) << "dllimport"; + D->dropAttr<DLLImportAttr>(); } - D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context)); + if (D->hasAttr<DLLExportAttr>()) + return NULL; + + return ::new (Context) DLLExportAttr(Range, Context); } static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) { @@ -208,13 +214,8 @@ static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) { } // Attribute can be applied only to functions or variables. - if (isa<VarDecl>(D)) { - D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context)); - return; - } - FunctionDecl *FD = dyn_cast<FunctionDecl>(D); - if (!FD) { + if (!FD && !isa<VarDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << 2 /*variable and function*/; return; @@ -222,13 +223,15 @@ static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) { // Currently, the dllexport attribute is ignored for inlined functions, unless // the -fkeep-inline-functions flag has been used. Warning is emitted; - if (FD->isInlineSpecified()) { + if (FD && FD->isInlineSpecified()) { // FIXME: ... unless the -fkeep-inline-functions flag has been used. S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllexport"; return; } - D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context)); + DLLExportAttr *NewAttr = S.mergeDLLExportAttr(D, Attr.getRange()); + if (NewAttr) + D->addAttr(NewAttr); } namespace { @@ -241,9 +244,9 @@ namespace { if (Triple.getOS() == llvm::Triple::Win32 || Triple.getOS() == llvm::Triple::MinGW32) { switch (Attr.getKind()) { - case AttributeList::AT_dllimport: HandleDLLImportAttr(D, Attr, S); + case AttributeList::AT_DLLImport: HandleDLLImportAttr(D, Attr, S); return true; - case AttributeList::AT_dllexport: HandleDLLExportAttr(D, Attr, S); + case AttributeList::AT_DLLExport: HandleDLLExportAttr(D, Attr, S); return true; default: break; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index a66378e51783..90d584037817 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -98,25 +98,25 @@ class TreeTransform { class ForgetPartiallySubstitutedPackRAII { Derived &Self; TemplateArgument Old; - + public: ForgetPartiallySubstitutedPackRAII(Derived &Self) : Self(Self) { Old = Self.ForgetPartiallySubstitutedPack(); } - + ~ForgetPartiallySubstitutedPackRAII() { Self.RememberPartiallySubstitutedPack(Old); } }; - + protected: Sema &SemaRef; - + /// \brief The set of local declarations that have been transformed, for /// cases where we are forced to build new declarations within the transformer /// rather than in the subclass (e.g., lambda closure types). llvm::DenseMap<Decl *, Decl *> TransformedLocalDecls; - + public: /// \brief Initializes a new tree transformer. TreeTransform(Sema &SemaRef) : SemaRef(SemaRef) { } @@ -177,7 +177,7 @@ public: DeclarationName Entity) : Self(Self) { OldLocation = Self.getDerived().getBaseLocation(); OldEntity = Self.getDerived().getBaseEntity(); - + if (Location.isValid()) Self.getDerived().setBase(Location, Entity); } @@ -207,7 +207,7 @@ public: bool DropCallArgument(Expr *E) { return E->isDefaultArgument(); } - + /// \brief Determine whether we should expand a pack expansion with the /// given set of parameter packs into separate arguments by repeatedly /// transforming the pattern. @@ -221,12 +221,9 @@ public: /// \param PatternRange The source range that covers the entire pattern of /// the pack expansion. /// - /// \param Unexpanded The set of unexpanded parameter packs within the + /// \param Unexpanded The set of unexpanded parameter packs within the /// pattern. /// - /// \param NumUnexpanded The number of unexpanded parameter packs in - /// \p Unexpanded. - /// /// \param ShouldExpand Will be set to \c true if the transformer should /// expand the corresponding pack expansions into separate arguments. When /// set, \c NumExpansions must also be set. @@ -244,9 +241,9 @@ public: /// The callee must set this value when \c ShouldExpand is \c true; it may /// set this value in other cases. /// - /// \returns true if an error occurred (e.g., because the parameter packs - /// are to be instantiated with arguments of different lengths), false - /// otherwise. If false, \c ShouldExpand (and possibly \c NumExpansions) + /// \returns true if an error occurred (e.g., because the parameter packs + /// are to be instantiated with arguments of different lengths), false + /// otherwise. If false, \c ShouldExpand (and possibly \c NumExpansions) /// must be set. bool TryExpandParameterPacks(SourceLocation EllipsisLoc, SourceRange PatternRange, @@ -257,7 +254,7 @@ public: ShouldExpand = false; return false; } - + /// \brief "Forget" about the partially-substituted pack template argument, /// when performing an instantiation that must preserve the parameter pack /// use. @@ -266,18 +263,18 @@ public: TemplateArgument ForgetPartiallySubstitutedPack() { return TemplateArgument(); } - + /// \brief "Remember" the partially-substituted pack template argument /// after performing an instantiation that must preserve the parameter pack /// use. /// /// This routine is meant to be overridden by the template instantiator. void RememberPartiallySubstitutedPack(TemplateArgument Arg) { } - + /// \brief Note to the derived class when a function parameter pack is /// being expanded. void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { } - + /// \brief Transforms the given type into another type. /// /// By default, this routine transforms a type by creating a @@ -328,8 +325,8 @@ public: /// \brief Transform the given list of expressions. /// - /// This routine transforms a list of expressions by invoking - /// \c TransformExpr() for each subexpression. However, it also provides + /// This routine transforms a list of expressions by invoking + /// \c TransformExpr() for each subexpression. However, it also provides /// support for variadic templates by expanding any pack expansions (if the /// derived class permits such expansion) along the way. When pack expansions /// are present, the number of outputs may not equal the number of inputs. @@ -339,7 +336,7 @@ public: /// \param NumInputs The number of expressions in \c Inputs. /// /// \param IsCall If \c true, then this transform is being performed on - /// function-call arguments, and any arguments that should be dropped, will + /// function-call arguments, and any arguments that should be dropped, will /// be. /// /// \param Outputs The transformed input expressions will be added to this @@ -352,61 +349,61 @@ public: bool TransformExprs(Expr **Inputs, unsigned NumInputs, bool IsCall, SmallVectorImpl<Expr *> &Outputs, bool *ArgChanged = 0); - + /// \brief Transform the given declaration, which is referenced from a type /// or expression. /// /// By default, acts as the identity function on declarations, unless the /// transformer has had to transform the declaration itself. Subclasses /// may override this function to provide alternate behavior. - Decl *TransformDecl(SourceLocation Loc, Decl *D) { + Decl *TransformDecl(SourceLocation Loc, Decl *D) { llvm::DenseMap<Decl *, Decl *>::iterator Known = TransformedLocalDecls.find(D); if (Known != TransformedLocalDecls.end()) return Known->second; - - return D; + + return D; } - /// \brief Transform the attributes associated with the given declaration and + /// \brief Transform the attributes associated with the given declaration and /// place them on the new declaration. /// /// By default, this operation does nothing. Subclasses may override this /// behavior to transform attributes. void transformAttrs(Decl *Old, Decl *New) { } - + /// \brief Note that a local declaration has been transformed by this /// transformer. /// - /// Local declarations are typically transformed via a call to + /// Local declarations are typically transformed via a call to /// TransformDefinition. However, in some cases (e.g., lambda expressions), /// the transformer itself has to transform the declarations. This routine /// can be overridden by a subclass that keeps track of such mappings. void transformedLocalDecl(Decl *Old, Decl *New) { TransformedLocalDecls[Old] = New; } - + /// \brief Transform the definition of the given declaration. /// /// By default, invokes TransformDecl() to transform the declaration. /// Subclasses may override this function to provide alternate behavior. - Decl *TransformDefinition(SourceLocation Loc, Decl *D) { - return getDerived().TransformDecl(Loc, D); + Decl *TransformDefinition(SourceLocation Loc, Decl *D) { + return getDerived().TransformDecl(Loc, D); } /// \brief Transform the given declaration, which was the first part of a /// nested-name-specifier in a member access expression. /// - /// This specific declaration transformation only applies to the first + /// This specific declaration transformation only applies to the first /// identifier in a nested-name-specifier of a member access expression, e.g., /// the \c T in \c x->T::member /// /// By default, invokes TransformDecl() to transform the declaration. /// Subclasses may override this function to provide alternate behavior. - NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc) { - return cast_or_null<NamedDecl>(getDerived().TransformDecl(Loc, D)); + NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc) { + return cast_or_null<NamedDecl>(getDerived().TransformDecl(Loc, D)); } - + /// \brief Transform the given nested-name-specifier with source-location /// information. /// @@ -436,7 +433,7 @@ public: /// /// \param NameLoc The source location of the template name. /// - /// \param ObjectType If we're translating a template name within a member + /// \param ObjectType If we're translating a template name within a member /// access expression, this is the type of the object whose member template /// is being referenced. /// @@ -466,7 +463,7 @@ public: /// \brief Transform the given set of template arguments. /// - /// By default, this operation transforms all of the template arguments + /// By default, this operation transforms all of the template arguments /// in the input set using \c TransformTemplateArgument(), and appends /// the transformed arguments to the output list. /// @@ -490,9 +487,9 @@ public: /// \brief Transform the given set of template arguments. /// - /// By default, this operation transforms all of the template arguments + /// By default, this operation transforms all of the template arguments /// in the input set using \c TransformTemplateArgument(), and appends - /// the transformed arguments to the output list. + /// the transformed arguments to the output list. /// /// \param First An iterator to the first template argument. /// @@ -530,18 +527,18 @@ public: StmtResult TransformSEHHandler(Stmt *Handler); - QualType + QualType TransformTemplateSpecializationType(TypeLocBuilder &TLB, TemplateSpecializationTypeLoc TL, TemplateName Template); - QualType + QualType TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, DependentTemplateSpecializationTypeLoc TL, TemplateName Template, CXXScopeSpec &SS); - QualType + QualType TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, DependentTemplateSpecializationTypeLoc TL, NestedNameSpecifierLoc QualifierLoc); @@ -574,6 +571,9 @@ public: StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E); + /// \brief Transform the captures and body of a lambda expression. + ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator); + #define STMT(Node, Parent) \ StmtResult Transform##Node(Node *S); #define EXPR(Node, Parent) \ @@ -784,8 +784,8 @@ public: ElaboratedTypeKeyword Keyword, NestedNameSpecifierLoc QualifierLoc, QualType Named) { - return SemaRef.Context.getElaboratedType(Keyword, - QualifierLoc.getNestedNameSpecifier(), + return SemaRef.Context.getElaboratedType(Keyword, + QualifierLoc.getNestedNameSpecifier(), Named); } @@ -804,30 +804,30 @@ public: // TODO: avoid TemplateName abstraction CXXScopeSpec SS; SS.Adopt(QualifierLoc); - TemplateName InstName + TemplateName InstName = getDerived().RebuildTemplateName(SS, *Name, NameLoc, QualType(), 0); - + if (InstName.isNull()) return QualType(); - + // If it's still dependent, make a dependent specialization. if (InstName.getAsDependentTemplateName()) - return SemaRef.Context.getDependentTemplateSpecializationType(Keyword, - QualifierLoc.getNestedNameSpecifier(), - Name, + return SemaRef.Context.getDependentTemplateSpecializationType(Keyword, + QualifierLoc.getNestedNameSpecifier(), + Name, Args); - + // Otherwise, make an elaborated type wrapping a non-dependent // specialization. QualType T = getDerived().RebuildTemplateSpecializationType(InstName, NameLoc, Args); if (T.isNull()) return QualType(); - + if (Keyword == ETK_None && QualifierLoc.getNestedNameSpecifier() == 0) return T; - - return SemaRef.Context.getElaboratedType(Keyword, - QualifierLoc.getNestedNameSpecifier(), + + return SemaRef.Context.getElaboratedType(Keyword, + QualifierLoc.getNestedNameSpecifier(), T); } @@ -847,8 +847,8 @@ public: if (QualifierLoc.getNestedNameSpecifier()->isDependent()) { // If the name is still dependent, just build a new dependent name type. if (!SemaRef.computeDeclContext(SS)) - return SemaRef.Context.getDependentNameType(Keyword, - QualifierLoc.getNestedNameSpecifier(), + return SemaRef.Context.getDependentNameType(Keyword, + QualifierLoc.getNestedNameSpecifier(), Id); } @@ -875,15 +875,15 @@ public: case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: break; - + case LookupResult::Found: Tag = Result.getAsSingle<TagDecl>(); break; - + case LookupResult::FoundOverloaded: case LookupResult::FoundUnresolvedValue: llvm_unreachable("Tag lookup cannot find non-tags"); - + case LookupResult::Ambiguous: // Let the LookupResult structure handle ambiguities. return QualType(); @@ -925,8 +925,8 @@ public: // Build the elaborated-type-specifier type. QualType T = SemaRef.Context.getTypeDeclType(Tag); - return SemaRef.Context.getElaboratedType(Keyword, - QualifierLoc.getNestedNameSpecifier(), + return SemaRef.Context.getElaboratedType(Keyword, + QualifierLoc.getNestedNameSpecifier(), T); } @@ -934,7 +934,7 @@ public: /// /// By default, builds a new PackExpansionType type from the given pattern. /// Subclasses may override this routine to provide different behavior. - QualType RebuildPackExpansionType(QualType Pattern, + QualType RebuildPackExpansionType(QualType Pattern, SourceRange PatternRange, SourceLocation EllipsisLoc, llvm::Optional<unsigned> NumExpansions) { @@ -984,7 +984,7 @@ public: QualType ObjectType); /// \brief Build a new template name given a template template parameter pack - /// and the + /// and the /// /// By default, performs semantic analysis to determine whether the name can /// be resolved to a specific template, then builds the appropriate kind of @@ -1053,7 +1053,8 @@ public: /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. - StmtResult RebuildAttributedStmt(SourceLocation AttrLoc, const AttrVec &Attrs, + StmtResult RebuildAttributedStmt(SourceLocation AttrLoc, + ArrayRef<const Attr*> Attrs, Stmt *SubStmt) { return SemaRef.ActOnAttributedStmt(AttrLoc, Attrs, SubStmt); } @@ -1063,7 +1064,7 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildIfStmt(SourceLocation IfLoc, Sema::FullExprArg Cond, - VarDecl *CondVar, Stmt *Then, + VarDecl *CondVar, Stmt *Then, SourceLocation ElseLoc, Stmt *Else) { return getSema().ActOnIfStmt(IfLoc, Cond, CondVar, Then, ElseLoc, Else); } @@ -1074,7 +1075,7 @@ public: /// Subclasses may override this routine to provide different behavior. StmtResult RebuildSwitchStmtStart(SourceLocation SwitchLoc, Expr *Cond, VarDecl *CondVar) { - return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Cond, + return getSema().ActOnStartOfSwitchStmt(SwitchLoc, Cond, CondVar); } @@ -1112,10 +1113,10 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, - Stmt *Init, Sema::FullExprArg Cond, + Stmt *Init, Sema::FullExprArg Cond, VarDecl *CondVar, Sema::FullExprArg Inc, SourceLocation RParenLoc, Stmt *Body) { - return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond, + return getSema().ActOnForStmt(ForLoc, LParenLoc, Init, Cond, CondVar, Inc, RParenLoc, Body); } @@ -1173,13 +1174,24 @@ public: MultiExprArg Clobbers, SourceLocation RParenLoc, bool MSAsm) { - return getSema().ActOnAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs, + return getSema().ActOnAsmStmt(AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, Names, move(Constraints), Exprs, AsmString, Clobbers, RParenLoc, MSAsm); } - /// \brief Build a new Objective-C @try statement. + /// \brief Build a new MS style inline asm statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildMSAsmStmt(SourceLocation AsmLoc, + ArrayRef<Token> AsmToks, + ArrayRef<unsigned> LineEnds, + SourceLocation EndLoc) { + return getSema().ActOnMSAsmStmt(AsmLoc, AsmToks, LineEnds, EndLoc); + } + + /// \brief Build a new Objective-C \@try statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1202,8 +1214,8 @@ public: ExceptionDecl->getLocation(), ExceptionDecl->getIdentifier()); } - - /// \brief Build a new Objective-C @catch statement. + + /// \brief Build a new Objective-C \@catch statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1214,8 +1226,8 @@ public: return getSema().ActOnObjCAtCatchStmt(AtLoc, RParenLoc, Var, Body); } - - /// \brief Build a new Objective-C @finally statement. + + /// \brief Build a new Objective-C \@finally statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1223,8 +1235,8 @@ public: Stmt *Body) { return getSema().ActOnObjCAtFinallyStmt(AtLoc, Body); } - - /// \brief Build a new Objective-C @throw statement. + + /// \brief Build a new Objective-C \@throw statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1232,8 +1244,8 @@ public: Expr *Operand) { return getSema().BuildObjCAtThrowStmt(AtLoc, Operand); } - - /// \brief Rebuild the operand to an Objective-C @synchronized statement. + + /// \brief Rebuild the operand to an Objective-C \@synchronized statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1242,7 +1254,7 @@ public: return getSema().ActOnObjCAtSynchronizedOperand(atLoc, object); } - /// \brief Build a new Objective-C @synchronized statement. + /// \brief Build a new Objective-C \@synchronized statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1251,7 +1263,7 @@ public: return getSema().ActOnObjCAtSynchronizedStmt(AtLoc, Object, Body); } - /// \brief Build a new Objective-C @autoreleasepool statement. + /// \brief Build a new Objective-C \@autoreleasepool statement. /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. @@ -1260,16 +1272,6 @@ public: return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body); } - /// \brief Build the collection operand to a new Objective-C fast - /// enumeration statement. - /// - /// By default, performs semantic analysis to build the new statement. - /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildObjCForCollectionOperand(SourceLocation forLoc, - Expr *collection) { - return getSema().ActOnObjCForCollectionOperand(forLoc, collection); - } - /// \brief Build a new Objective-C fast enumeration statement. /// /// By default, performs semantic analysis to build the new statement. @@ -1280,13 +1282,16 @@ public: Expr *Collection, SourceLocation RParenLoc, Stmt *Body) { - return getSema().ActOnObjCForCollectionStmt(ForLoc, LParenLoc, - Element, + StmtResult ForEachStmt = getSema().ActOnObjCForCollectionStmt(ForLoc, LParenLoc, + Element, Collection, - RParenLoc, - Body); + RParenLoc); + if (ForEachStmt.isInvalid()) + return StmtError(); + + return getSema().FinishObjCForCollectionStmt(ForEachStmt.take(), Body); } - + /// \brief Build a new C++ exception declaration. /// /// By default, performs semantic analysis to build the new decaration. @@ -1342,7 +1347,7 @@ public: /// /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. - StmtResult RebuildMSDependentExistsStmt(SourceLocation KeywordLoc, + StmtResult RebuildMSDependentExistsStmt(SourceLocation KeywordLoc, bool IsIfExists, NestedNameSpecifierLoc QualifierLoc, DeclarationNameInfo NameInfo, @@ -1358,7 +1363,7 @@ public: StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body) { return getSema().FinishCXXForRangeStmt(ForRange, Body); } - + StmtResult RebuildSEHTryStmt(bool IsCXXTry, SourceLocation TryLoc, Stmt *TryBlock, @@ -1448,8 +1453,8 @@ public: return getSema().BuildBuiltinOffsetOf(OperatorLoc, Type, Components, NumComponents, RParenLoc); } - - /// \brief Build a new sizeof, alignof or vec_step expression with a + + /// \brief Build a new sizeof, alignof or vec_step expression with a /// type argument. /// /// By default, performs semantic analysis to build the new expression. @@ -1637,7 +1642,7 @@ public: = SemaRef.ActOnInitList(LBraceLoc, move(Inits), RBraceLoc); if (Result.isInvalid() || ResultTy->isDependentType()) return move(Result); - + // Patch in the result type we were given, which may have been computed // when the initial InitListExpr was built. InitListExpr *ILE = cast<InitListExpr>((Expr *)Result.get()); @@ -1887,7 +1892,7 @@ public: SourceLocation TypeidLoc, TypeSourceInfo *Operand, SourceLocation RParenLoc) { - return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, Operand, + return getSema().BuildCXXTypeId(TypeInfoType, TypeidLoc, Operand, RParenLoc); } @@ -1912,7 +1917,7 @@ public: SourceLocation TypeidLoc, TypeSourceInfo *Operand, SourceLocation RParenLoc) { - return getSema().BuildCXXUuidof(TypeInfoType, TypeidLoc, Operand, + return getSema().BuildCXXUuidof(TypeInfoType, TypeidLoc, Operand, RParenLoc); } @@ -1956,7 +1961,7 @@ public: /// By default, builds a new default-argument expression, which does not /// require any semantic analysis. Subclasses may override this routine to /// provide different behavior. - ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, + ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) { return getSema().Owned(CXXDefaultArgExpr::Create(getSema().Context, Loc, Param)); @@ -2046,7 +2051,7 @@ public: SourceLocation RParenLoc) { return getSema().BuildTypeTrait(Trait, StartLoc, Args, RParenLoc); } - + /// \brief Build a new array type trait expression. /// /// By default, performs semantic analysis to build the new expression. @@ -2117,10 +2122,10 @@ public: CXXConstructExpr::ConstructionKind ConstructKind, SourceRange ParenRange) { ASTOwningVector<Expr*> ConvertedArgs(SemaRef); - if (getSema().CompleteConstructorCall(Constructor, move(Args), Loc, + if (getSema().CompleteConstructorCall(Constructor, move(Args), Loc, ConvertedArgs)) return ExprError(); - + return getSema().BuildCXXConstructExpr(Loc, T, Constructor, IsElidable, move_arg(ConvertedArgs), HadMultipleCandidates, @@ -2211,31 +2216,39 @@ public: } /// \brief Build a new expression to compute the length of a parameter pack. - ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack, - SourceLocation PackLoc, + ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack, + SourceLocation PackLoc, SourceLocation RParenLoc, llvm::Optional<unsigned> Length) { if (Length) - return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(), - OperatorLoc, Pack, PackLoc, + return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(), + OperatorLoc, Pack, PackLoc, RParenLoc, *Length); - - return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(), - OperatorLoc, Pack, PackLoc, + + return new (SemaRef.Context) SizeOfPackExpr(SemaRef.Context.getSizeType(), + OperatorLoc, Pack, PackLoc, RParenLoc); } + /// \brief Build a new Objective-C boxed expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { + return getSema().BuildObjCBoxedExpr(SR, ValueExpr); + } + /// \brief Build a new Objective-C array literal. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildObjCArrayLiteral(SourceRange Range, Expr **Elements, unsigned NumElements) { - return getSema().BuildObjCArrayLiteral(Range, + return getSema().BuildObjCArrayLiteral(Range, MultiExprArg(Elements, NumElements)); } - - ExprResult RebuildObjCSubscriptRefExpr(SourceLocation RB, + + ExprResult RebuildObjCSubscriptRefExpr(SourceLocation RB, Expr *Base, Expr *Key, ObjCMethodDecl *getterMethod, ObjCMethodDecl *setterMethod) { @@ -2252,8 +2265,8 @@ public: unsigned NumElements) { return getSema().BuildObjCDictionaryLiteral(Range, Elements, NumElements); } - - /// \brief Build a new Objective-C @encode expression. + + /// \brief Build a new Objective-C \@encode expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -2269,7 +2282,7 @@ public: Selector Sel, ArrayRef<SourceLocation> SelectorLocs, ObjCMethodDecl *Method, - SourceLocation LBracLoc, + SourceLocation LBracLoc, MultiExprArg Args, SourceLocation RBracLoc) { return SemaRef.BuildClassMessage(ReceiverTypeInfo, @@ -2284,7 +2297,7 @@ public: Selector Sel, ArrayRef<SourceLocation> SelectorLocs, ObjCMethodDecl *Method, - SourceLocation LBracLoc, + SourceLocation LBracLoc, MultiExprArg Args, SourceLocation RBracLoc) { return SemaRef.BuildInstanceMessage(Receiver, @@ -2312,15 +2325,15 @@ public: false); if (Result.isInvalid() || Base.isInvalid()) return ExprError(); - + if (Result.get()) return move(Result); - + return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(), /*FIXME:*/IvarLoc, IsArrow, SS, SourceLocation(), /*FirstQualifierInScope=*/0, - R, + R, /*TemplateArgs=*/0); } @@ -2328,7 +2341,7 @@ public: /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildObjCPropertyRefExpr(Expr *BaseArg, + ExprResult RebuildObjCPropertyRefExpr(Expr *BaseArg, ObjCPropertyDecl *Property, SourceLocation PropertyLoc) { CXXScopeSpec SS; @@ -2341,18 +2354,18 @@ public: SS, 0, false); if (Result.isInvalid() || Base.isInvalid()) return ExprError(); - + if (Result.get()) return move(Result); - + return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(), - /*FIXME:*/PropertyLoc, IsArrow, + /*FIXME:*/PropertyLoc, IsArrow, SS, SourceLocation(), /*FirstQualifierInScope=*/0, - R, + R, /*TemplateArgs=*/0); } - + /// \brief Build a new Objective-C property reference expression. /// /// By default, performs semantic analysis to build the new expression. @@ -2384,18 +2397,18 @@ public: SS, 0, false); if (Result.isInvalid() || Base.isInvalid()) return ExprError(); - + if (Result.get()) return move(Result); - + return getSema().BuildMemberReferenceExpr(Base.get(), Base.get()->getType(), /*FIXME:*/IsaLoc, IsArrow, SS, SourceLocation(), /*FirstQualifierInScope=*/0, - R, + R, /*TemplateArgs=*/0); } - + /// \brief Build a new shuffle vector expression. /// /// By default, performs semantic analysis to build the new expression. @@ -2437,7 +2450,7 @@ public: /// \brief Build a new template argument pack expansion. /// /// By default, performs semantic analysis to build a new pack expansion - /// for a template argument. Subclasses may override this routine to provide + /// for a template argument. Subclasses may override this routine to provide /// different behavior. TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern, SourceLocation EllipsisLoc, @@ -2449,10 +2462,10 @@ public: EllipsisLoc, NumExpansions); if (Result.isInvalid()) return TemplateArgumentLoc(); - + return TemplateArgumentLoc(Result.get(), Result.get()); } - + case TemplateArgument::Template: return TemplateArgumentLoc(TemplateArgument( Pattern.getArgument().getAsTemplate(), @@ -2460,16 +2473,16 @@ public: Pattern.getTemplateQualifierLoc(), Pattern.getTemplateNameLoc(), EllipsisLoc); - + case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: llvm_unreachable("Pack expansion pattern has no parameter packs"); - + case TemplateArgument::Type: - if (TypeSourceInfo *Expansion + if (TypeSourceInfo *Expansion = getSema().CheckPackExpansion(Pattern.getTypeSourceInfo(), EllipsisLoc, NumExpansions)) @@ -2477,14 +2490,14 @@ public: Expansion); break; } - + return TemplateArgumentLoc(); } - + /// \brief Build a new expression pack expansion. /// /// By default, performs semantic analysis to build a new pack expansion - /// for an expression. Subclasses may override this routine to provide + /// for an expression. Subclasses may override this routine to provide /// different behavior. ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc, llvm::Optional<unsigned> NumExpansions) { @@ -2573,8 +2586,8 @@ ExprResult TreeTransform<Derived>::TransformExpr(Expr *E) { } template<typename Derived> -bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, - unsigned NumInputs, +bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, + unsigned NumInputs, bool IsCall, SmallVectorImpl<Expr *> &Outputs, bool *ArgChanged) { @@ -2583,17 +2596,17 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, if (IsCall && getDerived().DropCallArgument(Inputs[I])) { if (ArgChanged) *ArgChanged = true; - + break; } - + if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(Inputs[I])) { Expr *Pattern = Expansion->getPattern(); - + SmallVector<UnexpandedParameterPack, 2> Unexpanded; getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - + // Determine whether the set of unexpanded parameter packs can and should // be expanded. bool Expand = true; @@ -2607,22 +2620,22 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, Expand, RetainExpansion, NumExpansions)) return true; - + if (!Expand) { // The transform has determined that we should perform a simple - // transformation on the pack expansion, producing another pack + // transformation on the pack expansion, producing another pack // expansion. Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); ExprResult OutPattern = getDerived().TransformExpr(Pattern); if (OutPattern.isInvalid()) return true; - - ExprResult Out = getDerived().RebuildPackExpansion(OutPattern.get(), + + ExprResult Out = getDerived().RebuildPackExpansion(OutPattern.get(), Expansion->getEllipsisLoc(), NumExpansions); if (Out.isInvalid()) return true; - + if (ArgChanged) *ArgChanged = true; Outputs.push_back(Out.get()); @@ -2632,7 +2645,7 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, // Record right away that the argument was changed. This needs // to happen even if the array expands to nothing. if (ArgChanged) *ArgChanged = true; - + // The transform has determined that we should perform an elementwise // expansion of the pattern. Do so. for (unsigned I = 0; I != *NumExpansions; ++I) { @@ -2647,23 +2660,23 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, if (Out.isInvalid()) return true; } - + Outputs.push_back(Out.get()); } - + continue; } - + ExprResult Result = getDerived().TransformExpr(Inputs[I]); if (Result.isInvalid()) return true; - + if (Result.get() != Inputs[I] && ArgChanged) *ArgChanged = true; - - Outputs.push_back(Result.get()); + + Outputs.push_back(Result.get()); } - + return false; } @@ -2674,7 +2687,7 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc( QualType ObjectType, NamedDecl *FirstQualifierInScope) { SmallVector<NestedNameSpecifierLoc, 4> Qualifiers; - for (NestedNameSpecifierLoc Qualifier = NNS; Qualifier; + for (NestedNameSpecifierLoc Qualifier = NNS; Qualifier; Qualifier = Qualifier.getPrefix()) Qualifiers.push_back(Qualifier); @@ -2682,19 +2695,19 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc( while (!Qualifiers.empty()) { NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier(); - + switch (QNNS->getKind()) { case NestedNameSpecifier::Identifier: - if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/0, + if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/0, *QNNS->getAsIdentifier(), - Q.getLocalBeginLoc(), + Q.getLocalBeginLoc(), Q.getLocalEndLoc(), - ObjectType, false, SS, + ObjectType, false, SS, FirstQualifierInScope, false)) return NestedNameSpecifierLoc(); - + break; - + case NestedNameSpecifier::Namespace: { NamespaceDecl *NS = cast_or_null<NamespaceDecl>( @@ -2704,35 +2717,35 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc( SS.Extend(SemaRef.Context, NS, Q.getLocalBeginLoc(), Q.getLocalEndLoc()); break; } - + case NestedNameSpecifier::NamespaceAlias: { NamespaceAliasDecl *Alias = cast_or_null<NamespaceAliasDecl>( getDerived().TransformDecl(Q.getLocalBeginLoc(), QNNS->getAsNamespaceAlias())); - SS.Extend(SemaRef.Context, Alias, Q.getLocalBeginLoc(), + SS.Extend(SemaRef.Context, Alias, Q.getLocalBeginLoc(), Q.getLocalEndLoc()); break; } - + case NestedNameSpecifier::Global: // There is no meaningful transformation that one could perform on the // global scope. SS.MakeGlobal(SemaRef.Context, Q.getBeginLoc()); break; - + case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: { TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, FirstQualifierInScope, SS); - + if (!TL) return NestedNameSpecifierLoc(); - + if (TL.getType()->isDependentType() || TL.getType()->isRecordType() || - (SemaRef.getLangOpts().CPlusPlus0x && + (SemaRef.getLangOpts().CPlusPlus0x && TL.getType()->isEnumeralType())) { - assert(!TL.getType().hasLocalQualifiers() && + assert(!TL.getType().hasLocalQualifiers() && "Can't get cv-qualifiers here"); if (TL.getType()->isEnumeralType()) SemaRef.Diag(TL.getBeginLoc(), @@ -2745,24 +2758,24 @@ TreeTransform<Derived>::TransformNestedNameSpecifierLoc( // error because a previous error should have already been emitted. TypedefTypeLoc* TTL = dyn_cast<TypedefTypeLoc>(&TL); if (!TTL || !TTL->getTypedefNameDecl()->isInvalidDecl()) { - SemaRef.Diag(TL.getBeginLoc(), diag::err_nested_name_spec_non_tag) + SemaRef.Diag(TL.getBeginLoc(), diag::err_nested_name_spec_non_tag) << TL.getType() << SS.getRange(); } return NestedNameSpecifierLoc(); } } - + // The qualifier-in-scope and object type only apply to the leftmost entity. FirstQualifierInScope = 0; ObjectType = QualType(); } - + // Don't rebuild the nested-name-specifier if we don't have to. - if (SS.getScopeRep() == NNS.getNestedNameSpecifier() && + if (SS.getScopeRep() == NNS.getNestedNameSpecifier() && !getDerived().AlwaysRebuild()) return NNS; - - // If we can re-use the source-location data from the original + + // If we can re-use the source-location data from the original // nested-name-specifier, do so. if (SS.location_size() == NNS.getDataLength() && memcmp(SS.location_data(), NNS.getOpaqueData(), SS.location_size()) == 0) @@ -2833,60 +2846,60 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS, if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) { TemplateDecl *Template = QTN->getTemplateDecl(); assert(Template && "qualified template name must refer to a template"); - + TemplateDecl *TransTemplate - = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc, + = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc, Template)); if (!TransTemplate) return TemplateName(); - + if (!getDerived().AlwaysRebuild() && SS.getScopeRep() == QTN->getQualifier() && TransTemplate == Template) return Name; - + return getDerived().RebuildTemplateName(SS, QTN->hasTemplateKeyword(), TransTemplate); } - + if (DependentTemplateName *DTN = Name.getAsDependentTemplateName()) { if (SS.getScopeRep()) { // These apply to the scope specifier, not the template. ObjectType = QualType(); FirstQualifierInScope = 0; - } - + } + if (!getDerived().AlwaysRebuild() && SS.getScopeRep() == DTN->getQualifier() && ObjectType.isNull()) return Name; - + if (DTN->isIdentifier()) { return getDerived().RebuildTemplateName(SS, - *DTN->getIdentifier(), + *DTN->getIdentifier(), NameLoc, ObjectType, FirstQualifierInScope); } - + return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc, ObjectType); } - + if (TemplateDecl *Template = Name.getAsTemplateDecl()) { TemplateDecl *TransTemplate - = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc, + = cast_or_null<TemplateDecl>(getDerived().TransformDecl(NameLoc, Template)); if (!TransTemplate) return TemplateName(); - + if (!getDerived().AlwaysRebuild() && TransTemplate == Template) return Name; - + return TemplateName(TransTemplate); } - + if (SubstTemplateTemplateParmPackStorage *SubstPack = Name.getAsSubstTemplateTemplateParmPack()) { TemplateTemplateParmDecl *TransParam @@ -2894,15 +2907,15 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS, getDerived().TransformDecl(NameLoc, SubstPack->getParameterPack())); if (!TransParam) return TemplateName(); - + if (!getDerived().AlwaysRebuild() && TransParam == SubstPack->getParameterPack()) return Name; - - return getDerived().RebuildTemplateName(TransParam, + + return getDerived().RebuildTemplateName(TransParam, SubstPack->getArgumentPack()); } - + // These should be getting filtered out before they reach the AST. llvm_unreachable("overloaded function decl survived to here"); } @@ -2920,7 +2933,7 @@ void TreeTransform<Derived>::InventTemplateArgumentLoc( case TemplateArgument::Type: Output = TemplateArgumentLoc(Arg, SemaRef.Context.getTrivialTypeSourceInfo(Arg.getAsType(), Loc)); - + break; case TemplateArgument::Template: @@ -2931,16 +2944,16 @@ void TreeTransform<Derived>::InventTemplateArgumentLoc( Builder.MakeTrivial(SemaRef.Context, DTN->getQualifier(), Loc); else if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) Builder.MakeTrivial(SemaRef.Context, QTN->getQualifier(), Loc); - + if (Arg.getKind() == TemplateArgument::Template) - Output = TemplateArgumentLoc(Arg, + Output = TemplateArgumentLoc(Arg, Builder.getWithLocInContext(SemaRef.Context), Loc); else - Output = TemplateArgumentLoc(Arg, + Output = TemplateArgumentLoc(Arg, Builder.getWithLocInContext(SemaRef.Context), Loc, Loc); - + break; } @@ -3008,7 +3021,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument( if (!QualifierLoc) return true; } - + CXXScopeSpec SS; SS.Adopt(QualifierLoc); TemplateName Template @@ -3016,7 +3029,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument( Input.getTemplateNameLoc()); if (Template.isNull()) return true; - + Output = TemplateArgumentLoc(TemplateArgument(Template), QualifierLoc, Input.getTemplateNameLoc()); return false; @@ -3063,8 +3076,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument( = new (getSema().Context) TemplateArgument[TransformedArgs.size()]; std::copy(TransformedArgs.begin(), TransformedArgs.end(), TransformedArgsPtr); - Output = TemplateArgumentLoc(TemplateArgument(TransformedArgsPtr, - TransformedArgs.size()), + Output = TemplateArgumentLoc(TemplateArgument(TransformedArgsPtr, + TransformedArgs.size()), Input.getLocInfo()); return false; } @@ -3080,48 +3093,48 @@ template<typename Derived, typename InputIterator> class TemplateArgumentLocInventIterator { TreeTransform<Derived> &Self; InputIterator Iter; - + public: typedef TemplateArgumentLoc value_type; typedef TemplateArgumentLoc reference; typedef typename std::iterator_traits<InputIterator>::difference_type difference_type; typedef std::input_iterator_tag iterator_category; - + class pointer { TemplateArgumentLoc Arg; - + public: explicit pointer(TemplateArgumentLoc Arg) : Arg(Arg) { } - + const TemplateArgumentLoc *operator->() const { return &Arg; } }; - + TemplateArgumentLocInventIterator() { } - + explicit TemplateArgumentLocInventIterator(TreeTransform<Derived> &Self, InputIterator Iter) : Self(Self), Iter(Iter) { } - + TemplateArgumentLocInventIterator &operator++() { ++Iter; return *this; } - + TemplateArgumentLocInventIterator operator++(int) { TemplateArgumentLocInventIterator Old(*this); ++(*this); return Old; } - + reference operator*() const { TemplateArgumentLoc Result; Self.InventTemplateArgumentLoc(*Iter, Result); return Result; } - + pointer operator->() const { return pointer(**this); } - + friend bool operator==(const TemplateArgumentLocInventIterator &X, const TemplateArgumentLocInventIterator &Y) { return X.Iter == Y.Iter; @@ -3132,7 +3145,7 @@ public: return X.Iter != Y.Iter; } }; - + template<typename Derived> template<typename InputIterator> bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First, @@ -3141,39 +3154,39 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First, for (; First != Last; ++First) { TemplateArgumentLoc Out; TemplateArgumentLoc In = *First; - + if (In.getArgument().getKind() == TemplateArgument::Pack) { // Unpack argument packs, which we translate them into separate // arguments. // FIXME: We could do much better if we could guarantee that the // TemplateArgumentLocInfo for the pack expansion would be usable for // all of the template arguments in the argument pack. - typedef TemplateArgumentLocInventIterator<Derived, + typedef TemplateArgumentLocInventIterator<Derived, TemplateArgument::pack_iterator> PackLocIterator; - if (TransformTemplateArguments(PackLocIterator(*this, + if (TransformTemplateArguments(PackLocIterator(*this, In.getArgument().pack_begin()), PackLocIterator(*this, In.getArgument().pack_end()), Outputs)) return true; - + continue; } - + if (In.getArgument().isPackExpansion()) { // We have a pack expansion, for which we will be substituting into // the pattern. SourceLocation Ellipsis; llvm::Optional<unsigned> OrigNumExpansions; TemplateArgumentLoc Pattern - = In.getPackExpansionPattern(Ellipsis, OrigNumExpansions, + = In.getPackExpansionPattern(Ellipsis, OrigNumExpansions, getSema().Context); - + SmallVector<UnexpandedParameterPack, 2> Unexpanded; getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - + // Determine whether the set of unexpanded parameter packs can and should // be expanded. bool Expand = true; @@ -3182,29 +3195,29 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First, if (getDerived().TryExpandParameterPacks(Ellipsis, Pattern.getSourceRange(), Unexpanded, - Expand, + Expand, RetainExpansion, NumExpansions)) return true; - + if (!Expand) { // The transform has determined that we should perform a simple - // transformation on the pack expansion, producing another pack + // transformation on the pack expansion, producing another pack // expansion. TemplateArgumentLoc OutPattern; Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); if (getDerived().TransformTemplateArgument(Pattern, OutPattern)) return true; - + Out = getDerived().RebuildPackExpansion(OutPattern, Ellipsis, NumExpansions); if (Out.getArgument().isNull()) return true; - + Outputs.addArgument(Out); continue; } - + // The transform has determined that we should perform an elementwise // expansion of the pattern. Do so. for (unsigned I = 0; I != *NumExpansions; ++I) { @@ -3212,43 +3225,43 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First, if (getDerived().TransformTemplateArgument(Pattern, Out)) return true; - + if (Out.getArgument().containsUnexpandedParameterPack()) { Out = getDerived().RebuildPackExpansion(Out, Ellipsis, OrigNumExpansions); if (Out.getArgument().isNull()) return true; } - + Outputs.addArgument(Out); } - + // If we're supposed to retain a pack expansion, do so by temporarily // forgetting the partially-substituted parameter pack. if (RetainExpansion) { ForgetPartiallySubstitutedPackRAII Forget(getDerived()); - + if (getDerived().TransformTemplateArgument(Pattern, Out)) return true; - + Out = getDerived().RebuildPackExpansion(Out, Ellipsis, OrigNumExpansions); if (Out.getArgument().isNull()) return true; - + Outputs.addArgument(Out); } - + continue; } - - // The simple case: + + // The simple case: if (getDerived().TransformTemplateArgument(In, Out)) return true; - + Outputs.addArgument(Out); } - + return false; } @@ -3266,7 +3279,7 @@ QualType TreeTransform<Derived>::TransformType(QualType T) { // eventually turn into transformations on TypeLocs. TypeSourceInfo *DI = getSema().Context.getTrivialTypeSourceInfo(T, getDerived().getBaseLocation()); - + TypeSourceInfo *NewDI = getDerived().TransformType(DI); if (!NewDI) @@ -3336,19 +3349,19 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB, if (!Result->isObjCLifetimeType() && !Result->isDependentType()) Quals.removeObjCLifetime(); else if (Result.getObjCLifetime()) { - // Objective-C ARC: + // Objective-C ARC: // A lifetime qualifier applied to a substituted template parameter // overrides the lifetime qualifier from the template argument. - if (const SubstTemplateTypeParmType *SubstTypeParam + if (const SubstTemplateTypeParmType *SubstTypeParam = dyn_cast<SubstTemplateTypeParmType>(Result)) { QualType Replacement = SubstTypeParam->getReplacementType(); Qualifiers Qs = Replacement.getQualifiers(); Qs.removeObjCLifetime(); - Replacement + Replacement = SemaRef.Context.getQualifiedType(Replacement.getUnqualifiedType(), Qs); Result = SemaRef.Context.getSubstTemplateTypeParmType( - SubstTypeParam->getReplacedParameter(), + SubstTypeParam->getReplacedParameter(), Replacement); TLB.TypeWasModifiedSafely(Result); } else { @@ -3357,7 +3370,7 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB, SourceRange R = TLB.getTemporaryTypeLoc(Result).getSourceRange(); SemaRef.Diag(R.getBegin(), diag::err_attr_objc_ownership_redundant) << Result << R; - + Quals.removeObjCLifetime(); } } @@ -3380,37 +3393,37 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL, QualType T = TL.getType(); if (getDerived().AlreadyTransformed(T)) return TL; - + TypeLocBuilder TLB; QualType Result; - + if (isa<TemplateSpecializationType>(T)) { TemplateSpecializationTypeLoc SpecTL = cast<TemplateSpecializationTypeLoc>(TL); - + TemplateName Template = getDerived().TransformTemplateName(SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(), ObjectType, UnqualLookup); - if (Template.isNull()) + if (Template.isNull()) return TypeLoc(); - - Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL, + + Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL, Template); } else if (isa<DependentTemplateSpecializationType>(T)) { DependentTemplateSpecializationTypeLoc SpecTL = cast<DependentTemplateSpecializationTypeLoc>(TL); - + TemplateName Template - = getDerived().RebuildTemplateName(SS, - *SpecTL.getTypePtr()->getIdentifier(), + = getDerived().RebuildTemplateName(SS, + *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(), ObjectType, UnqualLookup); if (Template.isNull()) return TypeLoc(); - - Result = getDerived().TransformDependentTemplateSpecializationType(TLB, + + Result = getDerived().TransformDependentTemplateSpecializationType(TLB, SpecTL, Template, SS); @@ -3418,10 +3431,10 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL, // Nothing special needs to be done for these. Result = getDerived().TransformType(TLB, TL); } - - if (Result.isNull()) + + if (Result.isNull()) return TypeLoc(); - + return TLB.getTypeSourceInfo(SemaRef.Context, Result)->getTypeLoc(); } @@ -3432,42 +3445,42 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeSourceInfo *TSInfo, NamedDecl *UnqualLookup, CXXScopeSpec &SS) { // FIXME: Painfully copy-paste from the above! - + QualType T = TSInfo->getType(); if (getDerived().AlreadyTransformed(T)) return TSInfo; - + TypeLocBuilder TLB; QualType Result; - + TypeLoc TL = TSInfo->getTypeLoc(); if (isa<TemplateSpecializationType>(T)) { TemplateSpecializationTypeLoc SpecTL = cast<TemplateSpecializationTypeLoc>(TL); - + TemplateName Template = getDerived().TransformTemplateName(SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(), ObjectType, UnqualLookup); - if (Template.isNull()) + if (Template.isNull()) return 0; - - Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL, + + Result = getDerived().TransformTemplateSpecializationType(TLB, SpecTL, Template); } else if (isa<DependentTemplateSpecializationType>(T)) { DependentTemplateSpecializationTypeLoc SpecTL = cast<DependentTemplateSpecializationTypeLoc>(TL); - + TemplateName Template - = getDerived().RebuildTemplateName(SS, - *SpecTL.getTypePtr()->getIdentifier(), + = getDerived().RebuildTemplateName(SS, + *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(), ObjectType, UnqualLookup); if (Template.isNull()) return 0; - - Result = getDerived().TransformDependentTemplateSpecializationType(TLB, + + Result = getDerived().TransformDependentTemplateSpecializationType(TLB, SpecTL, Template, SS); @@ -3475,10 +3488,10 @@ TreeTransform<Derived>::TransformTypeInObjectScope(TypeSourceInfo *TSInfo, // Nothing special needs to be done for these. Result = getDerived().TransformType(TLB, TL); } - - if (Result.isNull()) + + if (Result.isNull()) return 0; - + return TLB.getTypeSourceInfo(SemaRef.Context, Result); } @@ -3509,8 +3522,8 @@ QualType TreeTransform<Derived>::TransformComplexType(TypeLocBuilder &TLB, template<typename Derived> QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL) { - QualType PointeeType - = getDerived().TransformType(TLB, TL.getPointeeLoc()); + QualType PointeeType + = getDerived().TransformType(TLB, TL.getPointeeLoc()); if (PointeeType.isNull()) return QualType(); @@ -3521,7 +3534,7 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, // resulting pointer type is an ObjCObjectPointerType, not a // PointerType. Result = SemaRef.Context.getObjCObjectPointerType(PointeeType); - + ObjCObjectPointerTypeLoc NewT = TLB.push<ObjCObjectPointerTypeLoc>(Result); NewT.setStarLoc(TL.getStarLoc()); return Result; @@ -3533,14 +3546,14 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + // Objective-C ARC can add lifetime qualifiers to the type that we're // pointing to. TLB.TypeWasModifiedSafely(Result->getPointeeType()); - + PointerTypeLoc NewT = TLB.push<PointerTypeLoc>(Result); NewT.setSigilLoc(TL.getSigilLoc()); - return Result; + return Result; } template<typename Derived> @@ -3548,14 +3561,14 @@ QualType TreeTransform<Derived>::TransformBlockPointerType(TypeLocBuilder &TLB, BlockPointerTypeLoc TL) { QualType PointeeType - = getDerived().TransformType(TLB, TL.getPointeeLoc()); - if (PointeeType.isNull()) - return QualType(); - - QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || - PointeeType != TL.getPointeeLoc().getType()) { - Result = getDerived().RebuildBlockPointerType(PointeeType, + = getDerived().TransformType(TLB, TL.getPointeeLoc()); + if (PointeeType.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || + PointeeType != TL.getPointeeLoc().getType()) { + Result = getDerived().RebuildBlockPointerType(PointeeType, TL.getSigilLoc()); if (Result.isNull()) return QualType(); @@ -3725,7 +3738,7 @@ QualType TreeTransform<Derived>::TransformIncompleteArrayType( if (Result.isNull()) return QualType(); } - + IncompleteArrayTypeLoc NewTL = TLB.push<IncompleteArrayTypeLoc>(Result); NewTL.setLBracketLoc(TL.getLBracketLoc()); NewTL.setRBracketLoc(TL.getRBracketLoc()); @@ -3762,7 +3775,7 @@ TreeTransform<Derived>::TransformVariableArrayType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + VariableArrayTypeLoc NewTL = TLB.push<VariableArrayTypeLoc>(Result); NewTL.setLBracketLoc(TL.getLBracketLoc()); NewTL.setRBracketLoc(TL.getRBracketLoc()); @@ -3879,7 +3892,7 @@ QualType TreeTransform<Derived>::TransformVectorType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + VectorTypeLoc NewTL = TLB.push<VectorTypeLoc>(Result); NewTL.setNameLoc(TL.getNameLoc()); @@ -3903,7 +3916,7 @@ QualType TreeTransform<Derived>::TransformExtVectorType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + ExtVectorTypeLoc NewTL = TLB.push<ExtVectorTypeLoc>(Result); NewTL.setNameLoc(TL.getNameLoc()); @@ -3918,29 +3931,29 @@ TreeTransform<Derived>::TransformFunctionTypeParam(ParmVarDecl *OldParm, bool ExpectParameterPack) { TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo(); TypeSourceInfo *NewDI = 0; - + if (NumExpansions && isa<PackExpansionType>(OldDI->getType())) { - // If we're substituting into a pack expansion type and we know the + // If we're substituting into a pack expansion type and we know the // length we want to expand to, just substitute for the pattern. TypeLoc OldTL = OldDI->getTypeLoc(); PackExpansionTypeLoc OldExpansionTL = cast<PackExpansionTypeLoc>(OldTL); - + TypeLocBuilder TLB; TypeLoc NewTL = OldDI->getTypeLoc(); TLB.reserve(NewTL.getFullDataSize()); - - QualType Result = getDerived().TransformType(TLB, + + QualType Result = getDerived().TransformType(TLB, OldExpansionTL.getPatternLoc()); if (Result.isNull()) return 0; - - Result = RebuildPackExpansionType(Result, - OldExpansionTL.getPatternLoc().getSourceRange(), + + Result = RebuildPackExpansionType(Result, + OldExpansionTL.getPatternLoc().getSourceRange(), OldExpansionTL.getEllipsisLoc(), NumExpansions); if (Result.isNull()) return 0; - + PackExpansionTypeLoc NewExpansionTL = TLB.push<PackExpansionTypeLoc>(Result); NewExpansionTL.setEllipsisLoc(OldExpansionTL.getEllipsisLoc()); @@ -4002,27 +4015,27 @@ bool TreeTransform<Derived>:: NumExpansions = OrigNumExpansions; if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), - Unexpanded, - ShouldExpand, + Unexpanded, + ShouldExpand, RetainExpansion, NumExpansions)) { return true; } - + if (ShouldExpand) { // Expand the function parameter pack into multiple, separate // parameters. getDerived().ExpandingFunctionParameterPack(OldParm); for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); - ParmVarDecl *NewParm + ParmVarDecl *NewParm = getDerived().TransformFunctionTypeParam(OldParm, indexAdjustment++, OrigNumExpansions, /*ExpectParameterPack=*/false); if (!NewParm) return true; - + OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4032,14 +4045,14 @@ bool TreeTransform<Derived>:: // forgetting the partially-substituted parameter pack. if (RetainExpansion) { ForgetPartiallySubstitutedPackRAII Forget(getDerived()); - ParmVarDecl *NewParm + ParmVarDecl *NewParm = getDerived().TransformFunctionTypeParam(OldParm, indexAdjustment++, OrigNumExpansions, /*ExpectParameterPack=*/false); if (!NewParm) return true; - + OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4054,8 +4067,8 @@ bool TreeTransform<Derived>:: // We're done with the pack expansion. continue; } - - // We'll substitute the parameter now without expanding the pack + + // We'll substitute the parameter now without expanding the pack // expansion. Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); NewParm = getDerived().TransformFunctionTypeParam(OldParm, @@ -4071,7 +4084,7 @@ bool TreeTransform<Derived>:: if (!NewParm) return true; - + OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4084,26 +4097,26 @@ bool TreeTransform<Derived>:: bool IsPackExpansion = false; llvm::Optional<unsigned> NumExpansions; QualType NewType; - if (const PackExpansionType *Expansion + if (const PackExpansionType *Expansion = dyn_cast<PackExpansionType>(OldType)) { // We have a function parameter pack that may need to be expanded. QualType Pattern = Expansion->getPattern(); SmallVector<UnexpandedParameterPack, 2> Unexpanded; getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded); - + // Determine whether we should expand the parameter packs. bool ShouldExpand = false; bool RetainExpansion = false; if (getDerived().TryExpandParameterPacks(Loc, SourceRange(), - Unexpanded, - ShouldExpand, + Unexpanded, + ShouldExpand, RetainExpansion, NumExpansions)) { return true; } - + if (ShouldExpand) { - // Expand the function parameter pack into multiple, separate + // Expand the function parameter pack into multiple, separate // parameters. for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); @@ -4115,11 +4128,11 @@ bool TreeTransform<Derived>:: if (PVars) PVars->push_back(0); } - + // We're done with the pack expansion. continue; } - + // If we're supposed to retain a pack expansion, do so by temporarily // forgetting the partially-substituted parameter pack. if (RetainExpansion) { @@ -4127,13 +4140,13 @@ bool TreeTransform<Derived>:: QualType NewType = getDerived().TransformType(Pattern); if (NewType.isNull()) return true; - + OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(0); } - // We'll substitute the parameter now without expanding the pack + // We'll substitute the parameter now without expanding the pack // expansion. OldType = Expansion->getPattern(); IsPackExpansion = true; @@ -4142,14 +4155,14 @@ bool TreeTransform<Derived>:: } else { NewType = getDerived().TransformType(OldType); } - + if (NewType.isNull()) return true; if (IsPackExpansion) NewType = getSema().Context.getPackExpansionType(NewType, NumExpansions); - + OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(0); @@ -4193,22 +4206,22 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB, QualType ResultType; if (TL.getTrailingReturn()) { - if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(), + if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(), TL.getParmArray(), TL.getNumArgs(), - TL.getTypePtr()->arg_type_begin(), + TL.getTypePtr()->arg_type_begin(), ParamTypes, &ParamDecls)) return QualType(); { // C++11 [expr.prim.general]p3: - // If a declaration declares a member function or member function - // template of a class X, the expression this is a prvalue of type + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq - // and the end of the function-definition, member-declarator, or + // and the end of the function-definition, member-declarator, or // declarator. Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals); - + ResultType = getDerived().TransformType(TLB, TL.getResultLoc()); if (ResultType.isNull()) return QualType(); @@ -4219,10 +4232,10 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB, if (ResultType.isNull()) return QualType(); - if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(), + if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(), TL.getParmArray(), TL.getNumArgs(), - TL.getTypePtr()->arg_type_begin(), + TL.getTypePtr()->arg_type_begin(), ParamTypes, &ParamDecls)) return QualType(); } @@ -4533,7 +4546,7 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType( TypeLocBuilder &TLB, SubstTemplateTypeParmTypeLoc TL) { const SubstTemplateTypeParmType *T = TL.getTypePtr(); - + // Substitute into the replacement type, which itself might involve something // that needs to be transformed. This only tends to occur with default // template arguments of template template parameters. @@ -4541,13 +4554,13 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType( QualType Replacement = getDerived().TransformType(T->getReplacementType()); if (Replacement.isNull()) return QualType(); - + // Always canonicalize the replacement type. Replacement = SemaRef.Context.getCanonicalType(Replacement); QualType Result - = SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(), + = SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(), Replacement); - + // Propagate type-source information. SubstTemplateTypeParmTypeLoc NewTL = TLB.push<SubstTemplateTypeParmTypeLoc>(Result); @@ -4605,7 +4618,7 @@ QualType TreeTransform<Derived>::TransformAtomicType(TypeLocBuilder &TLB, } namespace { - /// \brief Simple iterator that traverses the template arguments in a + /// \brief Simple iterator that traverses the template arguments in a /// container that provides a \c getArgLoc() member function. /// /// This iterator is intended to be used with the iterator form of @@ -4614,63 +4627,63 @@ namespace { class TemplateArgumentLocContainerIterator { ArgLocContainer *Container; unsigned Index; - + public: typedef TemplateArgumentLoc value_type; typedef TemplateArgumentLoc reference; typedef int difference_type; typedef std::input_iterator_tag iterator_category; - + class pointer { TemplateArgumentLoc Arg; - + public: explicit pointer(TemplateArgumentLoc Arg) : Arg(Arg) { } - + const TemplateArgumentLoc *operator->() const { return &Arg; } }; - - + + TemplateArgumentLocContainerIterator() {} - + TemplateArgumentLocContainerIterator(ArgLocContainer &Container, unsigned Index) : Container(&Container), Index(Index) { } - + TemplateArgumentLocContainerIterator &operator++() { ++Index; return *this; } - + TemplateArgumentLocContainerIterator operator++(int) { TemplateArgumentLocContainerIterator Old(*this); ++(*this); return Old; } - + TemplateArgumentLoc operator*() const { return Container->getArgLoc(Index); } - + pointer operator->() const { return pointer(Container->getArgLoc(Index)); } - + friend bool operator==(const TemplateArgumentLocContainerIterator &X, const TemplateArgumentLocContainerIterator &Y) { return X.Container == Y.Container && X.Index == Y.Index; } - + friend bool operator!=(const TemplateArgumentLocContainerIterator &X, const TemplateArgumentLocContainerIterator &Y) { return !(X == Y); } }; } - - + + template <typename Derived> QualType TreeTransform<Derived>::TransformTemplateSpecializationType( TypeLocBuilder &TLB, @@ -4681,7 +4694,7 @@ QualType TreeTransform<Derived>::TransformTemplateSpecializationType( NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); typedef TemplateArgumentLocContainerIterator<TemplateSpecializationTypeLoc> ArgIterator; - if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), + if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), ArgIterator(TL, TL.getNumArgs()), NewTemplateArgs)) return QualType(); @@ -4736,13 +4749,13 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType( NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); typedef TemplateArgumentLocContainerIterator< DependentTemplateSpecializationTypeLoc> ArgIterator; - if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), + if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), ArgIterator(TL, TL.getNumArgs()), NewTemplateArgs)) return QualType(); - + // FIXME: maybe don't rebuild if all the template arguments are the same. - + if (DependentTemplateName *DTN = Template.getAsDependentTemplateName()) { QualType Result = getSema().Context.getDependentTemplateSpecializationType( @@ -4750,7 +4763,7 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType( DTN->getQualifier(), DTN->getIdentifier(), NewTemplateArgs); - + DependentTemplateSpecializationTypeLoc NewTL = TLB.push<DependentTemplateSpecializationTypeLoc>(Result); NewTL.setElaboratedKeywordLoc(TL.getElaboratedKeywordLoc()); @@ -4763,12 +4776,12 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType( NewTL.setArgLocInfo(i, NewTemplateArgs[i].getLocInfo()); return Result; } - - QualType Result + + QualType Result = getDerived().RebuildTemplateSpecializationType(Template, TL.getTemplateNameLoc(), NewTemplateArgs); - + if (!Result.isNull()) { /// FIXME: Wrap this in an elaborated-type-specifier? TemplateSpecializationTypeLoc NewTL @@ -4780,7 +4793,7 @@ QualType TreeTransform<Derived>::TransformDependentTemplateSpecializationType( for (unsigned i = 0, e = NewTemplateArgs.size(); i != e; ++i) NewTL.setArgLocInfo(i, NewTemplateArgs[i].getLocInfo()); } - + return Result; } @@ -4793,7 +4806,7 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB, NestedNameSpecifierLoc QualifierLoc; // NOTE: the qualifier in an ElaboratedType is optional. if (TL.getQualifierLoc()) { - QualifierLoc + QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(TL.getQualifierLoc()); if (!QualifierLoc) return QualType(); @@ -4825,7 +4838,7 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB, QualifierLoc != TL.getQualifierLoc() || NamedT != T->getNamedType()) { Result = getDerived().RebuildElaboratedType(TL.getElaboratedKeywordLoc(), - T->getKeyword(), + T->getKeyword(), QualifierLoc, NamedT); if (Result.isNull()) return QualType(); @@ -4942,7 +4955,7 @@ QualType TreeTransform<Derived>:: if (!QualifierLoc) return QualType(); } - + return getDerived() .TransformDependentTemplateSpecializationType(TLB, TL, QualifierLoc); } @@ -4953,18 +4966,18 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, DependentTemplateSpecializationTypeLoc TL, NestedNameSpecifierLoc QualifierLoc) { const DependentTemplateSpecializationType *T = TL.getTypePtr(); - + TemplateArgumentListInfo NewTemplateArgs; NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc()); NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); - + typedef TemplateArgumentLocContainerIterator< DependentTemplateSpecializationTypeLoc> ArgIterator; if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), ArgIterator(TL, TL.getNumArgs()), NewTemplateArgs)) return QualType(); - + QualType Result = getDerived().RebuildDependentTemplateSpecializationType(T->getKeyword(), QualifierLoc, @@ -4973,10 +4986,10 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, NewTemplateArgs); if (Result.isNull()) return QualType(); - + if (const ElaboratedType *ElabT = dyn_cast<ElaboratedType>(Result)) { QualType NamedT = ElabT->getNamedType(); - + // Copy information relevant to the template specialization. TemplateSpecializationTypeLoc NamedTL = TLB.push<TemplateSpecializationTypeLoc>(NamedT); @@ -4986,7 +4999,7 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, NamedTL.setRAngleLoc(TL.getRAngleLoc()); for (unsigned I = 0, E = NewTemplateArgs.size(); I != E; ++I) NamedTL.setArgLocInfo(I, NewTemplateArgs[I].getLocInfo()); - + // Copy information relevant to the elaborated type. ElaboratedTypeLoc NewTL = TLB.push<ElaboratedTypeLoc>(Result); NewTL.setElaboratedKeywordLoc(TL.getElaboratedKeywordLoc()); @@ -5018,22 +5031,22 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, template<typename Derived> QualType TreeTransform<Derived>::TransformPackExpansionType(TypeLocBuilder &TLB, PackExpansionTypeLoc TL) { - QualType Pattern - = getDerived().TransformType(TLB, TL.getPatternLoc()); + QualType Pattern + = getDerived().TransformType(TLB, TL.getPatternLoc()); if (Pattern.isNull()) return QualType(); - - QualType Result = TL.getType(); + + QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || Pattern != TL.getPatternLoc().getType()) { - Result = getDerived().RebuildPackExpansionType(Pattern, + Result = getDerived().RebuildPackExpansionType(Pattern, TL.getPatternLoc().getSourceRange(), TL.getEllipsisLoc(), TL.getTypePtr()->getNumExpansions()); if (Result.isNull()) return QualType(); } - + PackExpansionTypeLoc NewT = TLB.push<PackExpansionTypeLoc>(Result); NewT.setEllipsisLoc(TL.getEllipsisLoc()); return Result; @@ -5217,7 +5230,7 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) { ExprResult Cond; VarDecl *ConditionVar = 0; if (S->getConditionVariable()) { - ConditionVar + ConditionVar = cast_or_null<VarDecl>( getDerived().TransformDefinition( S->getConditionVariable()->getLocation(), @@ -5226,25 +5239,25 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) { return StmtError(); } else { Cond = getDerived().TransformExpr(S->getCond()); - + if (Cond.isInvalid()) return StmtError(); - + // Convert the condition to a boolean value. if (S->getCond()) { - ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getIfLoc(), + ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getIfLoc(), Cond.get()); if (CondE.isInvalid()) return StmtError(); - + Cond = CondE.get(); } } - + Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take())); if (!S->getConditionVariable() && S->getCond() && !FullCond.get()) return StmtError(); - + // Transform the "then" branch. StmtResult Then = getDerived().TransformStmt(S->getThen()); if (Then.isInvalid()) @@ -5274,7 +5287,7 @@ TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) { ExprResult Cond; VarDecl *ConditionVar = 0; if (S->getConditionVariable()) { - ConditionVar + ConditionVar = cast_or_null<VarDecl>( getDerived().TransformDefinition( S->getConditionVariable()->getLocation(), @@ -5283,7 +5296,7 @@ TreeTransform<Derived>::TransformSwitchStmt(SwitchStmt *S) { return StmtError(); } else { Cond = getDerived().TransformExpr(S->getCond()); - + if (Cond.isInvalid()) return StmtError(); } @@ -5312,7 +5325,7 @@ TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) { ExprResult Cond; VarDecl *ConditionVar = 0; if (S->getConditionVariable()) { - ConditionVar + ConditionVar = cast_or_null<VarDecl>( getDerived().TransformDefinition( S->getConditionVariable()->getLocation(), @@ -5321,13 +5334,13 @@ TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) { return StmtError(); } else { Cond = getDerived().TransformExpr(S->getCond()); - + if (Cond.isInvalid()) return StmtError(); if (S->getCond()) { // Convert the condition to a boolean value. - ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getWhileLoc(), + ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getWhileLoc(), Cond.get()); if (CondE.isInvalid()) return StmtError(); @@ -5366,7 +5379,7 @@ TreeTransform<Derived>::TransformDoStmt(DoStmt *S) { ExprResult Cond = getDerived().TransformExpr(S->getCond()); if (Cond.isInvalid()) return StmtError(); - + if (!getDerived().AlwaysRebuild() && Cond.get() == S->getCond() && Body.get() == S->getBody()) @@ -5389,7 +5402,7 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) { ExprResult Cond; VarDecl *ConditionVar = 0; if (S->getConditionVariable()) { - ConditionVar + ConditionVar = cast_or_null<VarDecl>( getDerived().TransformDefinition( S->getConditionVariable()->getLocation(), @@ -5398,13 +5411,13 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) { return StmtError(); } else { Cond = getDerived().TransformExpr(S->getCond()); - + if (Cond.isInvalid()) return StmtError(); if (S->getCond()) { // Convert the condition to a boolean value. - ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getForLoc(), + ExprResult CondE = getSema().ActOnBooleanCondition(0, S->getForLoc(), Cond.get()); if (CondE.isInvalid()) return StmtError(); @@ -5413,7 +5426,7 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) { } } - Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take())); + Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.take())); if (!S->getConditionVariable() && S->getCond() && !FullCond.get()) return StmtError(); @@ -5450,7 +5463,7 @@ TreeTransform<Derived>::TransformGotoStmt(GotoStmt *S) { S->getLabel()); if (!LD) return StmtError(); - + // Goto statements must always be rebuilt, to resolve the label. return getDerived().RebuildGotoStmt(S->getGotoLoc(), S->getLabelLoc(), cast<LabelDecl>(LD)); @@ -5524,7 +5537,7 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) { template<typename Derived> StmtResult TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) { - + ASTOwningVector<Expr*> Constraints(getSema()); ASTOwningVector<Expr*> Exprs(getSema()); SmallVector<IdentifierInfo *, 4> Names; @@ -5533,43 +5546,43 @@ TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) { ASTOwningVector<Expr*> Clobbers(getSema()); bool ExprsChanged = false; - + // Go through the outputs. for (unsigned I = 0, E = S->getNumOutputs(); I != E; ++I) { Names.push_back(S->getOutputIdentifier(I)); - + // No need to transform the constraint literal. Constraints.push_back(S->getOutputConstraintLiteral(I)); - + // Transform the output expr. Expr *OutputExpr = S->getOutputExpr(I); ExprResult Result = getDerived().TransformExpr(OutputExpr); if (Result.isInvalid()) return StmtError(); - + ExprsChanged |= Result.get() != OutputExpr; - + Exprs.push_back(Result.get()); } - + // Go through the inputs. for (unsigned I = 0, E = S->getNumInputs(); I != E; ++I) { Names.push_back(S->getInputIdentifier(I)); - + // No need to transform the constraint literal. Constraints.push_back(S->getInputConstraintLiteral(I)); - + // Transform the input expr. Expr *InputExpr = S->getInputExpr(I); ExprResult Result = getDerived().TransformExpr(InputExpr); if (Result.isInvalid()) return StmtError(); - + ExprsChanged |= Result.get() != InputExpr; - + Exprs.push_back(Result.get()); } - + if (!getDerived().AlwaysRebuild() && !ExprsChanged) return SemaRef.Owned(S); @@ -5594,6 +5607,18 @@ TreeTransform<Derived>::TransformAsmStmt(AsmStmt *S) { S->isMSAsm()); } +template<typename Derived> +StmtResult +TreeTransform<Derived>::TransformMSAsmStmt(MSAsmStmt *S) { + ArrayRef<Token> AsmToks = + llvm::makeArrayRef(S->getAsmToks(), S->getNumAsmToks()); + ArrayRef<unsigned> LineEnds = + llvm::makeArrayRef(S->getLineEnds(), S->getNumLineEnds()); + + // No need to transform the asm string literal. + return getDerived().RebuildMSAsmStmt(S->getAsmLoc(), AsmToks, LineEnds, + S->getEndLoc()); +} template<typename Derived> StmtResult @@ -5602,7 +5627,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { StmtResult TryBody = getDerived().TransformStmt(S->getTryBody()); if (TryBody.isInvalid()) return StmtError(); - + // Transform the @catch statements (if present). bool AnyCatchChanged = false; ASTOwningVector<Stmt*> CatchStmts(SemaRef); @@ -5614,7 +5639,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { AnyCatchChanged = true; CatchStmts.push_back(Catch.release()); } - + // Transform the @finally statement (if present). StmtResult Finally; if (S->getFinallyStmt()) { @@ -5629,7 +5654,7 @@ TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { !AnyCatchChanged && Finally.get() == S->getFinallyStmt()) return SemaRef.Owned(S); - + // Build a new statement. return getDerived().RebuildObjCAtTryStmt(S->getAtTryLoc(), TryBody.get(), move_arg(CatchStmts), Finally.get()); @@ -5647,26 +5672,26 @@ TreeTransform<Derived>::TransformObjCAtCatchStmt(ObjCAtCatchStmt *S) { if (!TSInfo) return StmtError(); } - + QualType T; if (TSInfo) T = TSInfo->getType(); else { T = getDerived().TransformType(FromVar->getType()); if (T.isNull()) - return StmtError(); + return StmtError(); } - + Var = getDerived().RebuildObjCExceptionDecl(FromVar, TSInfo, T); if (!Var) return StmtError(); } - + StmtResult Body = getDerived().TransformStmt(S->getCatchBody()); if (Body.isInvalid()) return StmtError(); - - return getDerived().RebuildObjCAtCatchStmt(S->getAtCatchLoc(), + + return getDerived().RebuildObjCAtCatchStmt(S->getAtCatchLoc(), S->getRParenLoc(), Var, Body.get()); } @@ -5678,7 +5703,7 @@ TreeTransform<Derived>::TransformObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { StmtResult Body = getDerived().TransformStmt(S->getFinallyBody()); if (Body.isInvalid()) return StmtError(); - + // If nothing changed, just retain this statement. if (!getDerived().AlwaysRebuild() && Body.get() == S->getFinallyBody()) @@ -5698,11 +5723,11 @@ TreeTransform<Derived>::TransformObjCAtThrowStmt(ObjCAtThrowStmt *S) { if (Operand.isInvalid()) return StmtError(); } - + if (!getDerived().AlwaysRebuild() && Operand.get() == S->getThrowExpr()) return getSema().Owned(S); - + return getDerived().RebuildObjCAtThrowStmt(S->getThrowLoc(), Operand.get()); } @@ -5719,12 +5744,12 @@ TreeTransform<Derived>::TransformObjCAtSynchronizedStmt( Object.get()); if (Object.isInvalid()) return StmtError(); - + // Transform the body. StmtResult Body = getDerived().TransformStmt(S->getSynchBody()); if (Body.isInvalid()) return StmtError(); - + // If nothing change, just retain the current statement. if (!getDerived().AlwaysRebuild() && Object.get() == S->getSynchExpr() && @@ -5744,7 +5769,7 @@ TreeTransform<Derived>::TransformObjCAutoreleasePoolStmt( StmtResult Body = getDerived().TransformStmt(S->getSubStmt()); if (Body.isInvalid()) return StmtError(); - + // If nothing changed, just retain this statement. if (!getDerived().AlwaysRebuild() && Body.get() == S->getSubStmt()) @@ -5763,28 +5788,24 @@ TreeTransform<Derived>::TransformObjCForCollectionStmt( StmtResult Element = getDerived().TransformStmt(S->getElement()); if (Element.isInvalid()) return StmtError(); - + // Transform the collection expression. ExprResult Collection = getDerived().TransformExpr(S->getCollection()); if (Collection.isInvalid()) return StmtError(); - Collection = getDerived().RebuildObjCForCollectionOperand(S->getForLoc(), - Collection.take()); - if (Collection.isInvalid()) - return StmtError(); - + // Transform the body. StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) return StmtError(); - + // If nothing changed, just retain this statement. if (!getDerived().AlwaysRebuild() && Element.get() == S->getElement() && Collection.get() == S->getCollection() && Body.get() == S->getBody()) return SemaRef.Owned(S); - + // Build a new statement. return getDerived().RebuildObjCForCollectionStmt(S->getForLoc(), /*FIXME:*/S->getForLoc(), @@ -5931,7 +5952,7 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt( // Transform the nested-name-specifier, if any. NestedNameSpecifierLoc QualifierLoc; if (S->getQualifierLoc()) { - QualifierLoc + QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(S->getQualifierLoc()); if (!QualifierLoc) return StmtError(); @@ -5950,7 +5971,7 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt( QualifierLoc == S->getQualifierLoc() && NameInfo.getName() == S->getNameInfo().getName()) return S; - + // Determine whether this name exists, if we can. CXXScopeSpec SS; SS.Adopt(QualifierLoc); @@ -5959,32 +5980,32 @@ TreeTransform<Derived>::TransformMSDependentExistsStmt( case Sema::IER_Exists: if (S->isIfExists()) break; - + return new (getSema().Context) NullStmt(S->getKeywordLoc()); case Sema::IER_DoesNotExist: if (S->isIfNotExists()) break; - + return new (getSema().Context) NullStmt(S->getKeywordLoc()); - + case Sema::IER_Dependent: Dependent = true; break; - + case Sema::IER_Error: return StmtError(); } - + // We need to continue with the instantiation, so do so now. StmtResult SubStmt = getDerived().TransformCompoundStmt(S->getSubStmt()); if (SubStmt.isInvalid()) return StmtError(); - + // If we have resolved the name, just transform to the substatement. if (!Dependent) return SubStmt; - + // The name is still dependent, so build a dependent expression again. return getDerived().RebuildMSDependentExistsStmt(S->getKeywordLoc(), S->isIfExists(), @@ -6101,7 +6122,7 @@ TreeTransform<Derived>::TransformDeclRefExpr(DeclRefExpr *E) { return ExprError(); } - return getDerived().RebuildDeclRefExpr(QualifierLoc, ND, NameInfo, + return getDerived().RebuildDeclRefExpr(QualifierLoc, ND, NameInfo, TemplateArgs); } @@ -6213,12 +6234,12 @@ TreeTransform<Derived>::TransformOffsetOfExpr(OffsetOfExpr *E) { TypeSourceInfo *Type = getDerived().TransformType(E->getTypeSourceInfo()); if (!Type) return ExprError(); - + // Transform all of the components into components similar to what the // parser uses. - // FIXME: It would be slightly more efficient in the non-dependent case to - // just map FieldDecls, rather than requiring the rebuilder to look for - // the fields again. However, __builtin_offsetof is rare enough in + // FIXME: It would be slightly more efficient in the non-dependent case to + // just map FieldDecls, rather than requiring the rebuilder to look for + // the fields again. However, __builtin_offsetof is rare enough in // template code that we don't care. bool ExprChanged = false; typedef Sema::OffsetOfComponent Component; @@ -6236,36 +6257,36 @@ TreeTransform<Derived>::TransformOffsetOfExpr(OffsetOfExpr *E) { ExprResult Index = getDerived().TransformExpr(FromIndex); if (Index.isInvalid()) return ExprError(); - + ExprChanged = ExprChanged || Index.get() != FromIndex; Comp.isBrackets = true; Comp.U.E = Index.get(); break; } - + case Node::Field: case Node::Identifier: Comp.isBrackets = false; Comp.U.IdentInfo = ON.getFieldName(); if (!Comp.U.IdentInfo) continue; - + break; - + case Node::Base: // Will be recomputed during the rebuild. continue; } - + Components.push_back(Comp); } - + // If nothing changed, retain the existing expression. if (!getDerived().AlwaysRebuild() && Type == E->getTypeSourceInfo() && !ExprChanged) return SemaRef.Owned(E); - + // Build a new offsetof expression. return getDerived().RebuildOffsetOfExpr(E->getOperatorLoc(), Type, Components.data(), Components.size(), @@ -6373,10 +6394,10 @@ TreeTransform<Derived>::TransformCallExpr(CallExpr *E) { // Transform arguments. bool ArgChanged = false; ASTOwningVector<Expr*> Args(SemaRef); - if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, + if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgChanged)) return ExprError(); - + if (!getDerived().AlwaysRebuild() && Callee.get() == E->getCallee() && !ArgChanged) @@ -6401,7 +6422,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) { if (E->hasQualifier()) { QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc()); - + if (!QualifierLoc) return ExprError(); } @@ -6429,7 +6450,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) { Member == E->getMemberDecl() && FoundDecl == E->getFoundDecl() && !E->hasExplicitTemplateArgs()) { - + // Mark it referenced in the new context regardless. // FIXME: this is a bit instantiation-specific. SemaRef.MarkMemberReferenced(E); @@ -6446,7 +6467,7 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) { TransArgs)) return ExprError(); } - + // FIXME: Bogus source location for the operator SourceLocation FakeOperatorLoc = SemaRef.PP.getLocForEndOfToken(E->getBase()->getSourceRange().getEnd()); @@ -6564,7 +6585,7 @@ TreeTransform<Derived>::TransformCStyleCastExpr(CStyleCastExpr *E) { TypeSourceInfo *Type = getDerived().TransformType(E->getTypeInfoAsWritten()); if (!Type) return ExprError(); - + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExprAsWritten()); if (SubExpr.isInvalid()) @@ -6632,10 +6653,10 @@ TreeTransform<Derived>::TransformInitListExpr(InitListExpr *E) { bool InitChanged = false; ASTOwningVector<Expr*, 4> Inits(SemaRef); - if (getDerived().TransformExprs(E->getInits(), E->getNumInits(), false, + if (getDerived().TransformExprs(E->getInits(), E->getNumInits(), false, Inits, &InitChanged)) return ExprError(); - + if (!getDerived().AlwaysRebuild() && !InitChanged) return SemaRef.Owned(E); @@ -6716,7 +6737,7 @@ ExprResult TreeTransform<Derived>::TransformImplicitValueInitExpr( ImplicitValueInitExpr *E) { TemporaryBase Rebase(*this, E->getLocStart(), DeclarationName()); - + // FIXME: Will we ever have proper type location here? Will we actually // need to transform the type? QualType T = getDerived().TransformType(E->getType()); @@ -6758,7 +6779,7 @@ TreeTransform<Derived>::TransformParenListExpr(ParenListExpr *E) { if (TransformExprs(E->getExprs(), E->getNumExprs(), true, Inits, &ArgumentChanged)) return ExprError(); - + return getDerived().RebuildParenListExpr(E->getLParenLoc(), move_arg(Inits), E->getRParenLoc()); @@ -6776,13 +6797,13 @@ TreeTransform<Derived>::TransformAddrLabelExpr(AddrLabelExpr *E) { E->getLabel()); if (!LD) return ExprError(); - + return getDerived().RebuildAddrLabelExpr(E->getAmpAmpLoc(), E->getLabelLoc(), cast<LabelDecl>(LD)); } template<typename Derived> -ExprResult +ExprResult TreeTransform<Derived>::TransformStmtExpr(StmtExpr *E) { SemaRef.ActOnStartStmtExpr(); StmtResult SubStmt @@ -6845,7 +6866,7 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { case OO_Array_New: case OO_Array_Delete: llvm_unreachable("new and delete operators cannot use CXXOperatorCallExpr"); - + case OO_Call: { // This is a call to an object's operator(). assert(E->getNumArgs() >= 1 && "Object call is missing arguments"); @@ -6862,7 +6883,7 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { // Transform the call arguments. ASTOwningVector<Expr*> Args(SemaRef); - if (getDerived().TransformExprs(E->getArgs() + 1, E->getNumArgs() - 1, true, + if (getDerived().TransformExprs(E->getArgs() + 1, E->getNumArgs() - 1, true, Args)) return ExprError(); @@ -6937,7 +6958,7 @@ TreeTransform<Derived>::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { // Transform arguments. bool ArgChanged = false; ASTOwningVector<Expr*> Args(SemaRef); - if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, + if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgChanged)) return ExprError(); @@ -6960,7 +6981,7 @@ TreeTransform<Derived>::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) { TypeSourceInfo *Type = getDerived().TransformType(E->getTypeInfoAsWritten()); if (!Type) return ExprError(); - + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExprAsWritten()); if (SubExpr.isInvalid()) @@ -7140,7 +7161,7 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) { getSema().CheckCXXThisCapture(E->getLocStart()); return SemaRef.Owned(E); } - + return getDerived().RebuildCXXThisExpr(E->getLocStart(), T, E->isImplicit()); } @@ -7182,12 +7203,12 @@ TreeTransform<Derived>::TransformCXXScalarValueInitExpr( TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo()); if (!T) return ExprError(); - + if (!getDerived().AlwaysRebuild() && T == E->getTypeSourceInfo()) return SemaRef.Owned(E); - return getDerived().RebuildCXXScalarValueInitExpr(T, + return getDerived().RebuildCXXScalarValueInitExpr(T, /*FIXME:*/T->getTypeLoc().getEndLoc(), E->getRParenLoc()); } @@ -7209,7 +7230,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) { // Transform the placement arguments (if any). bool ArgumentChanged = false; ASTOwningVector<Expr*> PlacementArgs(SemaRef); - if (getDerived().TransformExprs(E->getPlacementArgs(), + if (getDerived().TransformExprs(E->getPlacementArgs(), E->getNumPlacementArgs(), true, PlacementArgs, &ArgumentChanged)) return ExprError(); @@ -7240,7 +7261,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) { if (!OperatorDelete) return ExprError(); } - + if (!getDerived().AlwaysRebuild() && AllocTypeInfo == E->getAllocatedTypeSourceInfo() && ArraySize.get() == E->getArraySize() && @@ -7254,7 +7275,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) { SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorNew); if (OperatorDelete) SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorDelete); - + if (E->isArray() && !E->getAllocatedType()->isDependentType()) { QualType ElementType = SemaRef.Context.getBaseElementType(E->getAllocatedType()); @@ -7281,9 +7302,9 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) { // Do nothing } else if (const ConstantArrayType *ConsArrayT = dyn_cast<ConstantArrayType>(ArrayT)) { - ArraySize + ArraySize = SemaRef.Owned(IntegerLiteral::Create(SemaRef.Context, - ConsArrayT->getSize(), + ConsArrayT->getSize(), SemaRef.Context.getSizeType(), /*FIXME:*/E->getLocStart())); AllocType = ConsArrayT->getElementType(); @@ -7325,7 +7346,7 @@ TreeTransform<Derived>::TransformCXXDeleteExpr(CXXDeleteExpr *E) { if (!OperatorDelete) return ExprError(); } - + if (!getDerived().AlwaysRebuild() && Operand.get() == E->getArgument() && OperatorDelete == E->getOperatorDelete()) { @@ -7333,17 +7354,17 @@ TreeTransform<Derived>::TransformCXXDeleteExpr(CXXDeleteExpr *E) { // FIXME: instantiation-specific. if (OperatorDelete) SemaRef.MarkFunctionReferenced(E->getLocStart(), OperatorDelete); - + if (!E->getArgument()->isTypeDependent()) { QualType Destroyed = SemaRef.Context.getBaseElementType( E->getDestroyedType()); if (const RecordType *DestroyedRec = Destroyed->getAs<RecordType>()) { CXXRecordDecl *Record = cast<CXXRecordDecl>(DestroyedRec->getDecl()); - SemaRef.MarkFunctionReferenced(E->getLocStart(), + SemaRef.MarkFunctionReferenced(E->getLocStart(), SemaRef.LookupDestructor(Record)); } } - + return SemaRef.Owned(E); } @@ -7363,14 +7384,14 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr( ParsedType ObjectTypePtr; bool MayBePseudoDestructor = false; - Base = SemaRef.ActOnStartCXXMemberReference(0, Base.get(), + Base = SemaRef.ActOnStartCXXMemberReference(0, Base.get(), E->getOperatorLoc(), E->isArrow()? tok::arrow : tok::period, ObjectTypePtr, MayBePseudoDestructor); if (Base.isInvalid()) return ExprError(); - + QualType ObjectType = ObjectTypePtr.get(); NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc(); if (QualifierLoc) { @@ -7405,7 +7426,7 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr( false); if (!T) return ExprError(); - + Destroyed = SemaRef.Context.getTrivialTypeSourceInfo(SemaRef.GetTypeFromParser(T), E->getDestroyedTypeLoc()); @@ -7417,7 +7438,7 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr( if (!ScopeTypeInfo) return ExprError(); } - + return getDerived().RebuildCXXPseudoDestructorExpr(Base.get(), E->getOperatorLoc(), E->isArrow(), @@ -7473,10 +7494,10 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr( = getDerived().TransformNestedNameSpecifierLoc(Old->getQualifierLoc()); if (!QualifierLoc) return ExprError(); - + SS.Adopt(QualifierLoc); - } - + } + if (Old->getNamingClass()) { CXXRecordDecl *NamingClass = cast_or_null<CXXRecordDecl>(getDerived().TransformDecl( @@ -7484,7 +7505,7 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr( Old->getNamingClass())); if (!NamingClass) return ExprError(); - + R.setNamingClass(NamingClass); } @@ -7559,7 +7580,7 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { QualType To = getDerived().TransformType(TLB, FromTL); if (To.isNull()) return ExprError(); - + if (To == From->getType()) Args.push_back(From); else { @@ -7568,15 +7589,15 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { } continue; } - + ArgChanged = true; - + // We have a pack expansion. Instantiate it. - PackExpansionTypeLoc ExpansionTL = cast<PackExpansionTypeLoc>(FromTL); + PackExpansionTypeLoc ExpansionTL = cast<PackExpansionTypeLoc>(FromTL); TypeLoc PatternTL = ExpansionTL.getPatternLoc(); SmallVector<UnexpandedParameterPack, 2> Unexpanded; SemaRef.collectUnexpandedParameterPacks(PatternTL, Unexpanded); - + // Determine whether the set of unexpanded parameter packs can and should // be expanded. bool Expand = true; @@ -7590,13 +7611,13 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { Expand, RetainExpansion, NumExpansions)) return ExprError(); - + if (!Expand) { // The transform has determined that we should perform a simple - // transformation on the pack expansion, producing another pack + // transformation on the pack expansion, producing another pack // expansion. Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); - + TypeLocBuilder TLB; TLB.reserve(From->getTypeLoc().getFullDataSize()); @@ -7604,13 +7625,13 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { if (To.isNull()) return ExprError(); - To = getDerived().RebuildPackExpansionType(To, + To = getDerived().RebuildPackExpansionType(To, PatternTL.getSourceRange(), ExpansionTL.getEllipsisLoc(), NumExpansions); if (To.isNull()) return ExprError(); - + PackExpansionTypeLoc ToExpansionTL = TLB.push<PackExpansionTypeLoc>(To); ToExpansionTL.setEllipsisLoc(ExpansionTL.getEllipsisLoc()); @@ -7630,34 +7651,34 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { Args.push_back(TLB.getTypeSourceInfo(SemaRef.Context, To)); } - + if (!RetainExpansion) continue; - + // If we're supposed to retain a pack expansion, do so by temporarily // forgetting the partially-substituted parameter pack. ForgetPartiallySubstitutedPackRAII Forget(getDerived()); TypeLocBuilder TLB; TLB.reserve(From->getTypeLoc().getFullDataSize()); - + QualType To = getDerived().TransformType(TLB, PatternTL); if (To.isNull()) return ExprError(); - - To = getDerived().RebuildPackExpansionType(To, + + To = getDerived().RebuildPackExpansionType(To, PatternTL.getSourceRange(), ExpansionTL.getEllipsisLoc(), NumExpansions); if (To.isNull()) return ExprError(); - + PackExpansionTypeLoc ToExpansionTL = TLB.push<PackExpansionTypeLoc>(To); ToExpansionTL.setEllipsisLoc(ExpansionTL.getEllipsisLoc()); Args.push_back(TLB.getTypeSourceInfo(SemaRef.Context, To)); } - + if (!getDerived().AlwaysRebuild() && !ArgChanged) return SemaRef.Owned(E); @@ -7783,10 +7804,10 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) { bool ArgumentChanged = false; ASTOwningVector<Expr*> Args(SemaRef); - if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, + if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgumentChanged)) return ExprError(); - + if (!getDerived().AlwaysRebuild() && T == E->getType() && Constructor == E->getConstructor() && @@ -7837,7 +7858,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr( CXXConstructorDecl *Constructor = cast_or_null<CXXConstructorDecl>( - getDerived().TransformDecl(E->getLocStart(), + getDerived().TransformDecl(E->getLocStart(), E->getConstructor())); if (!Constructor) return ExprError(); @@ -7845,7 +7866,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr( bool ArgumentChanged = false; ASTOwningVector<Expr*> Args(SemaRef); Args.reserve(E->getNumArgs()); - if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, + if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgumentChanged)) return ExprError(); @@ -7857,7 +7878,7 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr( SemaRef.MarkFunctionReferenced(E->getLocStart(), Constructor); return SemaRef.MaybeBindToTemporary(E); } - + return getDerived().RebuildCXXTemporaryObjectExpr(T, /*FIXME:*/T->getTypeLoc().getEndLoc(), move_arg(Args), @@ -7872,40 +7893,38 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { = getSema().createLambdaClosureType(E->getIntroducerRange(), /*KnownDependent=*/false); getDerived().transformedLocalDecl(E->getLambdaClass(), Class); - + // Transform the type of the lambda parameters and start the definition of // the lambda itself. TypeSourceInfo *MethodTy - = TransformType(E->getCallOperator()->getTypeSourceInfo()); + = TransformType(E->getCallOperator()->getTypeSourceInfo()); if (!MethodTy) return ExprError(); // Transform lambda parameters. - bool Invalid = false; llvm::SmallVector<QualType, 4> ParamTypes; llvm::SmallVector<ParmVarDecl *, 4> Params; if (getDerived().TransformFunctionTypeParams(E->getLocStart(), E->getCallOperator()->param_begin(), E->getCallOperator()->param_size(), 0, ParamTypes, &Params)) - Invalid = true; + return ExprError(); // Build the call operator. - // Note: Once a lambda mangling number and context declaration have been - // assigned, they never change. - unsigned ManglingNumber = E->getLambdaClass()->getLambdaManglingNumber(); - Decl *ContextDecl = E->getLambdaClass()->getLambdaContextDecl(); CXXMethodDecl *CallOperator = getSema().startLambdaDefinition(Class, E->getIntroducerRange(), - MethodTy, + MethodTy, E->getCallOperator()->getLocEnd(), - Params, ManglingNumber, ContextDecl); + Params); getDerived().transformAttrs(E->getCallOperator(), CallOperator); - - // FIXME: Instantiation-specific. - CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(), - TSK_ImplicitInstantiation); + return getDerived().TransformLambdaScope(E, CallOperator); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformLambdaScope(LambdaExpr *E, + CXXMethodDecl *CallOperator) { // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), CallOperator); @@ -7916,10 +7935,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { E->hasExplicitParameters(), E->hasExplicitResultType(), E->isMutable()); - + // Transform captures. + bool Invalid = false; bool FinishedExplicitCaptures = false; - for (LambdaExpr::capture_iterator C = E->capture_begin(), + for (LambdaExpr::capture_iterator C = E->capture_begin(), CEnd = E->capture_end(); C != CEnd; ++C) { // When we hit the first implicit capture, tell Sema that we've finished @@ -7928,13 +7948,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { getSema().finishLambdaExplicitCaptures(LSI); FinishedExplicitCaptures = true; } - + // Capturing 'this' is trivial. if (C->capturesThis()) { getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit()); continue; } - + // Determine the capture kind for Sema. Sema::TryCaptureKind Kind = C->isImplicit()? Sema::TryCapture_Implicit @@ -7947,13 +7967,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { bool ShouldExpand = false; bool RetainExpansion = false; llvm::Optional<unsigned> NumExpansions; - if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(), - C->getLocation(), + if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(), + C->getLocation(), Unexpanded, ShouldExpand, RetainExpansion, NumExpansions)) return ExprError(); - + if (ShouldExpand) { // The transform has determined that we should perform an expansion; // transform and capture each of the arguments. @@ -7962,31 +7982,31 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), + = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), Pack)); if (!CapturedVar) { Invalid = true; continue; } - + // Capture the transformed variable. - getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind); - } + getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind); + } continue; } - + EllipsisLoc = C->getEllipsisLoc(); } - + // Transform the captured variable. VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), + = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), C->getCapturedVar())); if (!CapturedVar) { Invalid = true; continue; } - + // Capture the transformed variable. getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind); } @@ -7996,10 +8016,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // Enter a new evaluation context to insulate the lambda from any // cleanups from the enclosing full-expression. - getSema().PushExpressionEvaluationContext(Sema::PotentiallyEvaluated); + getSema().PushExpressionEvaluationContext(Sema::PotentiallyEvaluated); if (Invalid) { - getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0, + getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0, /*IsInstantiation=*/true); return ExprError(); } @@ -8007,12 +8027,12 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // Instantiate the body of the lambda expression. StmtResult Body = getDerived().TransformStmt(E->getBody()); if (Body.isInvalid()) { - getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0, + getSema().ActOnLambdaError(E->getLocStart(), /*CurScope=*/0, /*IsInstantiation=*/true); - return ExprError(); + return ExprError(); } - return getSema().ActOnLambdaExpr(E->getLocStart(), Body.take(), + return getSema().ActOnLambdaExpr(E->getLocStart(), Body.take(), /*CurScope=*/0, /*IsInstantiation=*/true); } @@ -8027,10 +8047,10 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr( bool ArgumentChanged = false; ASTOwningVector<Expr*> Args(SemaRef); Args.reserve(E->arg_size()); - if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args, + if (getDerived().TransformExprs(E->arg_begin(), E->arg_size(), true, Args, &ArgumentChanged)) return ExprError(); - + if (!getDerived().AlwaysRebuild() && T == E->getTypeSourceInfo() && !ArgumentChanged) @@ -8209,16 +8229,16 @@ TreeTransform<Derived>::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old) // Determine the naming class. if (Old->getNamingClass()) { - CXXRecordDecl *NamingClass + CXXRecordDecl *NamingClass = cast_or_null<CXXRecordDecl>(getDerived().TransformDecl( Old->getMemberLoc(), Old->getNamingClass())); if (!NamingClass) return ExprError(); - + R.setNamingClass(NamingClass); } - + TemplateArgumentListInfo TransArgs; if (Old->hasExplicitTemplateArgs()) { TransArgs.setLAngleLoc(Old->getLAngleLoc()); @@ -8234,7 +8254,7 @@ TreeTransform<Derived>::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old) // base (and therefore couldn't do the check) and a // nested-name-qualifier (and therefore could do the lookup). NamedDecl *FirstQualifierInScope = 0; - + return getDerived().RebuildUnresolvedMemberExpr(Base.get(), BaseType, Old->getOperatorLoc(), @@ -8267,7 +8287,7 @@ TreeTransform<Derived>::TransformPackExpansionExpr(PackExpansionExpr *E) { ExprResult Pattern = getDerived().TransformExpr(E->getPattern()); if (Pattern.isInvalid()) return ExprError(); - + if (!getDerived().AlwaysRebuild() && Pattern.get() == E->getPattern()) return SemaRef.Owned(E); @@ -8285,33 +8305,33 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) { // Note: None of the implementations of TryExpandParameterPacks can ever // produce a diagnostic when given only a single unexpanded parameter pack, - // so + // so UnexpandedParameterPack Unexpanded(E->getPack(), E->getPackLoc()); bool ShouldExpand = false; bool RetainExpansion = false; llvm::Optional<unsigned> NumExpansions; - if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(), + if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(), Unexpanded, ShouldExpand, RetainExpansion, NumExpansions)) return ExprError(); - + if (RetainExpansion) return SemaRef.Owned(E); - + NamedDecl *Pack = E->getPack(); if (!ShouldExpand) { - Pack = cast_or_null<NamedDecl>(getDerived().TransformDecl(E->getPackLoc(), + Pack = cast_or_null<NamedDecl>(getDerived().TransformDecl(E->getPackLoc(), Pack)); if (!Pack) return ExprError(); } - + // We now know the length of the parameter pack, so build a new expression // that stores that length. - return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), Pack, - E->getPackLoc(), E->getRParenLoc(), + return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), Pack, + E->getPackLoc(), E->getRParenLoc(), NumExpansions); } @@ -8337,7 +8357,7 @@ TreeTransform<Derived>::TransformMaterializeTemporaryExpr( MaterializeTemporaryExpr *E) { return getDerived().TransformExpr(E->GetTemporaryExpr()); } - + template<typename Derived> ExprResult TreeTransform<Derived>::TransformObjCStringLiteral(ObjCStringLiteral *E) { @@ -8352,8 +8372,16 @@ TreeTransform<Derived>::TransformObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { template<typename Derived> ExprResult -TreeTransform<Derived>::TransformObjCNumericLiteral(ObjCNumericLiteral *E) { - return SemaRef.MaybeBindToTemporary(E); +TreeTransform<Derived>::TransformObjCBoxedExpr(ObjCBoxedExpr *E) { + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExpr()); + if (SubExpr.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + SubExpr.get() == E->getSubExpr()) + return SemaRef.Owned(E); + + return getDerived().RebuildObjCBoxedExpr(E->getSourceRange(), SubExpr.get()); } template<typename Derived> @@ -8362,13 +8390,13 @@ TreeTransform<Derived>::TransformObjCArrayLiteral(ObjCArrayLiteral *E) { // Transform each of the elements. llvm::SmallVector<Expr *, 8> Elements; bool ArgChanged = false; - if (getDerived().TransformExprs(E->getElements(), E->getNumElements(), + if (getDerived().TransformExprs(E->getElements(), E->getNumElements(), /*IsCall=*/false, Elements, &ArgChanged)) return ExprError(); - + if (!getDerived().AlwaysRebuild() && !ArgChanged) return SemaRef.MaybeBindToTemporary(E); - + return getDerived().RebuildObjCArrayLiteral(E->getSourceRange(), Elements.data(), Elements.size()); @@ -8377,13 +8405,13 @@ TreeTransform<Derived>::TransformObjCArrayLiteral(ObjCArrayLiteral *E) { template<typename Derived> ExprResult TreeTransform<Derived>::TransformObjCDictionaryLiteral( - ObjCDictionaryLiteral *E) { + ObjCDictionaryLiteral *E) { // Transform each of the elements. llvm::SmallVector<ObjCDictionaryElement, 8> Elements; bool ArgChanged = false; for (unsigned I = 0, N = E->getNumElements(); I != N; ++I) { ObjCDictionaryElement OrigElement = E->getKeyValueElement(I); - + if (OrigElement.isPackExpansion()) { // This key/value element is a pack expansion. SmallVector<UnexpandedParameterPack, 2> Unexpanded; @@ -8408,7 +8436,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( if (!Expand) { // The transform has determined that we should perform a simple - // transformation on the pack expansion, producing another pack + // transformation on the pack expansion, producing another pack // expansion. Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); ExprResult Key = getDerived().TransformExpr(OrigElement.Key); @@ -8421,11 +8449,11 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( ExprResult Value = getDerived().TransformExpr(OrigElement.Value); if (Value.isInvalid()) return ExprError(); - + if (Value.get() != OrigElement.Value) ArgChanged = true; - ObjCDictionaryElement Expansion = { + ObjCDictionaryElement Expansion = { Key.get(), Value.get(), OrigElement.EllipsisLoc, NumExpansions }; Elements.push_back(Expansion); @@ -8435,7 +8463,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( // Record right away that the argument was changed. This needs // to happen even if the array expands to nothing. ArgChanged = true; - + // The transform has determined that we should perform an elementwise // expansion of the pattern. Do so. for (unsigned I = 0; I != *NumExpansions; ++I) { @@ -8448,7 +8476,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( if (Value.isInvalid()) return ExprError(); - ObjCDictionaryElement Element = { + ObjCDictionaryElement Element = { Key.get(), Value.get(), SourceLocation(), NumExpansions }; @@ -8457,7 +8485,7 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( if (Key.get()->containsUnexpandedParameterPack() || Value.get()->containsUnexpandedParameterPack()) Element.EllipsisLoc = OrigElement.EllipsisLoc; - + Elements.push_back(Element); } @@ -8469,25 +8497,25 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral( ExprResult Key = getDerived().TransformExpr(OrigElement.Key); if (Key.isInvalid()) return ExprError(); - + if (Key.get() != OrigElement.Key) ArgChanged = true; - + // Transform and check value. ExprResult Value = getDerived().TransformExpr(OrigElement.Value); if (Value.isInvalid()) return ExprError(); - + if (Value.get() != OrigElement.Value) ArgChanged = true; - - ObjCDictionaryElement Element = { + + ObjCDictionaryElement Element = { Key.get(), Value.get(), SourceLocation(), llvm::Optional<unsigned>() }; Elements.push_back(Element); } - + if (!getDerived().AlwaysRebuild() && !ArgChanged) return SemaRef.MaybeBindToTemporary(E); @@ -8531,22 +8559,22 @@ TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { template<typename Derived> ExprResult TreeTransform<Derived>:: TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { - TypeSourceInfo *TSInfo + TypeSourceInfo *TSInfo = getDerived().TransformType(E->getTypeInfoAsWritten()); if (!TSInfo) return ExprError(); - + ExprResult Result = getDerived().TransformExpr(E->getSubExpr()); - if (Result.isInvalid()) + if (Result.isInvalid()) return ExprError(); - + if (!getDerived().AlwaysRebuild() && TSInfo == E->getTypeInfoAsWritten() && Result.get() == E->getSubExpr()) return SemaRef.Owned(E); - + return SemaRef.BuildObjCBridgedCast(E->getLParenLoc(), E->getBridgeKind(), - E->getBridgeKeywordLoc(), TSInfo, + E->getBridgeKeywordLoc(), TSInfo, Result.get()); } @@ -8557,17 +8585,17 @@ TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) { bool ArgChanged = false; ASTOwningVector<Expr*> Args(SemaRef); Args.reserve(E->getNumArgs()); - if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), false, Args, + if (getDerived().TransformExprs(E->getArgs(), E->getNumArgs(), false, Args, &ArgChanged)) return ExprError(); - + if (E->getReceiverKind() == ObjCMessageExpr::Class) { // Class message: transform the receiver type. TypeSourceInfo *ReceiverTypeInfo = getDerived().TransformType(E->getClassReceiverTypeInfo()); if (!ReceiverTypeInfo) return ExprError(); - + // If nothing changed, just retain the existing message send. if (!getDerived().AlwaysRebuild() && ReceiverTypeInfo == E->getClassReceiverTypeInfo() && !ArgChanged) @@ -8597,7 +8625,7 @@ TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) { if (!getDerived().AlwaysRebuild() && Receiver.get() == E->getInstanceReceiver() && !ArgChanged) return SemaRef.MaybeBindToTemporary(E); - + // Build a new instance message send. SmallVector<SourceLocation, 16> SelLocs; E->getSelectorLocs(SelLocs); @@ -8631,12 +8659,12 @@ TreeTransform<Derived>::TransformObjCIvarRefExpr(ObjCIvarRefExpr *E) { return ExprError(); // We don't need to transform the ivar; it will never change. - + // If nothing changed, just retain the existing expression. if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase()) return SemaRef.Owned(E); - + return getDerived().RebuildObjCIvarRefExpr(Base.get(), E->getDecl(), E->getLocation(), E->isArrow(), E->isFreeIvar()); @@ -8649,14 +8677,14 @@ TreeTransform<Derived>::TransformObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { // retain the existing expression. if (!E->isObjectReceiver()) return SemaRef.Owned(E); - + // Transform the base expression. ExprResult Base = getDerived().TransformExpr(E->getBase()); if (Base.isInvalid()) return ExprError(); - + // We don't need to transform the property; it will never change. - + // If nothing changed, just retain the existing expression. if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase()) @@ -8692,7 +8720,7 @@ TreeTransform<Derived>::TransformObjCSubscriptRefExpr(ObjCSubscriptRefExpr *E) { Key.get() == E->getKeyExpr() && Base.get() == E->getBaseExpr()) return SemaRef.Owned(E); - return getDerived().RebuildObjCSubscriptRefExpr(E->getRBracket(), + return getDerived().RebuildObjCSubscriptRefExpr(E->getRBracket(), Base.get(), Key.get(), E->getAtIndexMethodDecl(), E->setAtIndexMethodDecl()); @@ -8705,12 +8733,12 @@ TreeTransform<Derived>::TransformObjCIsaExpr(ObjCIsaExpr *E) { ExprResult Base = getDerived().TransformExpr(E->getBase()); if (Base.isInvalid()) return ExprError(); - + // If nothing changed, just retain the existing expression. if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase()) return SemaRef.Owned(E); - + return getDerived().RebuildObjCIsaExpr(Base.get(), E->getIsaMemberLoc(), E->isArrow()); } @@ -8721,7 +8749,7 @@ TreeTransform<Derived>::TransformShuffleVectorExpr(ShuffleVectorExpr *E) { bool ArgumentChanged = false; ASTOwningVector<Expr*> SubExprs(SemaRef); SubExprs.reserve(E->getNumSubExprs()); - if (getDerived().TransformExprs(E->getSubExprs(), E->getNumSubExprs(), false, + if (getDerived().TransformExprs(E->getSubExprs(), E->getNumSubExprs(), false, SubExprs, &ArgumentChanged)) return ExprError(); @@ -8738,17 +8766,17 @@ template<typename Derived> ExprResult TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) { BlockDecl *oldBlock = E->getBlockDecl(); - + SemaRef.ActOnBlockStart(E->getCaretLocation(), /*Scope=*/0); BlockScopeInfo *blockScope = SemaRef.getCurBlock(); blockScope->TheDecl->setIsVariadic(oldBlock->isVariadic()); blockScope->TheDecl->setBlockMissingReturnType( oldBlock->blockMissingReturnType()); - + SmallVector<ParmVarDecl*, 4> params; SmallVector<QualType, 4> paramTypes; - + // Parameter substitution. if (getDerived().TransformFunctionTypeParams(E->getCaretLocation(), oldBlock->param_begin(), @@ -8764,8 +8792,8 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) { // Don't allow returning a objc interface by value. if (exprResultType->isObjCObjectType()) { - getSema().Diag(E->getCaretLocation(), - diag::err_object_cannot_be_passed_returned_by_value) + getSema().Diag(E->getCaretLocation(), + diag::err_object_cannot_be_passed_returned_by_value) << 0 << exprResultType; getSema().ActOnBlockError(E->getCaretLocation(), /*Scope=*/0); return ExprError(); @@ -8788,7 +8816,7 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) { blockScope->HasImplicitReturnType = false; blockScope->ReturnType = exprResultType; } - + // Transform the body StmtResult body = getDerived().TransformStmt(E->getBody()); if (body.isInvalid()) { @@ -8846,7 +8874,7 @@ TreeTransform<Derived>::TransformAtomicExpr(AtomicExpr *E) { return getDerived().RebuildAtomicExpr(E->getBuiltinLoc(), move_arg(SubExprs), RetTy, E->getOp(), E->getRParenLoc()); } - + //===----------------------------------------------------------------------===// // Type reconstruction //===----------------------------------------------------------------------===// @@ -9028,7 +9056,7 @@ QualType TreeTransform<Derived>::RebuildUnresolvedUsingType(Decl *D) { // A valid resolved using typename decl points to exactly one type decl. assert(++Using->shadow_begin() == Using->shadow_end()); Ty = cast<TypeDecl>((*Using->shadow_begin())->getTargetDecl()); - + } else { assert(isa<UnresolvedUsingTypenameDecl>(D) && "UnresolvedUsingTypenameDecl transformed to non-using decl"); @@ -9123,7 +9151,7 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS, Template); return Template.template getAsVal<TemplateName>(); } - + template<typename Derived> ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op, @@ -9223,7 +9251,7 @@ TreeTransform<Derived>::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op, } template<typename Derived> -ExprResult +ExprResult TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base, SourceLocation OperatorLoc, bool isArrow, @@ -9235,7 +9263,7 @@ TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base, QualType BaseType = Base->getType(); if (Base->isTypeDependent() || Destroyed.getIdentifier() || (!isArrow && !BaseType->getAs<RecordType>()) || - (isArrow && BaseType->getAs<PointerType>() && + (isArrow && BaseType->getAs<PointerType>() && !BaseType->getAs<PointerType>()->getPointeeType() ->template getAs<RecordType>())){ // This pseudo-destructor expression is still a pseudo-destructor. @@ -9252,7 +9280,11 @@ TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base, DeclarationNameInfo NameInfo(Name, Destroyed.getLocation()); NameInfo.setNamedTypeInfo(DestroyedType); - // FIXME: the ScopeType should be tacked onto SS. + // The scope type is now known to be a valid nested name specifier + // component. Tack it on to the end of the nested name specifier. + if (ScopeType) + SS.Extend(SemaRef.Context, SourceLocation(), + ScopeType->getTypeLoc(), CCLoc); SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller. return getSema().BuildMemberReferenceExpr(Base, BaseType, diff --git a/lib/Serialization/ASTCommon.h b/lib/Serialization/ASTCommon.h index 16db8e36952f..eacb39d86ea4 100644 --- a/lib/Serialization/ASTCommon.h +++ b/lib/Serialization/ASTCommon.h @@ -50,6 +50,8 @@ TypeID MakeTypeID(ASTContext &Context, QualType T, IdxForTypeTy IdxForType) { return TypeIdx(PREDEF_TYPE_AUTO_DEDUCT).asTypeID(FastQuals); if (T == Context.AutoRRefDeductTy) return TypeIdx(PREDEF_TYPE_AUTO_RREF_DEDUCT).asTypeID(FastQuals); + if (T == Context.VaListTagTy) + return TypeIdx(PREDEF_TYPE_VA_LIST_TAG).asTypeID(FastQuals); return IdxForType(T).asTypeID(FastQuals); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index fd0c17139468..beef338174f2 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -90,6 +90,12 @@ PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts) { #define BENIGN_LANGOPT(Name, Bits, Default, Description) #define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) #include "clang/Basic/LangOptions.def" + + if (PPLangOpts.ObjCRuntime != LangOpts.ObjCRuntime) { + Reader.Diag(diag::err_pch_langopt_value_mismatch) + << "target Objective-C runtime"; + return true; + } return false; } @@ -829,7 +835,7 @@ bool ASTReader::ParseLineTable(ModuleFile &F, Entries.push_back(LineEntry::get(FileOffset, LineNo, FilenameID, FileKind, IncludeOffset)); } - LineTable.AddEntry(FID, Entries); + LineTable.AddEntry(FileID::get(FID), Entries); } return false; @@ -1121,8 +1127,9 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(int ID) { // This is the module's main file. IncludeLoc = getImportLocation(F); } - FileID FID = SourceMgr.createFileID(File, IncludeLoc, - (SrcMgr::CharacteristicKind)Record[2], + SrcMgr::CharacteristicKind + FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; + FileID FID = SourceMgr.createFileID(File, IncludeLoc, FileCharacter, ID, BaseOffset + Record[0]); SrcMgr::FileInfo &FileInfo = const_cast<SrcMgr::FileInfo&>(SourceMgr.getSLocEntry(FID).getFile()); @@ -1139,7 +1146,8 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(int ID) { } const SrcMgr::ContentCache *ContentCache - = SourceMgr.getOrCreateContentCache(File); + = SourceMgr.getOrCreateContentCache(File, + /*isSystemFile=*/FileCharacter != SrcMgr::C_User); if (OverriddenBuffer && !ContentCache->BufferOverridden && ContentCache->ContentsEntry == ContentCache->OrigEntry) { unsigned Code = SLocEntryCursor.ReadCode(); @@ -1737,6 +1745,17 @@ ASTReader::ReadASTBlock(ModuleFile &F) { } break; + case COMMENTS_BLOCK_ID: { + llvm::BitstreamCursor C = Stream; + if (Stream.SkipBlock() || + ReadBlockAbbrevs(C, COMMENTS_BLOCK_ID)) { + Error("malformed comments block in AST file"); + return Failure; + } + CommentsCursors.push_back(std::make_pair(C, &F)); + break; + } + default: if (!Stream.SkipBlock()) break; @@ -2473,6 +2492,26 @@ ASTReader::ASTReadResult ASTReader::validateFileEntries(ModuleFile &M) { Error("source location entry is incorrect"); return Failure; } + + off_t StoredSize = (off_t)Record[4]; + time_t StoredTime = (time_t)Record[5]; + + // Check if there was a request to override the contents of the file + // that was part of the precompiled header. Overridding such a file + // can lead to problems when lexing using the source locations from the + // PCH. + SourceManager &SM = getSourceManager(); + if (SM.isFileOverridden(File)) { + Error(diag::err_fe_pch_file_overridden, Filename); + // After emitting the diagnostic, recover by disabling the override so + // that the original file will be used. + SM.disableFileContentsOverride(File); + // The FileEntry is a virtual file entry with the size of the contents + // that would override the original contents. Set it to the original's + // size/time. + FileMgr.modifyFileEntry(const_cast<FileEntry*>(File), + StoredSize, StoredTime); + } // The stat info from the FileEntry came from the cached stat // info of the PCH, so we cannot trust it. @@ -2482,12 +2521,12 @@ ASTReader::ASTReadResult ASTReader::validateFileEntries(ModuleFile &M) { StatBuf.st_mtime = File->getModificationTime(); } - if (((off_t)Record[4] != StatBuf.st_size + if ((StoredSize != StatBuf.st_size #if !defined(LLVM_ON_WIN32) // In our regression testing, the Windows file system seems to // have inconsistent modification times that sometimes // erroneously trigger this error-handling path. - || (time_t)Record[5] != StatBuf.st_mtime + || StoredTime != StatBuf.st_mtime #endif )) { Error(diag::err_fe_pch_file_modified, Filename); @@ -2831,11 +2870,6 @@ void ASTReader::InitializeContext() { // Load the special types. if (SpecialTypes.size() >= NumSpecialTypeIDs) { - if (Context.getBuiltinVaListType().isNull()) { - Context.setBuiltinVaListType( - GetType(SpecialTypes[SPECIAL_TYPE_BUILTIN_VA_LIST])); - } - if (unsigned String = SpecialTypes[SPECIAL_TYPE_CF_CONSTANT_STRING]) { if (!Context.CFConstantStringTypeDecl) Context.setCFConstantStringType(GetType(String)); @@ -2975,7 +3009,7 @@ std::string ASTReader::getOriginalSourceFile(const std::string &ASTFileName, OwningPtr<llvm::MemoryBuffer> Buffer; Buffer.reset(FileMgr.getBufferForFile(ASTFileName, &ErrStr)); if (!Buffer) { - Diags.Report(diag::err_fe_unable_to_read_pch_file) << ErrStr; + Diags.Report(diag::err_fe_unable_to_read_pch_file) << ASTFileName << ErrStr; return std::string(); } @@ -3297,8 +3331,7 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { /// them to the AST listener if one is set. /// /// \returns true if the listener deems the file unacceptable, false otherwise. -bool ASTReader::ParseLanguageOptions( - const SmallVectorImpl<uint64_t> &Record) { +bool ASTReader::ParseLanguageOptions(const RecordData &Record) { if (Listener) { LangOptions LangOpts; unsigned Idx = 0; @@ -3307,6 +3340,10 @@ bool ASTReader::ParseLanguageOptions( #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ LangOpts.set##Name(static_cast<LangOptions::Type>(Record[Idx++])); #include "clang/Basic/LangOptions.def" + + ObjCRuntime::Kind runtimeKind = (ObjCRuntime::Kind) Record[Idx++]; + VersionTuple runtimeVersion = ReadVersionTuple(Record, Idx); + LangOpts.ObjCRuntime = ObjCRuntime(runtimeKind, runtimeVersion); unsigned Length = Record[Idx++]; LangOpts.CurrentModule.assign(Record.begin() + Idx, @@ -3869,6 +3906,8 @@ QualType ASTReader::readTypeRecord(unsigned Index) { } else if (EST == EST_Uninstantiated) { EPI.ExceptionSpecDecl = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx); EPI.ExceptionSpecTemplate = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx); + } else if (EST == EST_Unevaluated) { + EPI.ExceptionSpecDecl = ReadDeclAs<FunctionDecl>(*Loc.F, Record, Idx); } return Context.getFunctionType(ResultType, ParamTypes.data(), NumParams, EPI); @@ -4124,7 +4163,6 @@ QualType ASTReader::readTypeRecord(unsigned Index) { class clang::TypeLocReader : public TypeLocVisitor<TypeLocReader> { ASTReader &Reader; ModuleFile &F; - llvm::BitstreamCursor &DeclsCursor; const ASTReader::RecordData &Record; unsigned &Idx; @@ -4141,7 +4179,7 @@ class clang::TypeLocReader : public TypeLocVisitor<TypeLocReader> { public: TypeLocReader(ASTReader &Reader, ModuleFile &F, const ASTReader::RecordData &Record, unsigned &Idx) - : Reader(Reader), F(F), DeclsCursor(F.DeclsCursor), Record(Record), Idx(Idx) + : Reader(Reader), F(F), Record(Record), Idx(Idx) { } // We want compile-time assurance that we've enumerated all of @@ -4427,6 +4465,9 @@ QualType ASTReader::GetType(TypeID ID) { T = Context.ARCUnbridgedCastTy; break; + case PREDEF_TYPE_VA_LIST_TAG: + T = Context.getVaListTagType(); + break; } assert(!T.isNull() && "Unknown predefined type"); @@ -4627,13 +4668,18 @@ Decl *ASTReader::GetDecl(DeclID ID) { case PREDEF_DECL_OBJC_INSTANCETYPE_ID: return Context.getObjCInstanceTypeDecl(); + + case PREDEF_DECL_BUILTIN_VA_LIST_ID: + return Context.getBuiltinVaListDecl(); } } unsigned Index = ID - NUM_PREDEF_DECL_IDS; if (Index >= DeclsLoaded.size()) { + assert(0 && "declaration ID out-of-range for AST file"); Error("declaration ID out-of-range for AST file"); + return 0; } if (!DeclsLoaded[Index]) { @@ -4839,7 +4885,6 @@ namespace { class DeclContextNameLookupVisitor { ASTReader &Reader; llvm::SmallVectorImpl<const DeclContext *> &Contexts; - const DeclContext *DC; DeclarationName Name; SmallVectorImpl<NamedDecl *> &Decls; @@ -4941,7 +4986,6 @@ namespace { class DeclContextAllNamesVisitor { ASTReader &Reader; llvm::SmallVectorImpl<const DeclContext *> &Contexts; - const DeclContext *DC; llvm::DenseMap<DeclarationName, SmallVector<NamedDecl *, 8> > &Decls; public: @@ -5025,6 +5069,7 @@ void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) { I = Decls.begin(), E = Decls.end(); I != E; ++I) { SetExternalVisibleDeclsForName(DC, I->first, I->second); } + const_cast<DeclContext *>(DC)->setHasExternalVisibleStorage(false); } /// \brief Under non-PCH compilation the consumer receives the objc methods @@ -5887,7 +5932,7 @@ ASTReader::ReadTemplateArgument(ModuleFile &F, case TemplateArgument::Integral: { llvm::APSInt Value = ReadAPSInt(Record, Idx); QualType T = readType(F, Record, Idx); - return TemplateArgument(Value, T); + return TemplateArgument(Context, Value, T); } case TemplateArgument::Template: return TemplateArgument(ReadTemplateName(F, Record, Idx)); @@ -6227,18 +6272,72 @@ IdentifierTable &ASTReader::getIdentifierTable() { /// \brief Record that the given ID maps to the given switch-case /// statement. void ASTReader::RecordSwitchCaseID(SwitchCase *SC, unsigned ID) { - assert(SwitchCaseStmts[ID] == 0 && "Already have a SwitchCase with this ID"); - SwitchCaseStmts[ID] = SC; + assert((*CurrSwitchCaseStmts)[ID] == 0 && + "Already have a SwitchCase with this ID"); + (*CurrSwitchCaseStmts)[ID] = SC; } /// \brief Retrieve the switch-case statement with the given ID. SwitchCase *ASTReader::getSwitchCaseWithID(unsigned ID) { - assert(SwitchCaseStmts[ID] != 0 && "No SwitchCase with this ID"); - return SwitchCaseStmts[ID]; + assert((*CurrSwitchCaseStmts)[ID] != 0 && "No SwitchCase with this ID"); + return (*CurrSwitchCaseStmts)[ID]; } void ASTReader::ClearSwitchCaseIDs() { - SwitchCaseStmts.clear(); + CurrSwitchCaseStmts->clear(); +} + +void ASTReader::ReadComments() { + std::vector<RawComment *> Comments; + for (SmallVectorImpl<std::pair<llvm::BitstreamCursor, + serialization::ModuleFile *> >::iterator + I = CommentsCursors.begin(), + E = CommentsCursors.end(); + I != E; ++I) { + llvm::BitstreamCursor &Cursor = I->first; + serialization::ModuleFile &F = *I->second; + SavedStreamPosition SavedPosition(Cursor); + + RecordData Record; + while (true) { + unsigned Code = Cursor.ReadCode(); + if (Code == llvm::bitc::END_BLOCK) + break; + + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + // No known subblocks, always skip them. + Cursor.ReadSubBlockID(); + if (Cursor.SkipBlock()) { + Error("malformed block record in AST file"); + return; + } + continue; + } + + if (Code == llvm::bitc::DEFINE_ABBREV) { + Cursor.ReadAbbrevRecord(); + continue; + } + + // Read a record. + Record.clear(); + switch ((CommentRecordTypes) Cursor.ReadRecord(Code, Record)) { + case COMMENTS_RAW_COMMENT: { + unsigned Idx = 0; + SourceRange SR = ReadSourceRange(F, Record, Idx); + RawComment::CommentKind Kind = + (RawComment::CommentKind) Record[Idx++]; + bool IsTrailingComment = Record[Idx++]; + bool IsAlmostTrailingComment = Record[Idx++]; + Comments.push_back(new (Context) RawComment(SR, Kind, + IsTrailingComment, + IsAlmostTrailingComment)); + break; + } + } + } + } + Context.Comments.addCommentsToFront(Comments); } void ASTReader::finishPendingActions() { @@ -6353,7 +6452,8 @@ ASTReader::ASTReader(Preprocessor &PP, ASTContext &Context, DisableValidation(DisableValidation), DisableStatCache(DisableStatCache), AllowASTWithCompilerErrors(AllowASTWithCompilerErrors), - CurrentGeneration(0), NumStatHits(0), NumStatMisses(0), + CurrentGeneration(0), CurrSwitchCaseStmts(&SwitchCaseStmts), + NumStatHits(0), NumStatMisses(0), NumSLocEntriesRead(0), TotalNumSLocEntries(0), NumStatementsRead(0), TotalNumStatements(0), NumMacrosRead(0), TotalNumMacros(0), NumSelectorsRead(0), NumMethodPoolEntriesRead(0), diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 8dd53ee7d206..cb21f82600e0 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -25,6 +25,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace clang::serialization; @@ -629,6 +630,10 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { if (Record[Idx++]) { // In practice, this won't be executed (since method definitions // don't occur in header files). + // Switch case IDs for this method body. + ASTReader::SwitchCaseMapTy SwitchCaseStmtsForObjCMethod; + SaveAndRestore<ASTReader::SwitchCaseMapTy *> + SCFOM(Reader.CurrSwitchCaseStmts, &SwitchCaseStmtsForObjCMethod); MD->setBody(Reader.ReadStmt(F)); MD->setSelfDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx)); MD->setCmdDecl(ReadDeclAs<ImplicitParamDecl>(Record, Idx)); @@ -637,6 +642,7 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { MD->setVariadic(Record[Idx++]); MD->setSynthesized(Record[Idx++]); MD->setDefined(Record[Idx++]); + MD->IsOverriding = Record[Idx++]; MD->IsRedeclaration = Record[Idx++]; MD->HasRedeclaration = Record[Idx++]; @@ -649,7 +655,7 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { MD->SetRelatedResultType(Record[Idx++]); MD->setResultType(Reader.readType(F, Record, Idx)); MD->setResultTypeSourceInfo(GetTypeSourceInfo(Record, Idx)); - MD->setEndLoc(ReadSourceLocation(Record, Idx)); + MD->DeclEndLoc = ReadSourceLocation(Record, Idx); unsigned NumParams = Record[Idx++]; SmallVector<ParmVarDecl *, 16> Params; Params.reserve(NumParams); @@ -797,7 +803,6 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { ProtoLocs.push_back(ReadSourceLocation(Record, Idx)); CD->setProtocolList(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(), Reader.getContext()); - CD->setHasSynthBitfield(Record[Idx++]); } void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { @@ -843,7 +848,6 @@ void ASTDeclReader::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { D->setIvarRBraceLoc(ReadSourceLocation(Record, Idx)); llvm::tie(D->IvarInitializers, D->NumIvarInitializers) = Reader.ReadCXXCtorInitializers(F, Record, Idx); - D->setHasSynthBitfield(Record[Idx++]); } @@ -859,12 +863,11 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) { VisitDeclaratorDecl(FD); - FD->setMutable(Record[Idx++]); - int BitWidthOrInitializer = Record[Idx++]; - if (BitWidthOrInitializer == 1) - FD->setBitWidth(Reader.ReadExpr(F)); - else if (BitWidthOrInitializer == 2) - FD->setInClassInitializer(Reader.ReadExpr(F)); + FD->Mutable = Record[Idx++]; + if (int BitWidthOrInitializer = Record[Idx++]) { + FD->InitializerOrBitWidth.setInt(BitWidthOrInitializer - 1); + FD->InitializerOrBitWidth.setPointer(Reader.ReadExpr(F)); + } if (!FD->getDeclName()) { if (FieldDecl *Tmpl = ReadDeclAs<FieldDecl>(Record, Idx)) Reader.getContext().setInstantiatedFromUnnamedFieldDecl(FD, Tmpl); @@ -1085,14 +1088,11 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasPublicFields = Record[Idx++]; Data.HasMutableFields = Record[Idx++]; Data.HasOnlyCMembers = Record[Idx++]; + Data.HasInClassInitializer = Record[Idx++]; Data.HasTrivialDefaultConstructor = Record[Idx++]; Data.HasConstexprNonCopyMoveConstructor = Record[Idx++]; Data.DefaultedDefaultConstructorIsConstexpr = Record[Idx++]; - Data.DefaultedCopyConstructorIsConstexpr = Record[Idx++]; - Data.DefaultedMoveConstructorIsConstexpr = Record[Idx++]; Data.HasConstexprDefaultConstructor = Record[Idx++]; - Data.HasConstexprCopyConstructor = Record[Idx++]; - Data.HasConstexprMoveConstructor = Record[Idx++]; Data.HasTrivialCopyConstructor = Record[Idx++]; Data.HasTrivialMoveConstructor = Record[Idx++]; Data.HasTrivialCopyAssignment = Record[Idx++]; @@ -1242,7 +1242,6 @@ void ASTDeclReader::VisitImportDecl(ImportDecl *D) { SourceLocation *StoredLocs = reinterpret_cast<SourceLocation *>(D + 1); for (unsigned I = 0, N = Record.back(); I != N; ++I) StoredLocs[I] = ReadSourceLocation(Record, Idx); - ++Idx; } void ASTDeclReader::VisitAccessSpecDecl(AccessSpecDecl *D) { @@ -1500,7 +1499,8 @@ void ASTDeclReader::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { VisitDecl(D); - D->AssertExpr = Reader.ReadExpr(F); + D->AssertExprAndFailed.setPointer(Reader.ReadExpr(F)); + D->AssertExprAndFailed.setInt(Record[Idx++]); D->Message = cast<StringLiteral>(Reader.ReadExpr(F)); D->RParenLoc = ReadSourceLocation(Record, Idx); } @@ -1528,7 +1528,7 @@ ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) { // We temporarily set the first (canonical) declaration as the previous one // which is the one that matters and mark the real previous DeclID to be // loaded & attached later on. - D->RedeclLink = typename Redeclarable<T>::PreviousDeclLink(FirstDecl); + D->RedeclLink = Redeclarable<T>::PreviousDeclLink(FirstDecl); } // Note that this declaration has been deserialized. @@ -1556,8 +1556,7 @@ void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D, // Have our redeclaration link point back at the canonical declaration // of the existing declaration, so that this declaration has the // appropriate canonical declaration. - D->RedeclLink - = typename Redeclarable<T>::PreviousDeclLink(ExistingCanon); + D->RedeclLink = Redeclarable<T>::PreviousDeclLink(ExistingCanon); // When we merge a namespace, update its pointer to the first namespace. if (NamespaceDecl *Namespace @@ -1799,22 +1798,22 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) { void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) { assert(D && previous); if (TagDecl *TD = dyn_cast<TagDecl>(D)) { - TD->RedeclLink.setPointer(cast<TagDecl>(previous)); + TD->RedeclLink.setNext(cast<TagDecl>(previous)); } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - FD->RedeclLink.setPointer(cast<FunctionDecl>(previous)); + FD->RedeclLink.setNext(cast<FunctionDecl>(previous)); } else if (VarDecl *VD = dyn_cast<VarDecl>(D)) { - VD->RedeclLink.setPointer(cast<VarDecl>(previous)); + VD->RedeclLink.setNext(cast<VarDecl>(previous)); } else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { - TD->RedeclLink.setPointer(cast<TypedefNameDecl>(previous)); + TD->RedeclLink.setNext(cast<TypedefNameDecl>(previous)); } else if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) { - ID->RedeclLink.setPointer(cast<ObjCInterfaceDecl>(previous)); + ID->RedeclLink.setNext(cast<ObjCInterfaceDecl>(previous)); } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { - PD->RedeclLink.setPointer(cast<ObjCProtocolDecl>(previous)); + PD->RedeclLink.setNext(cast<ObjCProtocolDecl>(previous)); } else if (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D)) { - ND->RedeclLink.setPointer(cast<NamespaceDecl>(previous)); + ND->RedeclLink.setNext(cast<NamespaceDecl>(previous)); } else { RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D); - TD->RedeclLink.setPointer(cast<RedeclarableTemplateDecl>(previous)); + TD->RedeclLink.setNext(cast<RedeclarableTemplateDecl>(previous)); } } diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 007ecee9f53f..c5325b5f7837 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTReader.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" @@ -161,9 +162,13 @@ void ASTStmtReader::VisitLabelStmt(LabelStmt *S) { void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) { VisitStmt(S); + uint64_t NumAttrs = Record[Idx++]; AttrVec Attrs; Reader.ReadAttributes(F, Attrs, Record, Idx); - S->Attrs = Attrs; + (void)NumAttrs; + assert(NumAttrs == S->NumAttrs); + assert(NumAttrs == Attrs.size()); + std::copy(Attrs.begin(), Attrs.end(), S->Attrs); S->SubStmt = Reader.ReadSubStmt(); S->AttrLoc = ReadSourceLocation(Record, Idx); } @@ -317,6 +322,11 @@ void ASTStmtReader::VisitAsmStmt(AsmStmt *S) { Clobbers.data(), NumClobbers); } +void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) { + // FIXME: Statement reader not yet implemented for MS style inline asm. + VisitStmt(S); +} + void ASTStmtReader::VisitExpr(Expr *E) { VisitStmt(E); E->setType(Reader.readType(F, Record, Idx)); @@ -816,12 +826,12 @@ void ASTStmtReader::VisitObjCStringLiteral(ObjCStringLiteral *E) { E->setAtLoc(ReadSourceLocation(Record, Idx)); } -void ASTStmtReader::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void ASTStmtReader::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { VisitExpr(E); // could be one of several IntegerLiteral, FloatLiteral, etc. - E->Number = Reader.ReadSubStmt(); - E->ObjCNumericLiteralMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); - E->AtLoc = ReadSourceLocation(Record, Idx); + E->SubExpr = Reader.ReadSubStmt(); + E->BoxingMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->Range = ReadSourceRange(Record, Idx); } void ASTStmtReader::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { @@ -873,6 +883,7 @@ void ASTStmtReader::VisitObjCProtocolExpr(ObjCProtocolExpr *E) { VisitExpr(E); E->setProtocol(ReadDeclAs<ObjCProtocolDecl>(Record, Idx)); E->setAtLoc(ReadSourceLocation(Record, Idx)); + E->ProtoLoc = ReadSourceLocation(Record, Idx); E->setRParenLoc(ReadSourceLocation(Record, Idx)); } @@ -1074,7 +1085,8 @@ void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); - E->setOperator((OverloadedOperatorKind)Record[Idx++]); + E->Operator = (OverloadedOperatorKind)Record[Idx++]; + E->Range = Reader.ReadSourceRange(F, Record, Idx); } void ASTStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) { @@ -1640,7 +1652,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; case STMT_ATTRIBUTED: - S = new (Context) AttributedStmt(Empty); + S = AttributedStmt::CreateEmpty( + Context, + /*NumAttrs*/Record[ASTStmtReader::NumStmtFields]); break; case STMT_IF: @@ -1888,8 +1902,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_OBJC_STRING_LITERAL: S = new (Context) ObjCStringLiteral(Empty); break; - case EXPR_OBJC_NUMERIC_LITERAL: - S = new (Context) ObjCNumericLiteral(Empty); + case EXPR_OBJC_BOXED_EXPRESSION: + S = new (Context) ObjCBoxedExpr(Empty); break; case EXPR_OBJC_ARRAY_LITERAL: S = ObjCArrayLiteral::CreateEmpty(Context, diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 36933a9d9b8c..b7718c4655b4 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -198,6 +198,8 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { } else if (T->getExceptionSpecType() == EST_Uninstantiated) { Writer.AddDeclRef(T->getExceptionSpecDecl(), Record); Writer.AddDeclRef(T->getExceptionSpecTemplate(), Record); + } else if (T->getExceptionSpecType() == EST_Unevaluated) { + Writer.AddDeclRef(T->getExceptionSpecDecl(), Record); } Code = TYPE_FUNCTION_PROTO; } @@ -699,7 +701,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(EXPR_BLOCK); RECORD(EXPR_GENERIC_SELECTION); RECORD(EXPR_OBJC_STRING_LITERAL); - RECORD(EXPR_OBJC_NUMERIC_LITERAL); + RECORD(EXPR_OBJC_BOXED_EXPRESSION); RECORD(EXPR_OBJC_ARRAY_LITERAL); RECORD(EXPR_OBJC_DICTIONARY_LITERAL); RECORD(EXPR_OBJC_ENCODE); @@ -1081,6 +1083,9 @@ void ASTWriter::WriteLanguageOptions(const LangOptions &LangOpts) { #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ Record.push_back(static_cast<unsigned>(LangOpts.get##Name())); #include "clang/Basic/LangOptions.def" + + Record.push_back((unsigned) LangOpts.ObjCRuntime.getKind()); + AddVersionTuple(LangOpts.ObjCRuntime.getVersion(), Record); Record.push_back(LangOpts.CurrentModule.size()); Record.append(LangOpts.CurrentModule.begin(), LangOpts.CurrentModule.end()); @@ -1242,15 +1247,14 @@ namespace { // Trait used for the on-disk hash table of header search information. class HeaderFileInfoTrait { ASTWriter &Writer; - const HeaderSearch &HS; // Keep track of the framework names we've used during serialization. SmallVector<char, 128> FrameworkStringData; llvm::StringMap<unsigned> FrameworkNameOffset; public: - HeaderFileInfoTrait(ASTWriter &Writer, const HeaderSearch &HS) - : Writer(Writer), HS(HS) { } + HeaderFileInfoTrait(ASTWriter &Writer) + : Writer(Writer) { } typedef const char *key_type; typedef key_type key_type_ref; @@ -1335,7 +1339,7 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS, StringRef isysroot) { if (FilesByUID.size() > HS.header_file_size()) FilesByUID.resize(HS.header_file_size()); - HeaderFileInfoTrait GeneratorTrait(*this, HS); + HeaderFileInfoTrait GeneratorTrait(*this); OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator; SmallVector<const char *, 4> SavedStrings; unsigned NumHeaderSearchEntries = 0; @@ -1605,11 +1609,11 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, for (LineTableInfo::iterator L = LineTable.begin(), LEnd = LineTable.end(); L != LEnd; ++L) { // Only emit entries for local files. - if (L->first < 0) + if (L->first.ID < 0) continue; // Emit the file ID - Record.push_back(L->first); + Record.push_back(L->first.ID); // Emit the line entries Record.push_back(L->second.size()); @@ -2241,6 +2245,23 @@ void ASTWriter::WriteFileDeclIDsMap() { Stream.EmitRecordWithBlob(AbbrevCode, Record, data(FileSortedIDs)); } +void ASTWriter::WriteComments() { + Stream.EnterSubblock(COMMENTS_BLOCK_ID, 3); + ArrayRef<RawComment *> RawComments = Context->Comments.getComments(); + RecordData Record; + for (ArrayRef<RawComment *>::iterator I = RawComments.begin(), + E = RawComments.end(); + I != E; ++I) { + Record.clear(); + AddSourceRange((*I)->getSourceRange(), Record); + Record.push_back((*I)->getKind()); + Record.push_back((*I)->isTrailingComment()); + Record.push_back((*I)->isAlmostTrailingComment()); + Stream.EmitRecord(COMMENTS_RAW_COMMENT, Record); + } + Stream.ExitBlock(); +} + //===----------------------------------------------------------------------===// // Global Method Pool and Selector Serialization //===----------------------------------------------------------------------===// @@ -3067,10 +3088,12 @@ void ASTWriter::WriteMergedDecls() { //===----------------------------------------------------------------------===// /// \brief Write a record containing the given attributes. -void ASTWriter::WriteAttributes(const AttrVec &Attrs, RecordDataImpl &Record) { +void ASTWriter::WriteAttributes(ArrayRef<const Attr*> Attrs, + RecordDataImpl &Record) { Record.push_back(Attrs.size()); - for (AttrVec::const_iterator i = Attrs.begin(), e = Attrs.end(); i != e; ++i){ - const Attr * A = *i; + for (ArrayRef<const Attr *>::iterator i = Attrs.begin(), + e = Attrs.end(); i != e; ++i){ + const Attr *A = *i; Record.push_back(A->getKind()); // FIXME: stable encoding, target attrs AddSourceRange(A->getRange(), Record); @@ -3121,7 +3144,8 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) { ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream) : Stream(Stream), Context(0), PP(0), Chain(0), WritingModule(0), - WritingAST(false), ASTHasCompilerErrors(false), + WritingAST(false), DoneWritingDeclsAndTypes(false), + ASTHasCompilerErrors(false), FirstDeclID(NUM_PREDEF_DECL_IDS), NextDeclID(FirstDeclID), FirstTypeID(NUM_PREDEF_TYPE_IDS), NextTypeID(FirstTypeID), FirstIdentID(NUM_PREDEF_IDENT_IDS), NextIdentID(FirstIdentID), @@ -3213,7 +3237,9 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, DeclIDs[Context.UInt128Decl] = PREDEF_DECL_UNSIGNED_INT_128_ID; if (Context.ObjCInstanceTypeDecl) DeclIDs[Context.ObjCInstanceTypeDecl] = PREDEF_DECL_OBJC_INSTANCETYPE_ID; - + if (Context.BuiltinVaListDecl) + DeclIDs[Context.getBuiltinVaListDecl()] = PREDEF_DECL_BUILTIN_VA_LIST_ID; + if (!Chain) { // Make sure that we emit IdentifierInfos (and any attached // declarations) for builtins. We don't need to do this when we're @@ -3379,13 +3405,20 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, Record.push_back(reinterpret_cast<uint64_t>(NS)); } } - + + // Make sure visible decls, added to DeclContexts previously loaded from + // an AST file, are registered for serialization. + for (SmallVector<const Decl *, 16>::iterator + I = UpdatingVisibleDecls.begin(), + E = UpdatingVisibleDecls.end(); I != E; ++I) { + GetDeclRef(*I); + } + // Resolve any declaration pointers within the declaration updates block. ResolveDeclUpdatesBlocks(); // Form the record of special types. RecordData SpecialTypes; - AddTypeRef(Context.getBuiltinVaListType(), SpecialTypes); AddTypeRef(Context.getRawCFConstantStringType(), SpecialTypes); AddTypeRef(Context.getFILEType(), SpecialTypes); AddTypeRef(Context.getjmp_bufType(), SpecialTypes); @@ -3413,8 +3446,11 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, } Stream.ExitBlock(); + DoneWritingDeclsAndTypes = true; + WriteFileDeclIDsMap(); WriteSourceManagerBlock(Context.getSourceManager(), PP, isysroot); + WriteComments(); if (Chain) { // Write the mapping information describing our module dependencies and how @@ -3798,6 +3834,11 @@ TypeIdx ASTWriter::GetOrCreateTypeIdx(QualType T) { TypeIdx &Idx = TypeIdxs[T]; if (Idx.getIndex() == 0) { + if (DoneWritingDeclsAndTypes) { + assert(0 && "New type seen after serializing all the types to emit!"); + return TypeIdx(); + } + // We haven't seen this type before. Assign it a new ID and put it // into the queue of types to emit. Idx = TypeIdx(NextTypeID++); @@ -3835,6 +3876,11 @@ DeclID ASTWriter::GetDeclRef(const Decl *D) { assert(!(reinterpret_cast<uintptr_t>(D) & 0x01) && "Invalid decl pointer"); DeclID &ID = DeclIDs[D]; if (ID == 0) { + if (DoneWritingDeclsAndTypes) { + assert(0 && "New decl seen after serializing all the decls to emit!"); + return 0; + } + // We haven't seen this declaration before. Give it a new ID and // enqueue it in the list of declarations to emit. ID = NextDeclID++; @@ -4148,7 +4194,7 @@ void ASTWriter::AddTemplateArgument(const TemplateArgument &Arg, AddDeclRef(Arg.getAsDecl(), Record); break; case TemplateArgument::Integral: - AddAPSInt(*Arg.getAsIntegral(), Record); + AddAPSInt(Arg.getAsIntegral(), Record); AddTypeRef(Arg.getIntegralType(), Record); break; case TemplateArgument::Template: @@ -4310,14 +4356,11 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.HasPublicFields); Record.push_back(Data.HasMutableFields); Record.push_back(Data.HasOnlyCMembers); + Record.push_back(Data.HasInClassInitializer); Record.push_back(Data.HasTrivialDefaultConstructor); Record.push_back(Data.HasConstexprNonCopyMoveConstructor); Record.push_back(Data.DefaultedDefaultConstructorIsConstexpr); - Record.push_back(Data.DefaultedCopyConstructorIsConstexpr); - Record.push_back(Data.DefaultedMoveConstructorIsConstexpr); Record.push_back(Data.HasConstexprDefaultConstructor); - Record.push_back(Data.HasConstexprCopyConstructor); - Record.push_back(Data.HasConstexprMoveConstructor); Record.push_back(Data.HasTrivialCopyConstructor); Record.push_back(Data.HasTrivialMoveConstructor); Record.push_back(Data.HasTrivialCopyAssignment); @@ -4459,6 +4502,7 @@ void ASTWriter::AddedVisibleDecl(const DeclContext *DC, const Decl *D) { return; // Not a source decl added to a DeclContext from PCH. AddUpdatedDeclContext(DC); + UpdatingVisibleDecls.push_back(D); } void ASTWriter::AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) { diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 1ee3ac480b6f..602943b9ba58 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -151,7 +151,8 @@ void ASTDeclWriter::VisitDecl(Decl *D) { Record.push_back(D->isInvalidDecl()); Record.push_back(D->hasAttrs()); if (D->hasAttrs()) - Writer.WriteAttributes(D->getAttrs(), Record); + Writer.WriteAttributes(ArrayRef<const Attr*>(D->getAttrs().begin(), + D->getAttrs().size()), Record); Record.push_back(D->isImplicit()); Record.push_back(D->isUsed(false)); Record.push_back(D->isReferenced()); @@ -417,6 +418,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { Record.push_back(D->isVariadic()); Record.push_back(D->isSynthesized()); Record.push_back(D->isDefined()); + Record.push_back(D->IsOverriding); Record.push_back(D->IsRedeclaration); Record.push_back(D->HasRedeclaration); @@ -559,7 +561,6 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { PL = D->protocol_loc_begin(), PLEnd = D->protocol_loc_end(); PL != PLEnd; ++PL) Writer.AddSourceLocation(*PL, Record); - Record.push_back(D->hasSynthBitfield()); Code = serialization::DECL_OBJC_CATEGORY; } @@ -607,7 +608,6 @@ void ASTDeclWriter::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { Writer.AddSourceLocation(D->getIvarRBraceLoc(), Record); Writer.AddCXXCtorInitializers(D->IvarInitializers, D->NumIvarInitializers, Record); - Record.push_back(D->hasSynthBitfield()); Code = serialization::DECL_OBJC_IMPLEMENTATION; } @@ -625,11 +625,13 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) { VisitDeclaratorDecl(D); Record.push_back(D->isMutable()); - Record.push_back(D->getBitWidth()? 1 : D->hasInClassInitializer() ? 2 : 0); - if (D->getBitWidth()) - Writer.AddStmt(D->getBitWidth()); - else if (D->hasInClassInitializer()) - Writer.AddStmt(D->getInClassInitializer()); + if (D->InitializerOrBitWidth.getInt() != ICIS_NoInit || + D->InitializerOrBitWidth.getPointer()) { + Record.push_back(D->InitializerOrBitWidth.getInt() + 1); + Writer.AddStmt(D->InitializerOrBitWidth.getPointer()); + } else { + Record.push_back(0); + } if (!D->getDeclName()) Writer.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D), Record); @@ -1054,7 +1056,7 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) { VisitRedeclarableTemplateDecl(D); if (D->isFirstDeclaration()) { - typedef llvm::FoldingSet<ClassTemplateSpecializationDecl> CTSDSetTy; + typedef llvm::FoldingSetVector<ClassTemplateSpecializationDecl> CTSDSetTy; CTSDSetTy &CTSDSet = D->getSpecializations(); Record.push_back(CTSDSet.size()); for (CTSDSetTy::iterator I=CTSDSet.begin(), E = CTSDSet.end(); I!=E; ++I) { @@ -1062,7 +1064,8 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) { Writer.AddDeclRef(&*I, Record); } - typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> CTPSDSetTy; + typedef llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> + CTPSDSetTy; CTPSDSetTy &CTPSDSet = D->getPartialSpecializations(); Record.push_back(CTPSDSet.size()); for (CTPSDSetTy::iterator I=CTPSDSet.begin(), E=CTPSDSet.end(); I!=E; ++I) { @@ -1146,7 +1149,7 @@ void ASTDeclWriter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { // Write the function specialization declarations. Record.push_back(D->getSpecializations().size()); - for (llvm::FoldingSet<FunctionTemplateSpecializationInfo>::iterator + for (llvm::FoldingSetVector<FunctionTemplateSpecializationInfo>::iterator I = D->getSpecializations().begin(), E = D->getSpecializations().end() ; I != E; ++I) { assert(I->Function->isCanonicalDecl() && @@ -1217,6 +1220,7 @@ void ASTDeclWriter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) { VisitDecl(D); Writer.AddStmt(D->getAssertExpr()); + Record.push_back(D->isFailed()); Writer.AddStmt(D->getMessage()); Writer.AddSourceLocation(D->getRParenLoc(), Record); Code = serialization::DECL_STATIC_ASSERT; diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 1e3121146319..f63388fa2fd1 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTWriter.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -108,6 +109,7 @@ void ASTStmtWriter::VisitLabelStmt(LabelStmt *S) { void ASTStmtWriter::VisitAttributedStmt(AttributedStmt *S) { VisitStmt(S); + Record.push_back(S->getAttrs().size()); Writer.WriteAttributes(S->getAttrs(), Record); Writer.AddStmt(S->getSubStmt()); Writer.AddSourceLocation(S->getAttrLoc(), Record); @@ -249,6 +251,11 @@ void ASTStmtWriter::VisitAsmStmt(AsmStmt *S) { Code = serialization::STMT_ASM; } +void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) { + // FIXME: Statement writer not yet implemented for MS style inline asm. + VisitStmt(S); +} + void ASTStmtWriter::VisitExpr(Expr *E) { VisitStmt(E); Writer.AddTypeRef(E->getType(), Record); @@ -777,12 +784,12 @@ void ASTStmtWriter::VisitObjCStringLiteral(ObjCStringLiteral *E) { Code = serialization::EXPR_OBJC_STRING_LITERAL; } -void ASTStmtWriter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void ASTStmtWriter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { VisitExpr(E); - Writer.AddStmt(E->getNumber()); - Writer.AddDeclRef(E->getObjCNumericLiteralMethod(), Record); - Writer.AddSourceLocation(E->getAtLoc(), Record); - Code = serialization::EXPR_OBJC_NUMERIC_LITERAL; + Writer.AddStmt(E->getSubExpr()); + Writer.AddDeclRef(E->getBoxingMethod(), Record); + Writer.AddSourceRange(E->getSourceRange(), Record); + Code = serialization::EXPR_OBJC_BOXED_EXPRESSION; } void ASTStmtWriter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { @@ -837,6 +844,7 @@ void ASTStmtWriter::VisitObjCProtocolExpr(ObjCProtocolExpr *E) { VisitExpr(E); Writer.AddDeclRef(E->getProtocol(), Record); Writer.AddSourceLocation(E->getAtLoc(), Record); + Writer.AddSourceLocation(E->ProtoLoc, Record); Writer.AddSourceLocation(E->getRParenLoc(), Record); Code = serialization::EXPR_OBJC_PROTOCOL_EXPR; } @@ -1045,6 +1053,7 @@ void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); Record.push_back(E->getOperator()); + Writer.AddSourceRange(E->Range, Record); Code = serialization::EXPR_CXX_OPERATOR_CALL; } diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index 04c5382d86c4..20999e1b5c6f 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -1,5 +1,3 @@ -set(LLVM_USED_LIBS clangSema) - add_clang_library(clangSerialization ASTCommon.h ASTReaderInternals.h @@ -18,10 +16,18 @@ add_clang_library(clangSerialization add_dependencies(clangSerialization ClangAttrClasses ClangAttrList + ClangAttrParsedAttrList ClangAttrPCHRead ClangAttrPCHWrite + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon ClangDiagnosticLex ClangDiagnosticSema ClangDiagnosticSerialization - ClangDeclNodes - ClangStmtNodes) + ClangStmtNodes + ) + +target_link_libraries(clangSerialization + clangSema + ) diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index ab66e9864fe2..c582cfc4a81b 100644 --- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -15,6 +15,7 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -23,40 +24,32 @@ using namespace ento; namespace { class AttrNonNullChecker - : public Checker< check::PreStmt<CallExpr> > { + : public Checker< check::PreCall > { mutable OwningPtr<BugType> BT; public: - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; }; } // end anonymous namespace -void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, +void AttrNonNullChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); - - // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee(), LCtx); - - const FunctionDecl *FD = X.getAsFunctionDecl(); + const Decl *FD = Call.getDecl(); if (!FD) return; - const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); + const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); if (!Att) return; - // Iterate through the arguments of CE and check them for null. - unsigned idx = 0; - - for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; - ++I, ++idx) { + ProgramStateRef state = C.getState(); + // Iterate through the arguments of CE and check them for null. + for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { if (!Att->isNonNull(idx)) continue; - SVal V = state->getSVal(*I, LCtx); + SVal V = Call.getArgSVal(idx); DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); // If the value is unknown or undefined, we can't perform this check. @@ -65,11 +58,16 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, if (!isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential - // transparent_unoin GCC extension. - QualType T = (*I)->getType(); + // transparent_union GCC extension. + const Expr *ArgE = Call.getArgExpr(idx); + if (!ArgE) + continue; + + QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; + if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); @@ -78,8 +76,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, assert(++CSV_I == CSV->end()); if (!DV) continue; - } - else { + } else { // FIXME: Handle LazyCompoundVals? continue; } @@ -106,10 +103,9 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. - const Expr *arg = *I; - R->addRange(arg->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode, - arg, R)); + R->addRange(Call.getArgSourceRange(idx)); + if (const Expr *ArgE = Call.getArgExpr(idx)) + bugreporter::addTrackNullOrUndefValueVisitor(errorNode, ArgE, R); // Emit the bug report. C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 6dd0a8c01fe6..955e79ae4661 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -17,18 +17,20 @@ #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtObjC.h" #include "clang/AST/ASTContext.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" using namespace clang; using namespace ento; @@ -44,21 +46,40 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static const char* GetReceiverNameType(const ObjCMessage &msg) { +static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) - return ID->getIdentifier()->getNameStart(); - return 0; + return ID->getIdentifier()->getName(); + return StringRef(); } -static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, - StringRef ClassName) { - if (ID->getIdentifier()->getName() == ClassName) - return true; +enum FoundationClass { + FC_None, + FC_NSArray, + FC_NSDictionary, + FC_NSEnumerator, + FC_NSOrderedSet, + FC_NSSet, + FC_NSString +}; + +static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { + static llvm::StringMap<FoundationClass> Classes; + if (Classes.empty()) { + Classes["NSArray"] = FC_NSArray; + Classes["NSDictionary"] = FC_NSDictionary; + Classes["NSEnumerator"] = FC_NSEnumerator; + Classes["NSOrderedSet"] = FC_NSOrderedSet; + Classes["NSSet"] = FC_NSSet; + Classes["NSString"] = FC_NSString; + } - if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) - return isReceiverClassOrSuperclass(Super, ClassName); + // FIXME: Should we cache this at all? + FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); + if (result == FC_None) + if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) + return findKnownClass(Super); - return false; + return result; } static inline bool isNil(SVal X) { @@ -74,15 +95,15 @@ namespace { mutable OwningPtr<APIMisuse> BT; void WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, unsigned Arg) const; + const ObjCMethodCall &msg, unsigned Arg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; }; } void NilArgChecker::WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, unsigned int Arg) const { if (!BT) @@ -91,7 +112,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(msg) << "' method '" + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; BugReport *R = new BugReport(*BT, os.str(), N); @@ -100,13 +121,13 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, } } -void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, +void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; - if (isReceiverClassOrSuperclass(ID, "NSString")) { + if (findKnownClass(ID) == FC_NSString) { Selector S = msg.getSelector(); if (S.isUnarySelector()) @@ -130,7 +151,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState()))) + if (isNil(msg.getArgSVal(0))) WarnNilArg(C, msg, 0); } } @@ -411,8 +432,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Arg, report); C.EmitReport(report); return; } @@ -434,11 +454,11 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> { mutable OwningPtr<BugType> BT; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } -void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, +void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { @@ -490,18 +510,18 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { mutable Selector initWithObjectsAndKeysS; mutable OwningPtr<BugType> BT; - bool isVariadicMessage(const ObjCMessage &msg) const; + bool isVariadicMessage(const ObjCMethodCall &msg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } /// isVariadicMessage - Returns whether the given message is a variadic message, /// where all arguments must be Objective-C types. bool -VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { - const ObjCMethodDecl *MD = msg.getMethodDecl(); +VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { + const ObjCMethodDecl *MD = msg.getDecl(); if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) return false; @@ -517,53 +537,35 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { // gains that this analysis gives. const ObjCInterfaceDecl *Class = MD->getClassInterface(); - // -[NSArray initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSArray") && - S == initWithObjectsS) - return true; - - // -[NSDictionary initWithObjectsAndKeys:] - if (isReceiverClassOrSuperclass(Class, "NSDictionary") && - S == initWithObjectsAndKeysS) - return true; - - // -[NSSet initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSSet") && - S == initWithObjectsS) - return true; - - // -[NSOrderedSet initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && - S == initWithObjectsS) - return true; + switch (findKnownClass(Class)) { + case FC_NSArray: + case FC_NSOrderedSet: + case FC_NSSet: + return S == initWithObjectsS; + case FC_NSDictionary: + return S == initWithObjectsAndKeysS; + default: + return false; + } } else { const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); - // -[NSArray arrayWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSArray") && - S == arrayWithObjectsS) - return true; - - // -[NSDictionary dictionaryWithObjectsAndKeys:] - if (isReceiverClassOrSuperclass(Class, "NSDictionary") && - S == dictionaryWithObjectsAndKeysS) - return true; - - // -[NSSet setWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSSet") && - S == setWithObjectsS) - return true; - - // -[NSOrderedSet orderedSetWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && - S == orderedSetWithObjectsS) - return true; + switch (findKnownClass(Class)) { + case FC_NSArray: + return S == arrayWithObjectsS; + case FC_NSOrderedSet: + return S == orderedSetWithObjectsS; + case FC_NSSet: + return S == setWithObjectsS; + case FC_NSDictionary: + return S == dictionaryWithObjectsAndKeysS; + default: + return false; + } } - - return false; } -void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, +void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " @@ -599,7 +601,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { - QualType ArgTy = msg.getArgType(I); + QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; @@ -608,8 +610,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Ignore pointer constants. - if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(), - state))) + if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -621,9 +622,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Generate only one error node to use for all bug reports. - if (!errorNode.hasValue()) { + if (!errorNode.hasValue()) errorNode = C.addTransition(); - } if (!errorNode.getValue()) continue; @@ -631,23 +631,93 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - if (const char *TypeName = GetReceiverNameType(msg)) + StringRef TypeName = GetReceiverInterfaceName(msg); + if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() - << "' should be an Objective-C pointer type, not '" - << ArgTy.getAsString() << "'"; + << "' should be an Objective-C pointer type, not '"; + ArgTy.print(os, C.getLangOpts()); + os << "'"; - BugReport *R = new BugReport(*BT, os.str(), - errorNode.getValue()); + BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.EmitReport(R); } } //===----------------------------------------------------------------------===// +// Improves the modeling of loops over Cocoa collections. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCLoopChecker + : public Checker<check::PostStmt<ObjCForCollectionStmt> > { + +public: + void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; +}; +} + +static bool isKnownNonNilCollectionType(QualType T) { + const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + if (!ID) + return false; + + switch (findKnownClass(ID)) { + case FC_NSArray: + case FC_NSDictionary: + case FC_NSEnumerator: + case FC_NSOrderedSet: + case FC_NSSet: + return true; + default: + return false; + } +} + +void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Check if this is the branch for the end of the loop. + SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); + if (CollectionSentinel.isZeroConstant()) + return; + + // See if the collection is one where we /know/ the elements are non-nil. + const Expr *Collection = FCS->getCollection(); + if (!isKnownNonNilCollectionType(Collection->getType())) + return; + + // FIXME: Copied from ExprEngineObjC. + const Stmt *Element = FCS->getElement(); + SVal ElementVar; + if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { + const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); + assert(ElemDecl->getInit() == 0); + ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); + } else { + ElementVar = State->getSVal(Element, C.getLocationContext()); + } + + if (!isa<Loc>(ElementVar)) + return; + + // Go ahead and assume the value is non-nil. + SVal Val = State->getSVal(cast<Loc>(ElementVar)); + State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); + C.addTransition(State); +} + + +//===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -670,3 +740,7 @@ void ento::registerClassReleaseChecker(CheckerManager &mgr) { void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { mgr.registerChecker<VariadicMethodTypeChecker>(); } + +void ento::registerObjCLoopChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCLoopChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index a377ca9e4d49..7fe51d3a4ce3 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -3,8 +3,6 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers SOURCE Checkers.td TARGET ClangSACheckers) -set(LLVM_USED_LIBS clangBasic clangAST clangStaticAnalyzerCore) - add_clang_library(clangStaticAnalyzerCheckers AdjustedReturnValueChecker.cpp AnalyzerStatsChecker.cpp @@ -31,10 +29,11 @@ add_clang_library(clangStaticAnalyzerCheckers DebugCheckers.cpp DereferenceChecker.cpp DivZeroChecker.cpp + DynamicTypePropagation.cpp + ExprInspectionChecker.cpp FixedAddressChecker.cpp GenericTaintChecker.cpp IdempotentOperationChecker.cpp - IteratorsChecker.cpp LLVMConventionsChecker.cpp MacOSKeychainAPIChecker.cpp MacOSXAPIChecker.cpp @@ -59,6 +58,7 @@ add_clang_library(clangStaticAnalyzerCheckers StackAddrEscapeChecker.cpp StreamChecker.cpp TaintTesterChecker.cpp + TraversalChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp UndefResultChecker.cpp @@ -74,7 +74,15 @@ add_dependencies(clangStaticAnalyzerCheckers clangStaticAnalyzerCore ClangAttrClasses ClangAttrList + ClangCommentNodes ClangDeclNodes + ClangDiagnosticCommon ClangStmtNodes ClangSACheckers ) + +target_link_libraries(clangStaticAnalyzerCheckers + clangBasic + clangAST + clangStaticAnalyzerCore + ) diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 9eb7edffe6df..483082a37f44 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -66,7 +66,7 @@ public: const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *) const; @@ -252,8 +252,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(S->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, S, report); C.EmitReport(report); return NULL; } @@ -901,9 +900,10 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // If the size is zero, there won't be any actual memory access, so // just bind the return value to the destination buffer and return. - if (stateZeroSize) { + if (stateZeroSize && !stateNonZeroSize) { stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); C.addTransition(stateZeroSize); + return; } // If the size can be nonzero, we have to check the other arguments. @@ -1403,6 +1403,24 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // For strncpy, this is just checking that lenVal <= sizeof(dst) // (Yes, strncpy and strncat differ in how they treat termination. // strncat ALWAYS terminates, but strncpy doesn't.) + + // We need a special case for when the copy size is zero, in which + // case strncpy will do no work at all. Our bounds check uses n-1 + // as the last element accessed, so n == 0 is problematic. + ProgramStateRef StateZeroSize, StateNonZeroSize; + llvm::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, state, *lenValNL, sizeTy); + + // If the size is known to be zero, we're done. + if (StateZeroSize && !StateNonZeroSize) { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal); + C.addTransition(StateZeroSize); + return; + } + + // Otherwise, go ahead and figure out the last element we'll touch. + // We don't record the non-zero assumption here because we can't + // be sure. We won't warn on a possible zero. NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy); @@ -1876,7 +1894,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return state; diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index f6014317c839..30f45c7685b9 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -15,8 +15,8 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" @@ -27,35 +27,37 @@ using namespace ento; namespace { class CallAndMessageChecker - : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > { + : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage, + check::PreCall > { mutable OwningPtr<BugType> BT_call_null; mutable OwningPtr<BugType> BT_call_undef; + mutable OwningPtr<BugType> BT_cxx_call_null; + mutable OwningPtr<BugType> BT_cxx_call_undef; mutable OwningPtr<BugType> BT_call_arg; mutable OwningPtr<BugType> BT_msg_undef; mutable OwningPtr<BugType> BT_objc_prop_undef; + mutable OwningPtr<BugType> BT_objc_subscript_undef; mutable OwningPtr<BugType> BT_msg_arg; mutable OwningPtr<BugType> BT_msg_ret; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg, - const char *BT_desc, OwningPtr<BugType> &BT); - static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange, - const Expr *argEx, - const bool checkUninitFields, - const char *BT_desc, - OwningPtr<BugType> &BT); - - static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, + static bool PreVisitProcessArg(CheckerContext &C, SVal V, + SourceRange argRange, const Expr *argEx, + bool IsFirstArgument, bool checkUninitFields, + const CallEvent &Call, OwningPtr<BugType> &BT); + + static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); + void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const; void HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const; + const ObjCMethodCall &msg) const; static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) { if (!BT) @@ -64,55 +66,63 @@ private: }; } // end anonymous namespace -void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, - const CallExpr *CE) { +void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, + const Expr *BadE) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *R = new BugReport(*BT, BT->getName(), N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetCalleeExpr(N), R)); + if (BadE) { + R->addRange(BadE->getSourceRange()); + bugreporter::addTrackNullOrUndefValueVisitor(N, BadE, R); + } C.EmitReport(R); } -void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, - CallOrObjCMessage callOrMsg, - const char *BT_desc, - OwningPtr<BugType> &BT) { - // Don't check for uninitialized field values in arguments if the - // caller has a body that is available and we have the chance to inline it. - // This is a hack, but is a reasonable compromise betweens sometimes warning - // and sometimes not depending on if we decide to inline a function. - const Decl *D = callOrMsg.getDecl(); - const bool checkUninitFields = - !(C.getAnalysisManager().shouldInlineCall() && - (D && D->getBody())); - - for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i) - if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i), - callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i), - checkUninitFields, - BT_desc, BT)) - return; +StringRef describeUninitializedArgumentInCall(const CallEvent &Call, + bool IsFirstArgument) { + switch (Call.getKind()) { + case CE_ObjCMessage: { + const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); + switch (Msg.getMessageKind()) { + case OCM_Message: + return "Argument in message expression is an uninitialized value"; + case OCM_PropertyAccess: + assert(Msg.isSetter() && "Getters have no args"); + return "Argument for property setter is an uninitialized value"; + case OCM_Subscript: + if (Msg.isSetter() && IsFirstArgument) + return "Argument for subscript setter is an uninitialized value"; + return "Subscript index is an uninitialized value"; + } + llvm_unreachable("Unknown message kind."); + } + case CE_Block: + return "Block call argument is an uninitialized value"; + default: + return "Function call argument is an uninitialized value"; + } } bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, - const bool checkUninitFields, - const char *BT_desc, + bool IsFirstArgument, + bool checkUninitFields, + const CallEvent &Call, OwningPtr<BugType> &BT) { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { - LazyInit_BT(BT_desc, BT); + LazyInit_BT("Uninitialized argument value", BT); // Generate a report for this bug. - BugReport *R = new BugReport(*BT, BT->getName(), N); + StringRef Desc = describeUninitializedArgumentInCall(Call, + IsFirstArgument); + BugReport *R = new BugReport(*BT, Desc, N); R->addRange(argRange); if (argEx) - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx, - R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, argEx, R); C.EmitReport(R); } return true; @@ -128,14 +138,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, public: SmallVector<const FieldDecl *, 10> FieldChain; private: - ASTContext &C; StoreManager &StoreMgr; MemRegionManager &MrMgr; Store store; public: - FindUninitializedField(ASTContext &c, StoreManager &storeMgr, + FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, Store s) - : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); @@ -146,7 +155,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, RD->field_begin(), E = RD->field_end(); I!=E; ++I) { const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); FieldChain.push_back(*I); - T = (*I)->getType(); + T = I->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; @@ -165,14 +174,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, }; const LazyCompoundValData *D = LV->getCVData(); - FindUninitializedField F(C.getASTContext(), - C.getState()->getStateManager().getStoreManager(), + FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), C.getSValBuilder().getRegionManager(), D->getStore()); if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { - LazyInit_BT(BT_desc, BT); + LazyInit_BT("Uninitialized argument value", BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; @@ -212,87 +220,137 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const{ const Expr *Callee = CE->getCallee()->IgnoreParens(); + ProgramStateRef State = C.getState(); const LocationContext *LCtx = C.getLocationContext(); - SVal L = C.getState()->getSVal(Callee, LCtx); + SVal L = State->getSVal(Callee, LCtx); if (L.isUndef()) { if (!BT_call_undef) BT_call_undef.reset(new BuiltinBug("Called function pointer is an " "uninitalized pointer value")); - EmitBadCall(BT_call_undef.get(), C, CE); + emitBadCall(BT_call_undef.get(), C, Callee); return; } - if (isa<loc::ConcreteInt>(L)) { + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L)); + + // FIXME: Do we want to record the non-null assumption here? + if (StNull && !StNonNull) { if (!BT_call_null) BT_call_null.reset( new BuiltinBug("Called function pointer is null (null dereference)")); - EmitBadCall(BT_call_null.get(), C, CE); + emitBadCall(BT_call_null.get(), C, Callee); + } +} + +void CallAndMessageChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // If this is a call to a C++ method, check if the callee is null or + // undefined. + if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { + SVal V = CC->getCXXThisVal(); + if (V.isUndef()) { + if (!BT_cxx_call_undef) + BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is " + "uninitialized")); + emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); + return; + } + + ProgramStateRef State = C.getState(); + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + + // FIXME: Do we want to record the non-null assumption here? + if (StNull && !StNonNull) { + if (!BT_cxx_call_null) + BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " + "is null")); + emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); + return; + } } - PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx), - "Function call argument is an uninitialized value", - BT_call_arg); + // Don't check for uninitialized field values in arguments if the + // caller has a body that is available and we have the chance to inline it. + // This is a hack, but is a reasonable compromise betweens sometimes warning + // and sometimes not depending on if we decide to inline a function. + const Decl *D = Call.getDecl(); + const bool checkUninitFields = + !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); + + OwningPtr<BugType> *BT; + if (isa<ObjCMethodCall>(Call)) + BT = &BT_msg_arg; + else + BT = &BT_call_arg; + + for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) + if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), + Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, + checkUninitFields, Call, *BT)) + return; } -void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, +void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { + SVal recVal = msg.getReceiverSVal(); + if (recVal.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + BugType *BT = 0; + switch (msg.getMessageKind()) { + case OCM_Message: + if (!BT_msg_undef) + BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " + "is an uninitialized value")); + BT = BT_msg_undef.get(); + break; + case OCM_PropertyAccess: + if (!BT_objc_prop_undef) + BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " + "uninitialized object " + "pointer")); + BT = BT_objc_prop_undef.get(); + break; + case OCM_Subscript: + if (!BT_objc_subscript_undef) + BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an " + "uninitialized object " + "pointer")); + BT = BT_objc_subscript_undef.get(); + break; + } + assert(BT && "Unknown message kind."); - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); + BugReport *R = new BugReport(*BT, BT->getName(), N); + const ObjCMessageExpr *ME = msg.getOriginExpr(); + R->addRange(ME->getReceiverRange()); - // FIXME: Handle 'super'? - if (const Expr *receiver = msg.getInstanceReceiver()) { - SVal recVal = state->getSVal(receiver, LCtx); - if (recVal.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { - BugType *BT = 0; - if (msg.isPureMessageExpr()) { - if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " - "is an uninitialized value")); - BT = BT_msg_undef.get(); - } - else { - if (!BT_objc_prop_undef) - BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " - "uninitialized object pointer")); - BT = BT_objc_prop_undef.get(); - } - BugReport *R = - new BugReport(*BT, BT->getName(), N); - R->addRange(receiver->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver, - R)); - C.EmitReport(R); - } + // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. + if (const Expr *ReceiverE = ME->getInstanceReceiver()) + bugreporter::addTrackNullOrUndefValueVisitor(N, ReceiverE, R); + C.EmitReport(R); + } + return; + } else { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); + + ProgramStateRef state = C.getState(); + ProgramStateRef notNilState, nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // Handle receiver must be nil. + if (nilState && !notNilState) { + HandleNilReceiver(C, state, msg); return; - } else { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - - ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); - - // Handle receiver must be nil. - if (nilState && !notNilState) { - HandleNilReceiver(C, state, msg); - return; - } } } - - const char *bugDesc = msg.isPropertySetter() ? - "Argument for property setter is an uninitialized value" - : "Argument in message expression is an uninitialized value"; - // Check for any arguments that are uninitialized/undefined. - PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx), - bugDesc, BT_msg_arg); } void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, ExplodedNode *N) const { if (!BT_msg_ret) @@ -300,18 +358,20 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, new BuiltinBug("Receiver in message expression is " "'nil' and returns a garbage value")); + const ObjCMessageExpr *ME = msg.getOriginExpr(); + SmallString<200> buf; llvm::raw_svector_ostream os(buf); - os << "The receiver of message '" << msg.getSelector().getAsString() - << "' is nil and returns a value of type '" - << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; + os << "The receiver of message '" << ME->getSelector().getAsString() + << "' is nil and returns a value of type '"; + msg.getResultType().print(os, C.getLangOpts()); + os << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); - if (const Expr *receiver = msg.getInstanceReceiver()) { - report->addRange(receiver->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver, - report)); + report->addRange(ME->getReceiverRange()); + // FIXME: This won't track "self" in messages to super. + if (const Expr *receiver = ME->getInstanceReceiver()) { + bugreporter::addTrackNullOrUndefValueVisitor(N, receiver, report); } C.EmitReport(report); } @@ -324,25 +384,25 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) { void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const { + const ObjCMethodCall &Msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. - QualType RetTy = msg.getType(Ctx); + QualType RetTy = Msg.getResultType(); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } // Other cases: check if sizeof(return type) > sizeof(void*) if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() - .isConsumedExpr(msg.getMessageExpr())) { + .isConsumedExpr(Msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); @@ -355,7 +415,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) - emitNilReceiverBug(C, msg, N); + emitNilReceiverBug(C, Msg, N); return; } @@ -372,8 +432,8 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 133204a6d350..7a2586557168 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -215,10 +215,10 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, E = D->propimpl_end(); I!=E; ++I) { // We can only check the synthesized properties - if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) continue; - ObjCIvarDecl *ID = (*I)->getPropertyIvarDecl(); + ObjCIvarDecl *ID = I->getPropertyIvarDecl(); if (!ID) continue; @@ -226,7 +226,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars continue; - const ObjCPropertyDecl *PD = (*I)->getPropertyDecl(); + const ObjCPropertyDecl *PD = I->getPropertyDecl(); if (!PD) continue; @@ -261,7 +261,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, } PathDiagnosticLocation SDLoc = - PathDiagnosticLocation::createBegin((*I), BR.getSourceManager()); + PathDiagnosticLocation::createBegin(*I, BR.getSourceManager()); BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, os.str(), SDLoc); diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index dde90713ce18..b8b7c367969c 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -31,6 +31,7 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { T.getOS() == llvm::Triple::FreeBSD || T.getOS() == llvm::Triple::NetBSD || T.getOS() == llvm::Triple::OpenBSD || + T.getOS() == llvm::Triple::Bitrig || T.getOS() == llvm::Triple::DragonFly; } diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 843502f9705b..0e9efaa5ade8 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -37,6 +37,8 @@ class CheckerDocumentation : public Checker< check::PreStmt<DeclStmt>, check::PostStmt<CallExpr>, check::PreObjCMessage, check::PostObjCMessage, + check::PreCall, + check::PostCall, check::BranchCondition, check::Location, check::Bind, @@ -72,14 +74,41 @@ public: /// which does not include the control flow statements such as IfStmt. The /// callback can be specialized to be called with any subclass of Stmt. /// - /// check::PostStmt<DeclStmt> + /// check::PostStmt<CallExpr> void checkPostStmt(const CallExpr *DS, CheckerContext &C) const; - /// \brief Pre-visit the Objective C messages. - void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + /// \brief Pre-visit the Objective C message. + /// + /// This will be called before the analyzer core processes the method call. + /// This is called for any action which produces an Objective-C message send, + /// including explicit message syntax and property access. + /// + /// check::PreObjCMessage + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} + + /// \brief Post-visit the Objective C message. + /// \sa checkPreObjCMessage() + /// + /// check::PostObjCMessage + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} - /// \brief Post-visit the Objective C messages. - void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + /// \brief Pre-visit an abstract "call" event. + /// + /// This is used for checkers that want to check arguments or attributed + /// behavior for functions and methods no matter how they are being invoked. + /// + /// Note that this includes ALL cross-body invocations, so if you want to + /// limit your checks to, say, function calls, you can either test for that + /// or fall back to the explicit callback (i.e. check::PreStmt). + /// + /// check::PreCall + void checkPreCall(const CallEvent &Call, CheckerContext &C) const {} + + /// \brief Post-visit an abstract "call" event. + /// \sa checkPreObjCMessage() + /// + /// check::PostCall + void checkPostCall(const CallEvent &Call, CheckerContext &C) const {} /// \brief Pre-visit of the condition statement of a branch (such as IfStmt). void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {} @@ -94,7 +123,7 @@ public: /// /// check::Location void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, - CheckerContext &C) const {} + CheckerContext &) const {} /// \brief Called on binding of a value to a location. /// @@ -103,7 +132,7 @@ public: /// \param S The bind is performed while processing the statement S. /// /// check::Bind - void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {} + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {} /// \brief Called whenever a symbol becomes dead. @@ -187,24 +216,26 @@ public: bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; } - /// check::RegionChanges - /// Allows tracking regions which get invalidated. - /// \param state The current program state. - /// \param invalidated A set of all symbols potentially touched by the change. + /// \brief Allows tracking regions which get invalidated. + /// + /// \param State The current program state. + /// \param Invalidated A set of all symbols potentially touched by the change. /// \param ExplicitRegions The regions explicitly requested for invalidation. /// For example, in the case of a function call, these would be arguments. /// \param Regions The transitive closure of accessible regions, /// i.e. all regions that may have been touched by this change. - /// \param The call expression wrapper if the regions are invalidated by a - /// call, 0 otherwise. - /// Note, in order to be notified, the checker should also implement + /// \param Call The call expression wrapper if the regions are invalidated + /// by a call, 0 otherwise. + /// Note, in order to be notified, the checker should also implement the /// wantsRegionChangeUpdate callback. + /// + /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *, + const StoreManager::InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { return State; } diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 96a8d26c3483..8110bd0e18c2 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -84,6 +84,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, DescFile<"StackAddrEscapeChecker.cpp">; +def DynamicTypePropagation : Checker<"DynamicTypePropagation">, + HelpText<"Generate dynamic type information">, + DescFile<"DynamicTypePropagation.cpp">; + } // end "core" let ParentPackage = CoreExperimental in { @@ -168,10 +172,6 @@ def ReturnUndefChecker : Checker<"UndefReturn">, let ParentPackage = CplusplusExperimental in { -def IteratorsChecker : Checker<"Iterators">, - HelpText<"Check improper uses of STL vector iterators">, - DescFile<"IteratorsChecker.cpp">; - def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; @@ -283,6 +283,10 @@ def MallocPessimistic : Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems.">, DescFile<"MallocChecker.cpp">; +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">, + DescFile<"MallocSizeofChecker.cpp">; + } // end "unix" let ParentPackage = UnixExperimental in { @@ -295,10 +299,6 @@ def MallocOptimistic : Checker<"MallocWithAnnotations">, HelpText<"Check for memory leaks, double free, and use-after-free problems. Assumes that all user-defined functions which might free a pointer are annotated.">, DescFile<"MallocChecker.cpp">; -def MallocSizeofChecker : Checker<"MallocSizeof">, - HelpText<"Check for dubious malloc arguments involving sizeof">, - DescFile<"MallocSizeofChecker.cpp">; - def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, DescFile<"PthreadLockChecker.cpp">; @@ -361,7 +361,7 @@ def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, let ParentPackage = Cocoa in { def ObjCAtSyncChecker : Checker<"AtSync">, - HelpText<"Check for null pointers used as mutexes for @synchronized">, + HelpText<"Check for nil pointers used as mutexes for @synchronized">, DescFile<"ObjCAtSyncChecker.cpp">; def NilArgChecker : Checker<"NilArg">, @@ -373,8 +373,8 @@ def ClassReleaseChecker : Checker<"ClassRelease">, DescFile<"BasicObjCFoundationChecks.cpp">; def VariadicMethodTypeChecker : Checker<"VariadicMethodTypes">, - HelpText<"Check for passing non-Objective-C types to variadic methods that expect " - "only Objective-C types">, + HelpText<"Check for passing non-Objective-C types to variadic collection " + "initialization methods that expect only Objective-C types">, DescFile<"BasicObjCFoundationChecks.cpp">; def NSAutoreleasePoolChecker : Checker<"NSAutoreleasePool">, @@ -393,6 +393,10 @@ def ObjCSelfInitChecker : Checker<"SelfInit">, HelpText<"Check that 'self' is properly initialized inside an initializer method">, DescFile<"ObjCSelfInitChecker.cpp">; +def ObjCLoopChecker : Checker<"Loops">, + HelpText<"Improved modeling of loops using Cocoa collection types">, + DescFile<"BasicObjCFoundationChecks.cpp">; + def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; @@ -401,7 +405,7 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, DescFile<"RetainCountChecker.cpp">; -} // end "cocoa" +} // end "osx.cocoa" let ParentPackage = CocoaExperimental in { @@ -475,6 +479,14 @@ def CallGraphDumper : Checker<"DumpCallGraph">, HelpText<"Display Call Graph">, DescFile<"DebugCheckers.cpp">; +def TraversalDumper : Checker<"DumpTraversal">, + HelpText<"Print branch conditions as they are traversed by the engine">, + DescFile<"TraversalChecker.cpp">; + +def CallDumper : Checker<"DumpCalls">, + HelpText<"Print calls as they are traversed by the engine">, + DescFile<"TraversalChecker.cpp">; + def AnalyzerStatsChecker : Checker<"Stats">, HelpText<"Emit warnings with analyzer statistics">, DescFile<"AnalyzerStatsChecker.cpp">; @@ -483,5 +495,9 @@ def TaintTesterChecker : Checker<"TaintTest">, HelpText<"Mark tainted symbols as such.">, DescFile<"TaintTesterChecker.cpp">; +def ExprInspectionChecker : Checker<"ExprInspection">, + HelpText<"Check the analyzer's understanding of expressions">, + DescFile<"ExprInspectionChecker.cpp">; + } // end "debug" diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 81a27451cbaa..e98c131ce97d 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -26,13 +26,18 @@ using namespace ento; namespace { class DereferenceChecker : public Checker< check::Location, - EventDispatcher<ImplicitNullDerefEvent> > { + check::Bind, + EventDispatcher<ImplicitNullDerefEvent> > { mutable OwningPtr<BuiltinBug> BT_null; mutable OwningPtr<BuiltinBug> BT_undef; + void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, + bool IsBind = false) const; + public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; static const MemRegion *AddDerefSource(raw_ostream &os, SmallVectorImpl<SourceRange> &Ranges, @@ -76,6 +81,106 @@ DereferenceChecker::AddDerefSource(raw_ostream &os, return sourceR; } +void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, + CheckerContext &C, bool IsBind) const { + // Generate an error node. + ExplodedNode *N = C.generateSink(State); + if (!N) + return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null.reset(new BuiltinBug("Dereference of null pointer")); + + SmallString<100> buf; + SmallVector<SourceRange, 2> Ranges; + + // Walk through lvalue casts to get the original expression + // that syntactically caused the load. + if (const Expr *expr = dyn_cast<Expr>(S)) + S = expr->IgnoreParenLValueCasts(); + + const MemRegion *sourceR = 0; + + if (IsBind) { + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { + if (BO->isAssignmentOp()) + S = BO->getRHS(); + } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + assert(DS->isSingleDecl() && "We process decls one by one"); + if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) + if (const Expr *Init = VD->getAnyInitializer()) + S = Init; + } + } + + switch (S->getStmtClass()) { + case Stmt::ArraySubscriptExprClass: { + llvm::raw_svector_ostream os(buf); + os << "Array access"; + const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); + sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + State.getPtr(), N->getLocationContext()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::UnaryOperatorClass: { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer"; + const UnaryOperator *U = cast<UnaryOperator>(S); + sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), + State.getPtr(), N->getLocationContext(), true); + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *M = cast<MemberExpr>(S); + if (M->isArrow()) { + llvm::raw_svector_ostream os(buf); + os << "Access to field '" << M->getMemberNameInfo() + << "' results in a dereference of a null pointer"; + sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), + State.getPtr(), N->getLocationContext(), true); + } + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); + if (const DeclRefExpr *DR = + dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Instance variable access (via '" << VD->getName() + << "') results in a null pointer dereference"; + } + } + Ranges.push_back(IV->getSourceRange()); + break; + } + default: + break; + } + + BugReport *report = + new BugReport(*BT_null, + buf.empty() ? BT_null->getDescription() : buf.str(), + N); + + bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), + report); + + for (SmallVectorImpl<SourceRange>::iterator + I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) + report->addRange(*I); + + if (sourceR) { + report->markInteresting(sourceR); + report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR))); + } + + C.EmitReport(report); +} + void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. @@ -86,8 +191,10 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N), report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, + bugreporter::GetDerefExpr(N), + report); + report->disablePathPruning(); C.EmitReport(report); } return; @@ -100,115 +207,81 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, return; ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { - // Generate an error node. - ExplodedNode *N = C.generateSink(nullState); - if (!N) - return; - - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null.reset(new BuiltinBug("Dereference of null pointer")); - - SmallString<100> buf; - SmallVector<SourceRange, 2> Ranges; - - // Walk through lvalue casts to get the original expression - // that syntactically caused the load. - if (const Expr *expr = dyn_cast<Expr>(S)) - S = expr->IgnoreParenLValueCasts(); - - const MemRegion *sourceR = 0; - - switch (S->getStmtClass()) { - case Stmt::ArraySubscriptExprClass: { - llvm::raw_svector_ostream os(buf); - os << "Array access"; - const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); - sourceR = - AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), - state.getPtr(), LCtx); - os << " results in a null pointer dereference"; - break; - } - case Stmt::UnaryOperatorClass: { - llvm::raw_svector_ostream os(buf); - os << "Dereference of null pointer"; - const UnaryOperator *U = cast<UnaryOperator>(S); - sourceR = - AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), - state.getPtr(), LCtx, true); - break; - } - case Stmt::MemberExprClass: { - const MemberExpr *M = cast<MemberExpr>(S); - if (M->isArrow()) { - llvm::raw_svector_ostream os(buf); - os << "Access to field '" << M->getMemberNameInfo() - << "' results in a dereference of a null pointer"; - sourceR = - AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), - state.getPtr(), LCtx, true); - } - break; - } - case Stmt::ObjCIvarRefExprClass: { - const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); - if (const DeclRefExpr *DR = - dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - llvm::raw_svector_ostream os(buf); - os << "Instance variable access (via '" << VD->getName() - << "') results in a null pointer dereference"; - } - } - Ranges.push_back(IV->getSourceRange()); - break; - } - default: - break; - } + reportBug(nullState, S, C); + return; + } - BugReport *report = - new BugReport(*BT_null, - buf.empty() ? BT_null->getDescription():buf.str(), - N); + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + if (ExplodedNode *N = C.generateSink(nullState)) { + ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; + dispatchEvent(event); + } + } - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N), report)); + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} - for (SmallVectorImpl<SourceRange>::iterator - I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) - report->addRange(*I); +void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + // If we're binding to a reference, check if the value is known to be null. + if (V.isUndef()) + return; - if (sourceR) { - report->markInteresting(sourceR); - report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); - } + const MemRegion *MR = L.getAsRegion(); + const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); + if (!TVR) + return; - C.EmitReport(report); + if (!TVR->getValueType()->isReferenceType()) + return; + + ProgramStateRef State = C.getState(); + + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + + if (StNull) { + if (!StNonNull) { + reportBug(StNull, S, C, /*isBind=*/true); return; } - else { - // Otherwise, we have the case where the location could either be - // null or not-null. Record the error node as an "implicit" null - // dereference. - if (ExplodedNode *N = C.generateSink(nullState)) { - ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; - dispatchEvent(event); - } + + // At this point the value could be either null or non-null. + // Record this as an "implicit" null dereference. + if (ExplodedNode *N = C.generateSink(StNull)) { + ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, + &C.getBugReporter() }; + dispatchEvent(event); } } - // From this point forward, we know that the location is not null. - C.addTransition(notNullState); + // Unlike a regular null dereference, initializing a reference with a + // dereferenced null pointer does not actually cause a runtime exception in + // Clang's implementation of references. + // + // int &r = *p; // safe?? + // if (p != NULL) return; // uh-oh + // r = 5; // trap here + // + // The standard says this is invalid as soon as we try to create a "null + // reference" (there is no such thing), but turning this into an assumption + // that 'p' is never null will not match our actual runtime behavior. + // So we do not record this assumption, allowing us to warn on the last line + // of this example. + // + // We do need to add a transition because we may have generated a sink for + // the "implicit" null dereference. + C.addTransition(State, this); } void ento::registerDereferenceChecker(CheckerManager &mgr) { diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 2627f0c982f0..dcf6a8603ec7 100644 --- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -42,8 +42,9 @@ void DivZeroChecker::reportBug(const char *Msg, BugReport *R = new BugReport(*BT, Msg, N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDenomExpr(N), R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, + bugreporter::GetDenomExpr(N), + R); C.EmitReport(R); } } @@ -57,8 +58,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, Op != BO_RemAssign) return; - if (!B->getRHS()->getType()->isIntegerType() || - !B->getRHS()->getType()->isScalarType()) + if (!B->getRHS()->getType()->isScalarType()) return; SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext()); diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp new file mode 100644 index 000000000000..fea57337bbda --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -0,0 +1,179 @@ +//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker defines the rules for dynamic type gathering and propagation. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { +class DynamicTypePropagation: + public Checker< check::PostCall, + check::PostStmt<ImplicitCastExpr> > { + const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const; + + /// \brief Return a better dynamic type if one can be derived from the cast. + const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, + CheckerContext &C) const; +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; +}; +} + +void DynamicTypePropagation::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can obtain perfect type info for return values from some calls. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + + // Get the returned value if it's a region. + SVal Result = C.getSVal(Call.getOriginExpr()); + const MemRegion *RetReg = Result.getAsRegion(); + if (!RetReg) + return; + + ProgramStateRef State = C.getState(); + + switch (Msg->getMethodFamily()) { + default: + break; + + // We assume that the type of the object returned by alloc and new are the + // pointer to the object of the class specified in the receiver of the + // message. + case OMF_alloc: + case OMF_new: { + // Get the type of object that will get created. + const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); + const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); + if (!ObjTy) + return; + QualType DynResTy = + C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); + C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); + break; + } + case OMF_init: { + // Assume, the result of the init method has the same dynamic type as + // the receiver and propagate the dynamic type info. + const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); + if (!RecReg) + return; + DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); + C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); + break; + } + } + } +} + +void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, + CheckerContext &C) const { + // We only track dynamic type info for regions. + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + if (!ToR) + return; + + switch (CastE->getCastKind()) { + default: + break; + case CK_BitCast: + // Only handle ObjCObjects for now. + if (const Type *NewTy = getBetterObjCType(CastE, C)) + C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0))); + break; + } + return; +} + +const ObjCObjectType * +DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const { + if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { + if (const ObjCObjectType *ObjTy + = MsgE->getClassReceiver()->getAs<ObjCObjectType>()) + return ObjTy; + } + + if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { + if (const ObjCObjectType *ObjTy + = MsgE->getSuperType()->getAs<ObjCObjectType>()) + return ObjTy; + } + + const Expr *RecE = MsgE->getInstanceReceiver(); + if (!RecE) + return 0; + + RecE= RecE->IgnoreParenImpCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) { + const StackFrameContext *SFCtx = C.getStackFrame(); + // Are we calling [self alloc]? If this is self, get the type of the + // enclosing ObjC class. + if (DRE->getDecl() == SFCtx->getSelfDecl()) { + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl())) + if (const ObjCObjectType *ObjTy = + dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl())) + return ObjTy; + } + } + return 0; +} + +// Return a better dynamic type if one can be derived from the cast. +// Compare the current dynamic type of the region and the new type to which we +// are casting. If the new type is lower in the inheritance hierarchy, pick it. +const ObjCObjectPointerType * +DynamicTypePropagation::getBetterObjCType(const Expr *CastE, + CheckerContext &C) const { + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + assert(ToR); + + // Get the old and new types. + const ObjCObjectPointerType *NewTy = + CastE->getType()->getAs<ObjCObjectPointerType>(); + if (!NewTy) + return 0; + QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); + if (OldDTy.isNull()) { + return NewTy; + } + const ObjCObjectPointerType *OldTy = + OldDTy->getAs<ObjCObjectPointerType>(); + if (!OldTy) + return 0; + + // Id the old type is 'id', the new one is more precise. + if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) + return NewTy; + + // Return new if it's a subclass of old. + const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); + const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); + if (ToI && FromI && FromI->isSuperClassOf(ToI)) + return NewTy; + + return 0; +} + +void ento::registerDynamicTypePropagation(CheckerManager &mgr) { + mgr.registerChecker<DynamicTypePropagation>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp new file mode 100644 index 000000000000..7acf223e63d6 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -0,0 +1,122 @@ +//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class ExprInspectionChecker : public Checker< eval::Call > { + mutable OwningPtr<BugType> BT; + + void analyzerEval(const CallExpr *CE, CheckerContext &C) const; + void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; + + typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, + CheckerContext &C) const; + +public: + bool evalCall(const CallExpr *CE, CheckerContext &C) const; +}; +} + +bool ExprInspectionChecker::evalCall(const CallExpr *CE, + CheckerContext &C) const { + // These checks should have no effect on the surrounding environment + // (globals should not be invalidated, etc), hence the use of evalCall. + FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) + .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) + .Case("clang_analyzer_checkInlined", + &ExprInspectionChecker::analyzerCheckInlined) + .Default(0); + + if (!Handler) + return false; + + (this->*Handler)(CE, C); + return true; +} + +static const char *getArgumentValueString(const CallExpr *CE, + CheckerContext &C) { + if (CE->getNumArgs() == 0) + return "Missing assertion argument"; + + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + ProgramStateRef State = N->getState(); + + const Expr *Assertion = CE->getArg(0); + SVal AssertionVal = State->getSVal(Assertion, LC); + + if (AssertionVal.isUndef()) + return "UNDEFINED"; + + ProgramStateRef StTrue, StFalse; + llvm::tie(StTrue, StFalse) = + State->assume(cast<DefinedOrUnknownSVal>(AssertionVal)); + + if (StTrue) { + if (StFalse) + return "UNKNOWN"; + else + return "TRUE"; + } else { + if (StFalse) + return "FALSE"; + else + llvm_unreachable("Invalid constraint; neither true or false."); + } +} + +void ExprInspectionChecker::analyzerEval(const CallExpr *CE, + CheckerContext &C) const { + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + + // A specific instantiation of an inlined function may have more constrained + // values than can generally be assumed. Skip the check. + if (LC->getCurrentStackFrame()->getParent() != 0) + return; + + if (!BT) + BT.reset(new BugType("Checking analyzer assumptions", "debug")); + + BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); + C.EmitReport(R); +} + +void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, + CheckerContext &C) const { + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + + // An inlined function could conceivably also be analyzed as a top-level + // function. We ignore this case and only emit a message (TRUE or FALSE) + // when we are analyzing it as an inlined function. This means that + // clang_analyzer_checkInlined(true) should always print TRUE, but + // clang_analyzer_checkInlined(false) should never actually print anything. + if (LC->getCurrentStackFrame()->getParent() == 0) + return; + + if (!BT) + BT.reset(new BugType("Checking analyzer assumptions", "debug")); + + BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); + C.EmitReport(R); +} + +void ento::registerExprInspectionChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ExprInspectionChecker>(); +} + diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 135b81dda4ac..afb862cd6c9a 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -273,7 +273,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( // Skipping the following functions, since they might be used for cleansing // or smart memory copy: - // - memccpy - copying untill hitting a special character. + // - memccpy - copying until hitting a special character. return TaintPropagationRule(); } @@ -299,6 +299,9 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = 0; const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; @@ -372,7 +375,11 @@ void GenericTaintChecker::addSourcesPost(const CallExpr *CE, CheckerContext &C) const { // Define the attack surface. // Set the evaluation function by switching on the callee name. - StringRef Name = C.getCalleeName(CE); + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) @@ -406,6 +413,9 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{ return true; const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return false; + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return false; @@ -549,7 +559,6 @@ ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE, if (CE->getNumArgs() < 2) return State; - SVal x = State->getSVal(CE->getArg(1), C.getLocationContext()); // All arguments except for the very first one should get taint. for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { // The arguments are pointer arguments. The data they are pointing at is diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index c08f163a1f7b..9d0b83f40b34 100644 --- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -106,6 +106,7 @@ private: typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData> AssumptionMap; mutable AssumptionMap hash; + mutable OwningPtr<BugType> BT; }; } @@ -343,7 +344,9 @@ void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B, void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { - BugType *BT = new BugType("Idempotent operation", "Dead code"); + if (!BT) + BT.reset(new BugType("Idempotent operation", "Dead code")); + // Iterate over the hash to see if we have any paths with definite // idempotent operations. for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp deleted file mode 100644 index b0bac33db162..000000000000 --- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ /dev/null @@ -1,603 +0,0 @@ -//=== IteratorsChecker.cpp - Check for Invalidated Iterators ------*- C++ -*---- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines IteratorsChecker, a number of small checks for conditions -// leading to invalid iterators being used. -// FIXME: Currently only supports 'vector' and 'deque' -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/DeclTemplate.h" -#include "clang/Basic/SourceManager.h" -#include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/Type.h" -#include "clang/AST/PrettyPrinter.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/StringSwitch.h" - - -using namespace clang; -using namespace ento; - -// This is the state associated with each iterator which includes both the -// kind of state and the instance used to initialize it. -// FIXME: add location where invalidated for better error reporting. -namespace { -class RefState { - enum Kind { BeginValid, EndValid, Invalid, Undefined, Unknown } K; - const void *VR; - -public: - RefState(Kind k, const void *vr) : K(k), VR(vr) {} - - bool isValid() const { return K == BeginValid || K == EndValid; } - bool isInvalid() const { return K == Invalid; } - bool isUndefined() const { return K == Undefined; } - bool isUnknown() const { return K == Unknown; } - const MemRegion *getMemRegion() const { - if (K == BeginValid || K == EndValid) - return(const MemRegion *)VR; - return 0; - } - const MemberExpr *getMemberExpr() const { - if (K == Invalid) - return(const MemberExpr *)VR; - return 0; - } - - bool operator==(const RefState &X) const { - return K == X.K && VR == X.VR; - } - - static RefState getBeginValid(const MemRegion *vr) { - assert(vr); - return RefState(BeginValid, vr); - } - static RefState getEndValid(const MemRegion *vr) { - assert(vr); - return RefState(EndValid, vr); - } - static RefState getInvalid( const MemberExpr *ME ) { - return RefState(Invalid, ME); - } - static RefState getUndefined( void ) { - return RefState(Undefined, 0); - } - static RefState getUnknown( void ) { - return RefState(Unknown, 0); - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(K); - ID.AddPointer(VR); - } -}; - -enum RefKind { NoKind, VectorKind, VectorIteratorKind }; - -class IteratorsChecker : - public Checker<check::PreStmt<CXXOperatorCallExpr>, - check::PreStmt<DeclStmt>, - check::PreStmt<CXXMemberCallExpr>, - check::PreStmt<CallExpr> > - { - // Used when parsing iterators and vectors and deques. - BuiltinBug *BT_Invalid, *BT_Undefined, *BT_Incompatible; - -public: - IteratorsChecker() : - BT_Invalid(0), BT_Undefined(0), BT_Incompatible(0) - {} - static void *getTag() { static int tag; return &tag; } - - // Checker entry points. - void checkPreStmt(const CXXOperatorCallExpr *OCE, - CheckerContext &C) const; - - void checkPreStmt(const DeclStmt *DS, - CheckerContext &C) const; - - void checkPreStmt(const CXXMemberCallExpr *MCE, - CheckerContext &C) const; - - void checkPreStmt(const CallExpr *CE, - CheckerContext &C) const; - -private: - ProgramStateRef handleAssign(ProgramStateRef state, - const Expr *lexp, - const Expr *rexp, - const LocationContext *LC) const; - - ProgramStateRef handleAssign(ProgramStateRef state, - const MemRegion *MR, - const Expr *rexp, - const LocationContext *LC) const; - - ProgramStateRef invalidateIterators(ProgramStateRef state, - const MemRegion *MR, - const MemberExpr *ME) const; - - void checkExpr(CheckerContext &C, const Expr *E) const; - - void checkArgs(CheckerContext &C, const CallExpr *CE) const; - - const MemRegion *getRegion(ProgramStateRef state, - const Expr *E, - const LocationContext *LC) const; - - const DeclRefExpr *getDeclRefExpr(const Expr *E) const; -}; - -class IteratorState { -public: - typedef llvm::ImmutableMap<const MemRegion *, RefState> EntryMap; -}; -} //end anonymous namespace - -namespace clang { - namespace ento { - template <> - struct ProgramStateTrait<IteratorState> - : public ProgramStatePartialTrait<IteratorState::EntryMap> { - static void *GDMIndex() { return IteratorsChecker::getTag(); } - }; - } -} - -void ento::registerIteratorsChecker(CheckerManager &mgr) { - mgr.registerChecker<IteratorsChecker>(); -} - -// =============================================== -// Utility functions used by visitor functions -// =============================================== - -// check a templated type for std::vector or std::deque -static RefKind getTemplateKind(const NamedDecl *td) { - const DeclContext *dc = td->getDeclContext(); - const NamespaceDecl *nameSpace = dyn_cast<NamespaceDecl>(dc); - if (!nameSpace || !isa<TranslationUnitDecl>(nameSpace->getDeclContext()) - || nameSpace->getName() != "std") - return NoKind; - - StringRef name = td->getName(); - return llvm::StringSwitch<RefKind>(name) - .Cases("vector", "deque", VectorKind) - .Default(NoKind); -} - -static RefKind getTemplateKind(const DeclContext *dc) { - if (const ClassTemplateSpecializationDecl *td = - dyn_cast<ClassTemplateSpecializationDecl>(dc)) - return getTemplateKind(cast<NamedDecl>(td)); - return NoKind; -} - -static RefKind getTemplateKind(const TypedefType *tdt) { - const TypedefNameDecl *td = tdt->getDecl(); - RefKind parentKind = getTemplateKind(td->getDeclContext()); - if (parentKind == VectorKind) { - return llvm::StringSwitch<RefKind>(td->getName()) - .Cases("iterator", - "const_iterator", - "reverse_iterator", VectorIteratorKind) - .Default(NoKind); - } - return NoKind; -} - -static RefKind getTemplateKind(const TemplateSpecializationType *tsp) { - const TemplateName &tname = tsp->getTemplateName(); - TemplateDecl *td = tname.getAsTemplateDecl(); - if (!td) - return NoKind; - return getTemplateKind(td); -} - -static RefKind getTemplateKind(QualType T) { - if (const TemplateSpecializationType *tsp = - T->getAs<TemplateSpecializationType>()) { - return getTemplateKind(tsp); - } - if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) { - QualType namedType = ET->getNamedType(); - if (const TypedefType *tdt = namedType->getAs<TypedefType>()) - return getTemplateKind(tdt); - if (const TemplateSpecializationType *tsp = - namedType->getAs<TemplateSpecializationType>()) { - return getTemplateKind(tsp); - } - } - return NoKind; -} - -// Iterate through our map and invalidate any iterators that were -// initialized fromt the specified instance MemRegion. -ProgramStateRef IteratorsChecker::invalidateIterators(ProgramStateRef state, - const MemRegion *MR, const MemberExpr *ME) const { - IteratorState::EntryMap Map = state->get<IteratorState>(); - if (Map.isEmpty()) - return state; - - // Loop over the entries in the current state. - // The key doesn't change, so the map iterators won't change. - for (IteratorState::EntryMap::iterator I = Map.begin(), E = Map.end(); - I != E; ++I) { - RefState RS = I.getData(); - if (RS.getMemRegion() == MR) - state = state->set<IteratorState>(I.getKey(), RefState::getInvalid(ME)); - } - - return state; -} - -// Handle assigning to an iterator where we don't have the LValue MemRegion. -ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state, - const Expr *lexp, const Expr *rexp, const LocationContext *LC) const { - // Skip the cast if present. - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(lexp)) - lexp = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(lexp)) - lexp = ICE->getSubExpr(); - SVal sv = state->getSVal(lexp, LC); - const MemRegion *MR = sv.getAsRegion(); - if (!MR) - return state; - RefKind kind = getTemplateKind(lexp->getType()); - - // If assigning to a vector, invalidate any iterators currently associated. - if (kind == VectorKind) - return invalidateIterators(state, MR, 0); - - // Make sure that we are assigning to an iterator. - if (getTemplateKind(lexp->getType()) != VectorIteratorKind) - return state; - return handleAssign(state, MR, rexp, LC); -} - -// handle assigning to an iterator -ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state, - const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const { - // Assume unknown until we find something definite. - state = state->set<IteratorState>(MR, RefState::getUnknown()); - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(rexp)) - rexp = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(rexp)) - rexp = ICE->getSubExpr(); - // Need to handle three cases: MemberCall, copy, copy with addition. - if (const CallExpr *CE = dyn_cast<CallExpr>(rexp)) { - // Handle MemberCall. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee())) { - const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase()); - if (!DRE) - return state; - // Verify that the type is std::vector<T>. - if (getTemplateKind(DRE->getType()) != VectorKind) - return state; - // Now get the MemRegion associated with the instance. - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return state; - const MemRegion *IMR = state->getRegion(VD, LC); - if (!IMR) - return state; - // Finally, see if it is one of the calls that will create - // a valid iterator and mark it if so, else mark as Unknown. - StringRef mName = ME->getMemberDecl()->getName(); - - if (llvm::StringSwitch<bool>(mName) - .Cases("begin", "insert", "erase", true).Default(false)) { - return state->set<IteratorState>(MR, RefState::getBeginValid(IMR)); - } - if (mName == "end") - return state->set<IteratorState>(MR, RefState::getEndValid(IMR)); - - return state->set<IteratorState>(MR, RefState::getUnknown()); - } - } - // Handle straight copy from another iterator. - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(rexp)) { - if (getTemplateKind(DRE->getType()) != VectorIteratorKind) - return state; - // Now get the MemRegion associated with the instance. - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return state; - const MemRegion *IMR = state->getRegion(VD, LC); - if (!IMR) - return state; - // Get the RefState of the iterator being copied. - const RefState *RS = state->get<IteratorState>(IMR); - if (!RS) - return state; - // Use it to set the state of the LValue. - return state->set<IteratorState>(MR, *RS); - } - // If we have operator+ or operator- ... - if (const CXXOperatorCallExpr *OCE = dyn_cast<CXXOperatorCallExpr>(rexp)) { - OverloadedOperatorKind Kind = OCE->getOperator(); - if (Kind == OO_Plus || Kind == OO_Minus) { - // Check left side of tree for a valid value. - state = handleAssign( state, MR, OCE->getArg(0), LC); - const RefState *RS = state->get<IteratorState>(MR); - // If found, return it. - if (!RS->isUnknown()) - return state; - // Otherwise return what we find in the right side. - return handleAssign(state, MR, OCE->getArg(1), LC); - } - } - // Fall through if nothing matched. - return state; -} - -// Iterate through the arguments looking for an Invalid or Undefined iterator. -void IteratorsChecker::checkArgs(CheckerContext &C, const CallExpr *CE) const { - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - checkExpr(C, *I); - } -} - -// Get the DeclRefExpr associated with the expression. -const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const { - // If it is a CXXConstructExpr, need to get the subexpression. - if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) { - if (CE->getNumArgs()== 1) { - CXXConstructorDecl *CD = CE->getConstructor(); - if (CD->isTrivial()) - E = CE->getArg(0); - } - } - if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) - E = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - E = ICE->getSubExpr(); - // If it isn't one of our types, don't do anything. - if (getTemplateKind(E->getType()) != VectorIteratorKind) - return NULL; - return dyn_cast<DeclRefExpr>(E); -} - -// Get the MemRegion associated with the expresssion. -const MemRegion *IteratorsChecker::getRegion(ProgramStateRef state, - const Expr *E, const LocationContext *LC) const { - const DeclRefExpr *DRE = getDeclRefExpr(E); - if (!DRE) - return NULL; - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return NULL; - // return the MemRegion associated with the iterator - return state->getRegion(VD, LC); -} - -// Check the expression and if it is an iterator, generate a diagnostic -// if the iterator is not valid. -// FIXME: this method can generate new nodes, and subsequent logic should -// use those nodes. We also cannot create multiple nodes at one ProgramPoint -// with the same tag. -void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { - ProgramStateRef state = C.getState(); - const MemRegion *MR = getRegion(state, E, C.getLocationContext()); - if (!MR) - return; - - // Get the state associated with the iterator. - const RefState *RS = state->get<IteratorState>(MR); - if (!RS) - return; - if (RS->isInvalid()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Invalid) - // FIXME: We are eluding constness here. - const_cast<IteratorsChecker*>(this)->BT_Invalid = new BuiltinBug(""); - - std::string msg; - const MemberExpr *ME = RS->getMemberExpr(); - if (ME) { - std::string name = ME->getMemberNameInfo().getAsString(); - msg = "Attempt to use an iterator made invalid by call to '" + - name + "'"; - } - else { - msg = "Attempt to use an iterator made invalid by copying another " - "container to its container"; - } - - BugReport *R = new BugReport(*BT_Invalid, msg, N); - R->addRange(getDeclRefExpr(E)->getSourceRange()); - C.EmitReport(R); - } - } - else if (RS->isUndefined()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Undefined) - // FIXME: We are eluding constness here. - const_cast<IteratorsChecker*>(this)->BT_Undefined = - new BuiltinBug("Use of iterator that is not defined"); - - BugReport *R = new BugReport(*BT_Undefined, - BT_Undefined->getDescription(), N); - R->addRange(getDeclRefExpr(E)->getSourceRange()); - C.EmitReport(R); - } - } -} - -// =============================================== -// Path analysis visitor functions -// =============================================== - -// For a generic Call, just check the args for bad iterators. -void IteratorsChecker::checkPreStmt(const CallExpr *CE, - CheckerContext &C) const{ - - // FIXME: These checks are to currently work around a bug - // in CheckerManager. - if (isa<CXXOperatorCallExpr>(CE)) - return; - if (isa<CXXMemberCallExpr>(CE)) - return; - - checkArgs(C, CE); -} - -// Handle operator calls. First, if it is operator=, check the argument, -// and handle assigning and set target state appropriately. Otherwise, for -// other operators, check the args for bad iterators and handle comparisons. -void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, - CheckerContext &C) const -{ - const LocationContext *LC = C.getLocationContext(); - ProgramStateRef state = C.getState(); - OverloadedOperatorKind Kind = OCE->getOperator(); - if (Kind == OO_Equal) { - checkExpr(C, OCE->getArg(1)); - state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC); - C.addTransition(state); - return; - } - else { - checkArgs(C, OCE); - // If it is a compare and both are iterators, ensure that they are for - // the same container. - if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual || - Kind == OO_Less || Kind == OO_LessEqual || - Kind == OO_Greater || Kind == OO_GreaterEqual) { - const MemRegion *MR0, *MR1; - MR0 = getRegion(state, OCE->getArg(0), LC); - if (!MR0) - return; - MR1 = getRegion(state, OCE->getArg(1), LC); - if (!MR1) - return; - const RefState *RS0, *RS1; - RS0 = state->get<IteratorState>(MR0); - if (!RS0) - return; - RS1 = state->get<IteratorState>(MR1); - if (!RS1) - return; - if (RS0->getMemRegion() != RS1->getMemRegion()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Incompatible) - const_cast<IteratorsChecker*>(this)->BT_Incompatible = - new BuiltinBug( - "Cannot compare iterators from different containers"); - - BugReport *R = new BugReport(*BT_Incompatible, - BT_Incompatible->getDescription(), N); - R->addRange(OCE->getSourceRange()); - C.EmitReport(R); - } - } - } - } -} - -// Need to handle DeclStmts to pick up initializing of iterators and to mark -// uninitialized ones as Undefined. -void IteratorsChecker::checkPreStmt(const DeclStmt *DS, - CheckerContext &C) const { - const Decl *D = *DS->decl_begin(); - const VarDecl *VD = dyn_cast<VarDecl>(D); - // Only care about iterators. - if (getTemplateKind(VD->getType()) != VectorIteratorKind) - return; - - // Get the MemRegion associated with the iterator and mark it as Undefined. - ProgramStateRef state = C.getState(); - Loc VarLoc = state->getLValue(VD, C.getLocationContext()); - const MemRegion *MR = VarLoc.getAsRegion(); - if (!MR) - return; - state = state->set<IteratorState>(MR, RefState::getUndefined()); - - // if there is an initializer, handle marking Valid if a proper initializer - const Expr *InitEx = VD->getInit(); - if (InitEx) { - // FIXME: This is too syntactic. Since 'InitEx' will be analyzed first - // it should resolve to an SVal that we can check for validity - // *semantically* instead of walking through the AST. - if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(InitEx)) { - if (CE->getNumArgs() == 1) { - const Expr *E = CE->getArg(0); - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(E)) - E = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - InitEx = ICE->getSubExpr(); - state = handleAssign(state, MR, InitEx, C.getLocationContext()); - } - } - } - C.addTransition(state); -} - - -namespace { struct CalledReserved {}; } -namespace clang { namespace ento { -template<> struct ProgramStateTrait<CalledReserved> - : public ProgramStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { - static void *GDMIndex() { static int index = 0; return &index; } -}; -}} - -// on a member call, first check the args for any bad iterators -// then, check to see if it is a call to a function that will invalidate -// the iterators -void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE, - CheckerContext &C) const { - // Check the arguments. - checkArgs(C, MCE); - const MemberExpr *ME = dyn_cast<MemberExpr>(MCE->getCallee()); - if (!ME) - return; - // Make sure we have the right kind of container. - const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase()); - if (!DRE || getTemplateKind(DRE->getType()) != VectorKind) - return; - SVal tsv = C.getState()->getSVal(DRE, C.getLocationContext()); - // Get the MemRegion associated with the container instance. - const MemRegion *MR = tsv.getAsRegion(); - if (!MR) - return; - // If we are calling a function that invalidates iterators, mark them - // appropriately by finding matching instances. - ProgramStateRef state = C.getState(); - StringRef mName = ME->getMemberDecl()->getName(); - if (llvm::StringSwitch<bool>(mName) - .Cases("insert", "reserve", "push_back", true) - .Cases("erase", "pop_back", "clear", "resize", true) - .Default(false)) { - // If there was a 'reserve' call, assume iterators are good. - if (!state->contains<CalledReserved>(MR)) - state = invalidateIterators(state, MR, ME); - } - // Keep track of instances that have called 'reserve' - // note: do this after we invalidate any iterators by calling - // 'reserve' itself. - if (mName == "reserve") - state = state->add<CalledReserved>(MR); - - if (state != C.getState()) - C.addTransition(state); -} - diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index cb976e03a597..969f2ddeb4ca 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -290,7 +290,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); - StringRef funName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; @@ -446,7 +450,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = C.getState(); - StringRef funName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); // If a value has been allocated, add it to the set for tracking. unsigned idx = getTrackedFunctionIndex(funName, true); @@ -528,9 +536,11 @@ MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, } ProgramPoint P = AllocNode->getLocation(); - if (!isa<StmtPoint>(P)) - return 0; - return cast<clang::PostStmt>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + return Exit->getCalleeContext()->getCallSite(); + if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P)) + return PS->getStmt(); + return 0; } BugReport *MacOSKeychainAPIChecker:: diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 8bce88a76977..dfcedf640821 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -18,7 +18,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" @@ -34,15 +34,21 @@ using namespace ento; namespace { class RefState { - enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, + enum Kind { // Reference to allocated memory. + Allocated, + // Reference to released/freed memory. + Released, + // The responsibility for freeing resources has transfered from + // this reference. A relinquished symbol should not be freed. Relinquished } K; const Stmt *S; public: RefState(Kind k, const Stmt *s) : K(k), S(s) {} - bool isAllocated() const { return K == AllocateUnchecked; } + bool isAllocated() const { return K == Allocated; } bool isReleased() const { return K == Released; } + bool isRelinquished() const { return K == Relinquished; } const Stmt *getStmt() const { return S; } @@ -50,14 +56,10 @@ public: return K == X.K && S == X.S; } - static RefState getAllocateUnchecked(const Stmt *s) { - return RefState(AllocateUnchecked, s); - } - static RefState getAllocateFailed() { - return RefState(AllocateFailed, 0); + static RefState getAllocated(const Stmt *s) { + return RefState(Allocated, s); } static RefState getReleased(const Stmt *s) { return RefState(Released, s); } - static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } static RefState getRelinquished(const Stmt *s) { return RefState(Relinquished, s); } @@ -90,6 +92,7 @@ class MallocChecker : public Checker<check::DeadSymbols, check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::PostStmt<BlockExpr>, + check::PreObjCMessage, check::Location, check::Bind, eval::Assume, @@ -117,6 +120,7 @@ public: void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndPath(CheckerContext &C) const; @@ -132,17 +136,22 @@ public: const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; bool wantsRegionChangeUpdate(ProgramStateRef state) const { return true; } + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const; + private: void initIdentifierInfo(ASTContext &C) const; /// Check if this is one of the functions which can allocate/reallocate memory /// pointed to by one of its arguments. bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const; static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, @@ -167,20 +176,26 @@ private: ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) const; ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, - ProgramStateRef state, unsigned Num, - bool Hold) const; + ProgramStateRef state, unsigned Num, + bool Hold) const; + ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, + const Expr *ParentExpr, + ProgramStateRef state, + bool Hold) const; ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE, bool FreesMemOnFailure) const; static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE); - bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const; + ///\brief Check if the memory associated with this symbol was released. + bool isReleased(SymbolRef Sym, CheckerContext &C) const; + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S = 0) const; /// Check if the function is not known to us. So, for example, we could /// conservatively assume it can free/reallocate it's pointer arguments. - bool doesNotFreeMemory(const CallOrObjCMessage *Call, + bool doesNotFreeMemory(const CallEvent *Call, ProgramStateRef State) const; static bool SummarizeValue(raw_ostream &os, SVal V); @@ -207,15 +222,17 @@ private: // The allocated region symbol tracked by the main analysis. SymbolRef Sym; - // The mode we are in, i.e. what kind of diagnostics will be emitted. - NotificationMode Mode; + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; - // A symbol from when the primary region should have been reallocated. - SymbolRef FailedReallocSymbol; + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; - public: - MallocBugVisitor(SymbolRef S) - : Sym(S), Mode(Normal), FailedReallocSymbol(0) {} + bool IsLeak; + + public: + MallocBugVisitor(SymbolRef S, bool isLeak = false) + : Sym(S), Mode(Normal), FailedReallocSymbol(0), IsLeak(isLeak) {} virtual ~MallocBugVisitor() {} @@ -239,6 +256,15 @@ private: (S && S->isReleased()) && (!SPrev || !SPrev->isReleased())); } + inline bool isRelinquished(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> relinquished. Other state (allocated) -> relinquished. + return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || + isa<ObjCPropertyRefExpr>(Stmt)) && + (S && S->isRelinquished()) && + (!SPrev || !SPrev->isRelinquished())); + } + inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, const Stmt *Stmt) { // If the expression is not a call, and the state change is @@ -253,6 +279,20 @@ private: const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR); + + PathDiagnosticPiece* getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + if (!IsLeak) + return 0; + + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode, + BRC.getSourceManager()); + // Do not add the statement itself as a range in case of leak. + return new PathDiagnosticEventPiece(L, BR.getDescription(), false); + } + private: class StackHintGeneratorForReallocationFailed : public StackHintGeneratorForSymbol { @@ -315,44 +355,73 @@ public: } // end anonymous namespace void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (!II_malloc) - II_malloc = &Ctx.Idents.get("malloc"); - if (!II_free) - II_free = &Ctx.Idents.get("free"); - if (!II_realloc) - II_realloc = &Ctx.Idents.get("realloc"); - if (!II_reallocf) - II_reallocf = &Ctx.Idents.get("reallocf"); - if (!II_calloc) - II_calloc = &Ctx.Idents.get("calloc"); - if (!II_valloc) - II_valloc = &Ctx.Idents.get("valloc"); - if (!II_strdup) - II_strdup = &Ctx.Idents.get("strdup"); - if (!II_strndup) - II_strndup = &Ctx.Idents.get("strndup"); + if (II_malloc) + return; + II_malloc = &Ctx.Idents.get("malloc"); + II_free = &Ctx.Idents.get("free"); + II_realloc = &Ctx.Idents.get("realloc"); + II_reallocf = &Ctx.Idents.get("reallocf"); + II_calloc = &Ctx.Idents.get("calloc"); + II_valloc = &Ctx.Idents.get("valloc"); + II_strdup = &Ctx.Idents.get("strdup"); + II_strndup = &Ctx.Idents.get("strndup"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { + if (isFreeFunction(FD, C)) + return true; + + if (isAllocationFunction(FD, C)) + return true; + + return false; +} + +bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, + ASTContext &C) const { if (!FD) return false; - IdentifierInfo *FunI = FD->getIdentifier(); - if (!FunI) - return false; - initIdentifierInfo(C); + if (FD->getKind() == Decl::Function) { + IdentifierInfo *FunI = FD->getIdentifier(); + initIdentifierInfo(C); - if (FunI == II_malloc || FunI == II_free || FunI == II_realloc || - FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || - FunI == II_strdup || FunI == II_strndup) - return true; + if (FunI == II_malloc || FunI == II_realloc || + FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || + FunI == II_strdup || FunI == II_strndup) + return true; + } - if (Filter.CMallocOptimistic && FD->hasAttrs() && - FD->specific_attr_begin<OwnershipAttr>() != - FD->specific_attr_end<OwnershipAttr>()) - return true; + if (Filter.CMallocOptimistic && FD->hasAttrs()) + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) + if ((*i)->getOwnKind() == OwnershipAttr::Returns) + return true; + return false; +} + +bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const { + if (!FD) + return false; + + if (FD->getKind() == Decl::Function) { + IdentifierInfo *FunI = FD->getIdentifier(); + initIdentifierInfo(C); + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) + return true; + } + if (Filter.CMallocOptimistic && FD->hasAttrs()) + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) + if ((*i)->getOwnKind() == OwnershipAttr::Takes || + (*i)->getOwnKind() == OwnershipAttr::Holds) + return true; return false; } @@ -361,29 +430,32 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (!FD) return; - initIdentifierInfo(C.getASTContext()); - IdentifierInfo *FunI = FD->getIdentifier(); - if (!FunI) - return; - ProgramStateRef State = C.getState(); - if (FunI == II_malloc || FunI == II_valloc) { - if (CE->getNumArgs() < 1) - return; - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - } else if (FunI == II_realloc) { - State = ReallocMem(C, CE, false); - } else if (FunI == II_reallocf) { - State = ReallocMem(C, CE, true); - } else if (FunI == II_calloc) { - State = CallocMem(C, CE); - } else if (FunI == II_free) { - State = FreeMemAux(C, CE, C.getState(), 0, false); - } else if (FunI == II_strdup) { - State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_strndup) { - State = MallocUpdateRefState(C, CE, State); - } else if (Filter.CMallocOptimistic) { + + if (FD->getKind() == Decl::Function) { + initIdentifierInfo(C.getASTContext()); + IdentifierInfo *FunI = FD->getIdentifier(); + + if (FunI == II_malloc || FunI == II_valloc) { + if (CE->getNumArgs() < 1) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (FunI == II_realloc) { + State = ReallocMem(C, CE, false); + } else if (FunI == II_reallocf) { + State = ReallocMem(C, CE, true); + } else if (FunI == II_calloc) { + State = CallocMem(C, CE); + } else if (FunI == II_free) { + State = FreeMemAux(C, CE, State, 0, false); + } else if (FunI == II_strdup) { + State = MallocUpdateRefState(C, CE, State); + } else if (FunI == II_strndup) { + State = MallocUpdateRefState(C, CE, State); + } + } + + if (Filter.CMallocOptimistic) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -405,6 +477,34 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { C.addTransition(State); } +static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) { + Selector S = Call.getSelector(); + for (unsigned i = 1; i < S.getNumArgs(); ++i) + if (S.getNameForSlot(i).equals("freeWhenDone")) + if (Call.getArgSVal(i).isConstant(0)) + return true; + + return false; +} + +void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call, + CheckerContext &C) const { + // If the first selector is dataWithBytesNoCopy, assume that the memory will + // be released with 'free' by the new object. + // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; + // Unless 'freeWhenDone' param set to 0. + // TODO: Check that the memory was allocated with malloc. + Selector S = Call.getSelector(); + if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" || + S.getNameForSlot(0) == "initWithBytesNoCopy" || + S.getNameForSlot(0) == "initWithCharactersNoCopy") && + !isFreeWhenDoneSetToZero(Call)){ + unsigned int argIdx = 0; + C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx), + Call.getOriginExpr(), C.getState(), true)); + } +} + ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) { @@ -422,19 +522,27 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, ProgramStateRef state) { - // Get the return value. - SVal retVal = state->getSVal(CE, C.getLocationContext()); + + // Bind the return value to the symbolic value from the heap region. + // TODO: We could rewrite post visit to eval call; 'malloc' does not have + // side effects other than what we model here. + unsigned Count = C.getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + DefinedSVal RetVal = + cast<DefinedSVal>(svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)); + state = state->BindExpr(CE, C.getLocationContext(), RetVal); // We expect the malloc functions to return a pointer. - if (!isa<Loc>(retVal)) + if (!isa<Loc>(RetVal)) return 0; // Fill the region with the initialization value. - state = state->bindDefault(retVal, Init); + state = state->bindDefault(RetVal, Init); // Set the region's extent equal to the Size parameter. const SymbolicRegion *R = - dyn_cast_or_null<SymbolicRegion>(retVal.getAsRegion()); + dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); if (!R) return 0; if (isa<DefinedOrUnknownSVal>(Size)) { @@ -465,7 +573,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, assert(Sym); // Set the symbol's state to Allocated. - return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE)); + return state->set<RegionState>(Sym, RefState::getAllocated(CE)); } @@ -495,7 +603,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, if (CE->getNumArgs() < (Num + 1)) return 0; - const Expr *ArgExpr = CE->getArg(Num); + return FreeMemAux(C, CE->getArg(Num), CE, state, Hold); +} + +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const Expr *ArgExpr, + const Expr *ParentExpr, + ProgramStateRef state, + bool Hold) const { + SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); if (!isa<DefinedOrUnknownSVal>(ArgVal)) return 0; @@ -558,20 +674,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, SymbolRef Sym = SR->getSymbol(); const RefState *RS = state->get<RegionState>(Sym); - // If the symbol has not been tracked, return. This is possible when free() is - // called on a pointer that does not get its pointee directly from malloc(). - // Full support of this requires inter-procedural analysis. - if (!RS) - return 0; - // Check double free. - if (RS->isReleased()) { + if (RS && (RS->isReleased() || RS->isRelinquished())) { if (ExplodedNode *N = C.generateSink()) { if (!BT_DoubleFree) BT_DoubleFree.reset( new BugType("Double free", "Memory Error")); BugReport *R = new BugReport(*BT_DoubleFree, - "Attempt to free released memory", N); + (RS->isReleased() ? "Attempt to free released memory" : + "Attempt to free non-owned memory"), N); R->addRange(ArgExpr->getSourceRange()); R->markInteresting(Sym); R->addVisitor(new MallocBugVisitor(Sym)); @@ -582,8 +693,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Normal free. if (Hold) - return state->set<RegionState>(Sym, RefState::getRelinquished(CE)); - return state->set<RegionState>(Sym, RefState::getReleased(CE)); + return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr)); + return state->set<RegionState>(Sym, RefState::getReleased(ParentExpr)); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -780,10 +891,8 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){ // The semantics of the return value are: // If size was equal to 0, either NULL or a pointer suitable to be passed - // to free() is returned. - stateFree = stateFree->set<ReallocPairs>(ToPtr, - ReallocPair(FromPtr, FreesOnFail)); - C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); + // to free() is returned. We just free the input pointer and do not add + // any constrains on the output pointer. return stateFree; } @@ -851,8 +960,10 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, ProgramPoint P = AllocNode->getLocation(); const Stmt *AllocationStmt = 0; - if (isa<StmtPoint>(P)) - AllocationStmt = cast<StmtPoint>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + AllocationStmt = Exit->getCalleeContext()->getCallSite(); + else if (StmtPoint *SP = dyn_cast<StmtPoint>(&P)) + AllocationStmt = SP->getStmt(); return LeakInfo(AllocationStmt, ReferenceRegion); } @@ -884,15 +995,15 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "Memory is never released; potential leak"; - if (Region) { + if (Region && Region->canPrintPretty()) { os << " of memory pointed to by '"; - Region->dumpPretty(os); - os <<'\''; + Region->printPretty(os); + os << '\''; } BugReport *R = new BugReport(*BT_Leak, os.str(), N, LocUsedForUniqueing); R->markInteresting(Sym); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(new MallocBugVisitor(Sym, true)); C.EmitReport(R); } @@ -960,23 +1071,9 @@ void MallocChecker::checkEndPath(CheckerContext &C) const { } } -bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S, - CheckerContext &C) const { - ProgramStateRef state = C.getState(); - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) - return false; - - if (RS->isAllocated()) { - state = state->set<RegionState>(Sym, RefState::getEscaped(S)); - C.addTransition(state); - return true; - } - return false; -} - void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - if (isMemFunction(C.getCalleeDecl(CE), C.getASTContext())) + // We will check for double free in the post visit. + if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) return; // Check use after free, when a freed pointer is passed to a call. @@ -1000,7 +1097,8 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { return; // Check if we are returning a symbol. - SVal RetVal = C.getState()->getSVal(E, C.getLocationContext()); + ProgramStateRef State = C.getState(); + SVal RetVal = State->getSVal(E, C.getLocationContext()); SymbolRef Sym = RetVal.getAsSymbol(); if (!Sym) // If we are returning a field of the allocated struct or an array element, @@ -1011,16 +1109,18 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { if (const SymbolicRegion *BMR = dyn_cast<SymbolicRegion>(MR->getBaseRegion())) Sym = BMR->getSymbol(); - if (!Sym) - return; // Check if we are returning freed memory. - if (checkUseAfterFree(Sym, C, E)) - return; + if (Sym) + if (checkUseAfterFree(Sym, C, E)) + return; - // If this function body is not inlined, check if the symbol is escaping. - if (C.getLocationContext()->getParent() == 0) - checkEscape(Sym, E, C); + // If this function body is not inlined, stop tracking any returned symbols. + if (C.getLocationContext()->getParent() == 0) { + State = + State->scanReachableSymbols<StopTrackingCallback>(RetVal).getState(); + C.addTransition(State); + } } // TODO: Blocks should be either inlined or should call invalidate regions @@ -1063,11 +1163,15 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } -bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, - const Stmt *S) const { +bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { assert(Sym); const RefState *RS = C.getState()->get<RegionState>(Sym); - if (RS && RS->isReleased()) { + return (RS && RS->isReleased()); +} + +bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const { + if (isReleased(Sym, C)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_UseFree) BT_UseFree.reset(new BugType("Use-after-free", "Memory Error")); @@ -1090,7 +1194,7 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) - checkUseAfterFree(Sym, C); + checkUseAfterFree(Sym, C, S); } //===----------------------------------------------------------------------===// @@ -1118,13 +1222,11 @@ void MallocChecker::checkBind(SVal loc, SVal val, const Stmt *S, // To test (3), generate a new state with the binding added. If it is // the same state, then it escapes (since the store cannot represent // the binding). - escapes = (state == (state->bindLoc(*regionLoc, val))); - } - if (!escapes) { - // Case 4: We do not currently model what happens when a symbol is - // assigned to a struct field, so be conservative here and let the symbol - // go. TODO: This could definitely be improved upon. - escapes = !isa<VarRegion>(regionLoc->getRegion()); + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = state->getSVal(regionLoc->getRegion()); + if (StoredVal != val) + escapes = (state == (state->bindLoc(*regionLoc, val))); } } @@ -1165,7 +1267,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, if (RS) { if (RS->isReleased() && ! I.getData().IsFreeOnFailure) state = state->set<RegionState>(ReallocSym, - RefState::getAllocateUnchecked(RS->getStmt())); + RefState::getAllocated(RS->getStmt())); } state = state->remove<ReallocPairs>(I.getKey()); } @@ -1175,118 +1277,30 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, } // Check if the function is known to us. So, for example, we could -// conservatively assume it can free/reallocate it's pointer arguments. +// conservatively assume it can free/reallocate its pointer arguments. // (We assume that the pointers cannot escape through calls to system // functions not handled by this checker.) -bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call, +bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, ProgramStateRef State) const { - if (!Call) - return false; + assert(Call); // For now, assume that any C++ call can free memory. // TODO: If we want to be more optimistic here, we'll need to make sure that // regions escape to C++ containers. They seem to do that even now, but for // mysterious reasons. - if (Call->isCXXCall()) - return false; - - const Decl *D = Call->getDecl(); - if (!D) + if (!(isa<FunctionCall>(Call) || isa<ObjCMethodCall>(Call))) return false; - ASTContext &ASTC = State->getStateManager().getContext(); - - // If it's one of the allocation functions we can reason about, we model - // its behavior explicitly. - if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) { - return true; - } - - // If it's not a system call, assume it frees memory. - SourceManager &SM = ASTC.getSourceManager(); - if (!SM.isInSystemHeader(D->getLocation())) - return false; - - // Process C/ObjC functions. - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - // White list the system functions whose arguments escape. - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return true; - StringRef FName = II->getName(); - - // White list thread local storage. - if (FName.equals("pthread_setspecific")) - return false; - - // White list the 'XXXNoCopy' ObjC functions. - if (FName.endswith("NoCopy")) { - // Look for the deallocator argument. We know that the memory ownership - // is not transfered only if the deallocator argument is - // 'kCFAllocatorNull'. - for (unsigned i = 1; i < Call->getNumArgs(); ++i) { - const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts(); - if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { - StringRef DeallocatorName = DE->getFoundDecl()->getName(); - if (DeallocatorName == "kCFAllocatorNull") - return true; - } - } - return false; - } - - // PR12101 - // Many CoreFoundation and CoreGraphics might allow a tracked object - // to escape. - if (Call->isCFCGAllowingEscape(FName)) + // Check Objective-C messages by selector name. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + // If it's not a framework call, or if it takes a callback, assume it + // can free memory. + if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg()) return false; - // Associating streams with malloced buffers. The pointer can escape if - // 'closefn' is specified (and if that function does free memory). - // Currently, we do not inspect the 'closefn' function (PR12101). - if (FName == "funopen") - if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0)) - return false; - - // Do not warn on pointers passed to 'setbuf' when used with std streams, - // these leaks might be intentional when setting the buffer for stdio. - // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer - if (FName == "setbuf" || FName =="setbuffer" || - FName == "setlinebuf" || FName == "setvbuf") { - if (Call->getNumArgs() >= 1) - if (const DeclRefExpr *Arg = - dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts())) - if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl())) - if (D->getCanonicalDecl()->getName().find("std") - != StringRef::npos) - return false; - } - - // A bunch of other functions, which take ownership of a pointer (See retain - // release checker). Not all the parameters here are invalidated, but the - // Malloc checker cannot differentiate between them. The right way of doing - // this would be to implement a pointer escapes callback. - if (FName == "CVPixelBufferCreateWithBytes" || - FName == "CGBitmapContextCreateWithData" || - FName == "CVPixelBufferCreateWithPlanarBytes" || - FName == "OSAtomicEnqueue") { - return false; - } - - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) - return false; - - // Otherwise, assume that the function does not free memory. - // Most system calls, do not free the memory. - return true; - - // Process ObjC functions. - } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) { - Selector S = ObjCD->getSelector(); + Selector S = Msg->getSelector(); - // White list the ObjC functions which do free memory. + // Whitelist the ObjC methods which do free memory. // - Anything containing 'freeWhenDone' param set to 1. // Ex: dataWithBytesNoCopy:length:freeWhenDone. for (unsigned i = 1; i < S.getNumArgs(); ++i) { @@ -1299,20 +1313,111 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call, } // If the first selector ends with NoCopy, assume that the ownership is - // transfered as well. + // transferred as well. // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; - if (S.getNameForSlot(0).endswith("NoCopy")) { + StringRef FirstSlot = S.getNameForSlot(0); + if (FirstSlot.endswith("NoCopy")) + return false; + + // If the first selector starts with addPointer, insertPointer, + // or replacePointer, assume we are dealing with NSPointerArray or similar. + // This is similar to C++ containers (vector); we still might want to check + // that the pointers get freed by following the container itself. + if (FirstSlot.startswith("addPointer") || + FirstSlot.startswith("insertPointer") || + FirstSlot.startswith("replacePointer")) { return false; } - // Otherwise, assume that the function does not free memory. - // Most system calls, do not free the memory. + // Otherwise, assume that the method does not free memory. + // Most framework methods do not free memory. return true; } - // Otherwise, assume that the function can free memory. - return false; + // At this point the only thing left to handle is straight function calls. + const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl(); + if (!FD) + return false; + + ASTContext &ASTC = State->getStateManager().getContext(); + + // If it's one of the allocation functions we can reason about, we model + // its behavior explicitly. + if (isMemFunction(FD, ASTC)) + return true; + // If it's not a system call, assume it frees memory. + if (!Call->isInSystemHeader()) + return false; + + // White list the system functions whose arguments escape. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + StringRef FName = II->getName(); + + // White list the 'XXXNoCopy' CoreFoundation functions. + // We specifically check these before + if (FName.endswith("NoCopy")) { + // Look for the deallocator argument. We know that the memory ownership + // is not transferred only if the deallocator argument is + // 'kCFAllocatorNull'. + for (unsigned i = 1; i < Call->getNumArgs(); ++i) { + const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts(); + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorNull") + return true; + } + } + return false; + } + + // Associating streams with malloced buffers. The pointer can escape if + // 'closefn' is specified (and if that function does free memory), + // but it will not if closefn is not specified. + // Currently, we do not inspect the 'closefn' function (PR12101). + if (FName == "funopen") + if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0)) + return true; + + // Do not warn on pointers passed to 'setbuf' when used with std streams, + // these leaks might be intentional when setting the buffer for stdio. + // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer + if (FName == "setbuf" || FName =="setbuffer" || + FName == "setlinebuf" || FName == "setvbuf") { + if (Call->getNumArgs() >= 1) { + const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts(); + if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE)) + if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl())) + if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos) + return false; + } + } + + // A bunch of other functions which either take ownership of a pointer or + // wrap the result up in a struct or object, meaning it can be freed later. + // (See RetainCountChecker.) Not all the parameters here are invalidated, + // but the Malloc checker cannot differentiate between them. The right way + // of doing this would be to implement a pointer escapes callback. + if (FName == "CGBitmapContextCreate" || + FName == "CGBitmapContextCreateWithData" || + FName == "CVPixelBufferCreateWithBytes" || + FName == "CVPixelBufferCreateWithPlanarBytes" || + FName == "OSAtomicEnqueue") { + return false; + } + + // Handle cases where we know a buffer's /address/ can escape. + // Note that the above checks handle some special cases where we know that + // even though the address escapes, it's still our responsibility to free the + // buffer. + if (Call->argumentsMayEscape()) + return false; + + // Otherwise, assume that the function does not free memory. + // Most system calls do not free the memory. + return true; } // If the symbol we are tracking is invalidated, but not explicitly (ex: the &p @@ -1323,7 +1428,7 @@ MallocChecker::checkRegionChanges(ProgramStateRef State, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!invalidated || invalidated->empty()) return State; llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; @@ -1345,9 +1450,13 @@ MallocChecker::checkRegionChanges(ProgramStateRef State, SymbolRef sym = *I; if (WhitelistedSymbols.count(sym)) continue; - // The symbol escaped. - if (const RefState *RS = State->get<RegionState>(sym)) - State = State->set<RegionState>(sym, RefState::getEscaped(RS->getStmt())); + // The symbol escaped. Note, we assume that if the symbol is released, + // passing it out will result in a use after free. We also keep tracking + // relinquished symbols. + if (const RefState *RS = State->get<RegionState>(sym)) { + if (RS->isAllocated()) + State = State->remove<RegionState>(sym); + } } return State; } @@ -1377,7 +1486,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, const RefState *RS = state->get<RegionState>(Sym); const RefState *RSPrev = statePrev->get<RegionState>(Sym); - if (!RS && !RSPrev) + if (!RS) return 0; const Stmt *S = 0; @@ -1386,17 +1495,22 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Retrieve the associated statement. ProgramPoint ProgLoc = N->getLocation(); - if (isa<StmtPoint>(ProgLoc)) - S = cast<StmtPoint>(ProgLoc).getStmt(); + if (StmtPoint *SP = dyn_cast<StmtPoint>(&ProgLoc)) + S = SP->getStmt(); + else if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&ProgLoc)) + S = Exit->getCalleeContext()->getCallSite(); // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (isa<BlockEdge>(ProgLoc)) { - const CFGBlock *srcBlk = cast<BlockEdge>(ProgLoc).getSrc(); + else if (BlockEdge *Edge = dyn_cast<BlockEdge>(&ProgLoc)) { + const CFGBlock *srcBlk = Edge->getSrc(); S = srcBlk->getTerminator(); } if (!S) return 0; + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). + // Find out if this is an interesting point and what is the kind. if (Mode == Normal) { if (isAllocated(RS, RSPrev, S)) { @@ -1407,6 +1521,9 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, Msg = "Memory is released"; StackHint = new StackHintGeneratorForSymbol(Sym, "Returned released memory"); + } else if (isRelinquished(RS, RSPrev, S)) { + Msg = "Memory ownership is transfered"; + StackHint = new StackHintGeneratorForSymbol(Sym, ""); } else if (isReallocFailedCheck(RS, RSPrev, S)) { Mode = ReallocationFailed; Msg = "Reallocation failed"; @@ -1428,11 +1545,6 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Is this is the first appearance of the reallocated symbol? if (!statePrev->get<RegionState>(FailedReallocSymbol)) { - // If we ever hit this assert, that means BugReporter has decided to skip - // node pairs or visit them out of order. - assert(state->get<RegionState>(FailedReallocSymbol) && - "Missed the reallocation point"); - // We're at the reallocation point. Msg = "Attempt to reallocate memory"; StackHint = new StackHintGeneratorForSymbol(Sym, @@ -1452,6 +1564,14 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint); } +void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + RegionStateTy RS = State->get<RegionState>(); + + if (!RS.isEmpty()) + Out << "Has Malloc data" << NL; +} #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) {\ diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index 08a9da1fe3ef..6292a4725128 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -118,11 +118,6 @@ public: Visit(E->getRHS()); } - void VisitBinAdd(const BinaryOperator *E) { - Visit(E->getLHS()); - Visit(E->getRHS()); - } - void VisitImplicitCastExpr(const ImplicitCastExpr *E) { return Visit(E->getSubExpr()); } @@ -139,6 +134,29 @@ public: } }; +// Determine if the pointee and sizeof types are compatible. Here +// we ignore constness of pointer types. +static bool typesCompatible(ASTContext &C, QualType A, QualType B) { + while (true) { + A = A.getCanonicalType(); + B = B.getCanonicalType(); + + if (A.getTypePtr() == B.getTypePtr()) + return true; + + if (const PointerType *ptrA = A->getAs<PointerType>()) + if (const PointerType *ptrB = B->getAs<PointerType>()) { + A = ptrA->getPointeeType(); + B = ptrB->getPointeeType(); + continue; + } + + break; + } + + return false; +} + class MallocSizeofChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, @@ -166,7 +184,7 @@ public: continue; QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument(); - if (!BR.getContext().hasSameUnqualifiedType(PointeeType, SizeofType)) { + if (!typesCompatible(BR.getContext(), PointeeType, SizeofType)) { const TypeSourceInfo *TSI = 0; if (i->CastedExprParent.is<const VarDecl *>()) { TSI = @@ -180,9 +198,8 @@ public: OS << "Result of '" << i->AllocCall->getDirectCallee()->getIdentifier()->getName() - << "' is converted to type '" - << CastedType.getAsString() << "', whose pointee type '" - << PointeeType.getAsString() << "' is incompatible with " + << "' is converted to a pointer of type '" + << PointeeType.getAsString() << "', which is incompatible with " << "sizeof operand type '" << SizeofType.getAsString() << "'"; llvm::SmallVector<SourceRange, 4> Ranges; Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); @@ -194,7 +211,7 @@ public: PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), BR.getSourceManager(), ADC); - BR.EmitBasicReport(D, "allocator sizeof operand mismatch", + BR.EmitBasicReport(D, "Allocator sizeof operand mismatch", categories::UnixAPI, OS.str(), L, Ranges.data(), Ranges.size()); diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 4989ba88680e..aad3b0f5f277 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -20,9 +20,9 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" @@ -36,29 +36,20 @@ class NSAutoreleasePoolChecker mutable Selector releaseS; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } // end anonymous namespace -void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, +void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { - - const Expr *receiver = msg.getInstanceReceiver(); - if (!receiver) + if (!msg.isInstanceMessage()) return; - - // FIXME: Enhance with value-tracking information instead of consulting - // the type of the expression. - const ObjCObjectPointerType* PT = - receiver->getType()->getAs<ObjCObjectPointerType>(); - - if (!PT) - return; - const ObjCInterfaceDecl *OD = PT->getInterfaceDecl(); + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); if (!OD) return; - if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + if (!OD->getIdentifier()->isStr("NSAutoreleasePool")) return; if (releaseS.isNull()) diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index c2d7c09cf3b9..efb707294d56 100644 --- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -15,8 +15,8 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/StringSwitch.h" #include <cstdarg> @@ -29,7 +29,7 @@ class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>, check::PostObjCMessage > { public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } @@ -98,7 +98,7 @@ static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) { return (Arg == NULL); } -void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg, +void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 777e9ea219b3..4cc92ce9e958 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -50,8 +50,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); C.EmitReport(report); } return; @@ -74,8 +73,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); C.EmitReport(report); return; diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index f4655b6793d2..2ab49ed12a14 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -21,7 +21,6 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 97b58cf79b0e..be45da1be066 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -39,9 +39,9 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" @@ -50,29 +50,27 @@ using namespace ento; static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); static bool isInitializationMethod(const ObjCMethodDecl *MD); -static bool isInitMessage(const ObjCMessage &msg); +static bool isInitMessage(const ObjCMethodCall &Msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { -class ObjCSelfInitChecker : public Checker< check::PreObjCMessage, - check::PostObjCMessage, +class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, check::PostStmt<ObjCIvarRefExpr>, check::PreStmt<ReturnStmt>, - check::PreStmt<CallExpr>, - check::PostStmt<CallExpr>, - check::Location > { + check::PreCall, + check::PostCall, + check::Location, + check::Bind > { public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; - void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const; void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const; + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; - void checkPreStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; - void checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &CE, CheckerContext &C) const; + void checkPostCall(const CallEvent &CE, CheckerContext &C) const; }; } // end anonymous namespace @@ -181,7 +179,7 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, C.EmitReport(report); } -void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, +void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value @@ -192,7 +190,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, C.getCurrentAnalysisDeclContext()->getDecl()))) return; - if (isInitMessage(msg)) { + if (isInitMessage(Msg)) { // Tag the return value as the result of an initializer. ProgramStateRef state = C.getState(); @@ -201,14 +199,11 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, // value out when we return from this method. state = state->set<CalledInit>(true); - SVal V = state->getSVal(msg.getMessageExpr(), C.getLocationContext()); + SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext()); addSelfFlag(state, V, SelfFlag_InitRes, C); return; } - CallOrObjCMessage MsgWrapper(msg, C.getState(), C.getLocationContext()); - checkPostStmt(MsgWrapper, C); - // We don't check for an invalid 'self' in an obj-c message expression to cut // down false positives where logging functions get information from self // (like its class) or doing "invalidation" on self when the initialization @@ -239,8 +234,8 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, "'[(super or self) init...]'"); } -// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass -// the SelfFlags from the object 'self' point to before the call, to the new +// When a call receives a reference to 'self', [Pre/Post]Call pass +// the SelfFlags from the object 'self' points to before the call to the new // object after the call. This is to avoid invalidation of 'self' by logging // functions. // Another common pattern in classes with multiple initializers is to put the @@ -255,26 +250,13 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, // Until we can use inter-procedural analysis, in such a call, transfer the // SelfFlags to the result of the call. -void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, +void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, CheckerContext &C) const { - CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); - checkPreStmt(CEWrapper, C); -} - -void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, - CheckerContext &C) const { - CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); - checkPostStmt(CEWrapper, C); -} - -void ObjCSelfInitChecker::checkPreObjCMessage(ObjCMessage Msg, - CheckerContext &C) const { - CallOrObjCMessage MsgWrapper(Msg, C.getState(), C.getLocationContext()); - checkPreStmt(MsgWrapper, C); -} + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; -void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE, - CheckerContext &C) const { ProgramStateRef state = C.getState(); unsigned NumArgs = CE.getNumArgs(); // If we passed 'self' as and argument to the call, record it in the state @@ -296,9 +278,19 @@ void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE, } } -void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, +void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + ProgramStateRef state = C.getState(); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + if (!prevFlags) + return; + state = state->remove<PreCallSelfFlags>(); + unsigned NumArgs = CE.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { SVal argV = CE.getArgSVal(i); @@ -306,8 +298,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, // If the address of 'self' is being passed to the call, assume that the // 'self' after the call will have the same flags. // EX: log(&self) - SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); - state = state->remove<PreCallSelfFlags>(); addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { @@ -315,8 +305,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, // returns 'self'. So assign the flags, which were set on 'self' to the // return value. // EX: self = performMoreInitialization(self) - SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); - state = state->remove<PreCallSelfFlags>(); const Expr *CallExpr = CE.getOriginExpr(); if (CallExpr) addSelfFlag(state, state->getSVal(CallExpr, C.getLocationContext()), @@ -336,6 +324,28 @@ void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); } + +void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Allow assignment of anything to self. Self is a local variable in the + // initializer, so it is legal to assign anything to it, like results of + // static functions/method calls. After self is assigned something we cannot + // reason about, stop enforcing the rules. + // (Only continue checking if the assigned value should be treated as self.) + if ((isSelfVar(loc, C)) && + !hasSelfFlag(val, SelfFlag_InitRes, C) && + !hasSelfFlag(val, SelfFlag_Self, C) && + !isSelfVar(val, C)) { + + // Stop tracking the checker-specific state in the state. + ProgramStateRef State = C.getState(); + State = State->remove<CalledInit>(); + if (SymbolRef sym = loc.getAsSymbol()) + State = State->remove<SelfFlag>(sym); + C.addTransition(State); + } +} + // FIXME: A callback should disable checkers at the start of functions. static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { if (!ND) @@ -383,8 +393,8 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) { return MD->getMethodFamily() == OMF_init; } -static bool isInitMessage(const ObjCMessage &msg) { - return msg.getMethodFamily() == OMF_init; +static bool isInitMessage(const ObjCMethodCall &Call) { + return Call.getMethodFamily() == OMF_init; } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index 4718dc7c7aa9..582269c33279 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -47,6 +47,15 @@ static void Scan(IvarUsageMap& M, const Stmt *S) { return; } + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) + for (PseudoObjectExpr::const_semantics_iterator + i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { + const Expr *sub = *i; + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) + sub = OVE->getSourceExpr(); + Scan(M, sub); + } + for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) Scan(M, *I); } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index b569e412c41f..5503b23f4ae6 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -23,10 +23,10 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" @@ -66,8 +66,9 @@ public: /// particular argument. enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg, DecRefBridgedTransfered, + DecRefAndStopTracking, DecRefMsgAndStopTracking, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, SelfOwn, StopTracking }; + NewAutoreleasePool, StopTracking }; namespace llvm { template <> struct FoldingSetTrait<ArgEffect> { @@ -351,6 +352,20 @@ struct ProgramStateTrait<RefBindings> } } +static inline const RefVal *getRefBinding(ProgramStateRef State, + SymbolRef Sym) { + return State->get<RefBindings>(Sym); +} + +static inline ProgramStateRef setRefBinding(ProgramStateRef State, + SymbolRef Sym, RefVal Val) { + return State->set<RefBindings>(Sym, Val); +} + +static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { + return State->remove<RefBindings>(Sym); +} + //===----------------------------------------------------------------------===// // Function/Method behavior summaries. //===----------------------------------------------------------------------===// @@ -431,6 +446,12 @@ public: bool isSimple() const { return Args.isEmpty(); } + +private: + ArgEffects getArgEffects() const { return Args; } + ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; } + + friend class RetainSummaryManager; }; } // end anonymous namespace @@ -449,9 +470,6 @@ public: ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) : II(d ? d->getIdentifier() : 0), S(s) {} - ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s) - : II(d ? d->getIdentifier() : ii), S(s) {} - ObjCSummaryKey(Selector s) : II(0), S(s) {} @@ -473,17 +491,14 @@ template <> struct DenseMapInfo<ObjCSummaryKey> { } static unsigned getHashValue(const ObjCSummaryKey &V) { - return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier()) - & 0x88888888) - | (DenseMapInfo<Selector>::getHashValue(V.getSelector()) - & 0x55555555); + typedef std::pair<IdentifierInfo*, Selector> PairTy; + return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(), + V.getSelector())); } static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { - return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(), - RHS.getIdentifier()) && - DenseMapInfo<Selector>::isEqual(LHS.getSelector(), - RHS.getSelector()); + return LHS.getIdentifier() == RHS.getIdentifier() && + LHS.getSelector() == RHS.getSelector(); } }; @@ -498,21 +513,16 @@ class ObjCSummaryCache { public: ObjCSummaryCache() {} - const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName, - Selector S) { - // Lookup the method using the decl for the class @interface. If we - // have no decl, lookup using the class name. - return D ? find(D, S) : find(ClsName, S); - } - const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { // Do a lookup with the (D,S) pair. If we find a match return // the iterator. ObjCSummaryKey K(D, S); MapTy::iterator I = M.find(K); - if (I != M.end() || !D) + if (I != M.end()) return I->second; + if (!D) + return NULL; // Walk the super chain. If we find a hit with a parent, we'll end // up returning that summary. We actually allow that key (null,S), as @@ -628,9 +638,6 @@ class RetainSummaryManager { ArgEffects getArgEffects(); enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; - -public: - RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } const RetainSummary *getUnarySummary(const FunctionType* FT, UnaryFuncKind func); @@ -648,6 +655,10 @@ public: return getPersistentSummary(Summ); } + const RetainSummary *getDoNothingSummary() { + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } + const RetainSummary *getDefaultSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, MayEscape); @@ -739,41 +750,32 @@ public: InitializeMethodSummaries(); } - const RetainSummary *getSummary(const FunctionDecl *FD); + const RetainSummary *getSummary(const CallEvent &Call, + ProgramStateRef State = 0); - const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, + const RetainSummary *getFunctionSummary(const FunctionDecl *FD); + + const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries); - const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - ProgramStateRef state, - const LocationContext *LC); - - const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - const ObjCInterfaceDecl *ID) { - return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(), - msg.getType(Ctx), ObjCMethodSummaries); - } + const RetainSummary *getInstanceMethodSummary(const ObjCMethodCall &M, + ProgramStateRef State); - const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { - const ObjCInterfaceDecl *Class = 0; - if (!msg.isInstanceMessage()) - Class = msg.getReceiverInterface(); + const RetainSummary *getClassMethodSummary(const ObjCMethodCall &M) { + assert(!M.isInstanceMessage()); + const ObjCInterfaceDecl *Class = M.getReceiverInterface(); - return getMethodSummary(msg.getSelector(), Class->getIdentifier(), - Class, msg.getMethodDecl(), msg.getType(Ctx), - ObjCClassMethodSummaries); + return getMethodSummary(M.getSelector(), Class, M.getDecl(), + M.getResultType(), ObjCClassMethodSummaries); } /// getMethodSummary - This version of getMethodSummary is used to query /// the summary for the current method being analyzed. const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { - // FIXME: Eventually this should be unneeded. const ObjCInterfaceDecl *ID = MD->getClassInterface(); Selector S = MD->getSelector(); - IdentifierInfo *ClsName = ID->getIdentifier(); QualType ResultTy = MD->getResultType(); ObjCMethodSummariesTy *CachedSummaries; @@ -782,11 +784,11 @@ public: else CachedSummaries = &ObjCClassMethodSummaries; - return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries); + return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries); } const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy); + Selector S, QualType RetTy); void updateSummaryFromAnnotations(const RetainSummary *&Summ, const ObjCMethodDecl *MD); @@ -794,11 +796,18 @@ public: void updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD); + void updateSummaryForCall(const RetainSummary *&Summ, + const CallEvent &Call); + bool isGCEnabled() const { return GCEnabled; } bool isARCEnabled() const { return ARCEnabled; } bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } + + RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } + + friend class RetainSummaryTemplate; }; // Used to avoid allocating long-term (BPAlloc'd) memory for default retain @@ -811,10 +820,8 @@ class RetainSummaryTemplate { RetainSummary ScratchSummary; bool Accessed; public: - RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base, - RetainSummaryManager &mgr) - : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base), - Accessed(false) {} + RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr) + : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {} ~RetainSummaryTemplate() { if (Accessed) @@ -886,7 +893,101 @@ static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) { return FName.find("MakeCollectable") != StringRef::npos; } -const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { +static ArgEffect getStopTrackingEquivalent(ArgEffect E) { + switch (E) { + case DoNothing: + case Autorelease: + case DecRefBridgedTransfered: + case IncRef: + case IncRefMsg: + case MakeCollectable: + case MayEscape: + case NewAutoreleasePool: + case StopTracking: + return StopTracking; + case DecRef: + case DecRefAndStopTracking: + return DecRefAndStopTracking; + case DecRefMsg: + case DecRefMsgAndStopTracking: + return DecRefMsgAndStopTracking; + case Dealloc: + return Dealloc; + } + + llvm_unreachable("Unknown ArgEffect kind"); +} + +void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, + const CallEvent &Call) { + if (Call.hasNonZeroCallbackArg()) { + ArgEffect RecEffect = getStopTrackingEquivalent(S->getReceiverEffect()); + ArgEffect DefEffect = getStopTrackingEquivalent(S->getDefaultArgEffect()); + + ArgEffects CustomArgEffects = S->getArgEffects(); + for (ArgEffects::iterator I = CustomArgEffects.begin(), + E = CustomArgEffects.end(); + I != E; ++I) { + ArgEffect Translated = getStopTrackingEquivalent(I->second); + if (Translated != DefEffect) + ScratchArgs = AF.add(ScratchArgs, I->first, Translated); + } + + RetEffect RE = RetEffect::MakeNoRet(); + + // Special cases where the callback argument CANNOT free the return value. + // This can generally only happen if we know that the callback will only be + // called when the return value is already being deallocated. + if (const FunctionCall *FC = dyn_cast<FunctionCall>(&Call)) { + IdentifierInfo *Name = FC->getDecl()->getIdentifier(); + + // This callback frees the associated buffer. + if (Name->isStr("CGBitmapContextCreateWithData")) + RE = S->getRetEffect(); + } + + S = getPersistentSummary(RE, RecEffect, DefEffect); + } +} + +const RetainSummary * +RetainSummaryManager::getSummary(const CallEvent &Call, + ProgramStateRef State) { + const RetainSummary *Summ; + switch (Call.getKind()) { + case CE_Function: + Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl()); + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + case CE_Block: + case CE_CXXConstructor: + case CE_CXXDestructor: + case CE_CXXAllocator: + // FIXME: These calls are currently unsupported. + return getPersistentStopSummary(); + case CE_ObjCMessage: { + const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); + if (Msg.isInstanceMessage()) + Summ = getInstanceMethodSummary(Msg, State); + else + Summ = getClassMethodSummary(Msg); + break; + } + } + + updateSummaryForCall(Summ, Call); + + assert(Summ && "Unknown call type?"); + return Summ; +} + +const RetainSummary * +RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { + // If we don't know what function we're calling, use our default summary. + if (!FD) + return getDefaultSummary(); + // Look up a summary in our cache of FunctionDecls -> Summaries. FuncSummariesTy::iterator I = FuncSummaries.find(FD); if (I != FuncSummaries.end()) @@ -894,6 +995,7 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // No summary? Generate one. const RetainSummary *S = 0; + bool AllowAnnotations = true; do { // We generate "stop" summaries for implicitly defined functions. @@ -901,13 +1003,6 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { S = getPersistentStopSummary(); break; } - // For C++ methods, generate an implicit "stop" summary as well. We - // can relax this once we have a clear policy for C++ methods and - // ownership attributes. - if (isa<CXXMethodDecl>(FD)) { - S = getPersistentStopSummary(); - break; - } // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the // function's type. @@ -929,18 +1024,22 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // filters. assert(ScratchArgs.isEmpty()); - if (FName == "pthread_create") { - // Part of: <rdar://problem/7299394>. This will be addressed - // better with IPA. + if (FName == "pthread_create" || FName == "pthread_setspecific") { + // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. + // This will be addressed better with IPA. S = getPersistentStopSummary(); } else if (FName == "NSMakeCollectable") { // Handle: id NSMakeCollectable(CFTypeRef) S = (RetTy->isObjCIdType()) ? getUnarySummary(FT, cfmakecollectable) : getPersistentStopSummary(); + // The headers on OS X 10.8 use cf_consumed/ns_returns_retained, + // but we can fully model NSMakeCollectable ourselves. + AllowAnnotations = false; } else if (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" || FName == "IOServiceNameMatching" || + FName == "IORegistryEntrySearchCFProperty" || FName == "IORegistryEntryIDMatching" || FName == "IOOpenFirmwarePathMatching") { // Part of <rdar://problem/6961230>. (IOKit) @@ -993,6 +1092,8 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // libdispatch finalizers. ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName.startswith("NSLog")) { + S = getDoNothingSummary(); } else if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) { // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can @@ -1090,8 +1191,13 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { } while (0); + // If we got all the way here without any luck, use a default summary. + if (!S) + S = getDefaultSummary(); + // Annotations override defaults. - updateSummaryFromAnnotations(S, FD); + if (AllowAnnotations) + updateSummaryFromAnnotations(S, FD); FuncSummaries[FD] = S; return S; @@ -1152,7 +1258,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, if (!FD) return; - RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); + assert(Summ && "Must have a summary to add annotations to."); + RetainSummaryTemplate Template(Summ, *this); // Effects on the parameters. unsigned parm_idx = 0; @@ -1200,7 +1307,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, if (!MD) return; - RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); + assert(Summ && "Must have a valid summary to add annotations to"); + RetainSummaryTemplate Template(Summ, *this); bool isTrackedLoc = false; // Effects on the receiver. @@ -1341,10 +1449,15 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, // because the reference count is quite possibly handled by a delegate // method. if (S.isKeywordSelector()) { - const std::string &str = S.getAsString(); - assert(!str.empty()); - if (StrInStrNoCase(str, "delegate:") != StringRef::npos) - ReceiverEff = StopTracking; + for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) { + StringRef Slot = S.getNameForSlot(i); + if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) { + if (ResultEff == ObjCInitRetE) + ResultEff = RetEffect::MakeNoRet(); + else + ReceiverEff = StopTracking; + } + } } if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing && @@ -1355,56 +1468,46 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, } const RetainSummary * -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, - ProgramStateRef state, - const LocationContext *LC) { - - // We need the type-information of the tracked receiver object - // Retrieve it from the state. - const Expr *Receiver = msg.getInstanceReceiver(); - const ObjCInterfaceDecl *ID = 0; - - // FIXME: Is this really working as expected? There are cases where - // we just use the 'ID' from the message expression. - SVal receiverV; - - if (Receiver) { - receiverV = state->getSValAsScalarOrLoc(Receiver, LC); - - // FIXME: Eventually replace the use of state->get<RefBindings> with - // a generic API for reasoning about the Objective-C types of symbolic - // objects. - if (SymbolRef Sym = receiverV.getAsLocSymbol()) - if (const RefVal *T = state->get<RefBindings>(Sym)) - if (const ObjCObjectPointerType* PT = +RetainSummaryManager::getInstanceMethodSummary(const ObjCMethodCall &Msg, + ProgramStateRef State) { + const ObjCInterfaceDecl *ReceiverClass = 0; + + // We do better tracking of the type of the object than the core ExprEngine. + // See if we have its type in our private state. + // FIXME: Eventually replace the use of state->get<RefBindings> with + // a generic API for reasoning about the Objective-C types of symbolic + // objects. + SVal ReceiverV = Msg.getReceiverSVal(); + if (SymbolRef Sym = ReceiverV.getAsLocSymbol()) + if (const RefVal *T = getRefBinding(State, Sym)) + if (const ObjCObjectPointerType *PT = T->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); + ReceiverClass = PT->getInterfaceDecl(); - // FIXME: this is a hack. This may or may not be the actual method - // that is called. - if (!ID) { - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - } - } else { - // FIXME: Hack for 'super'. - ID = msg.getReceiverInterface(); - } + // If we don't know what kind of object this is, fall back to its static type. + if (!ReceiverClass) + ReceiverClass = Msg.getReceiverInterface(); // FIXME: The receiver could be a reference to a class, meaning that // we should use the class method. - return getInstanceMethodSummary(msg, ID); + // id x = [NSObject class]; + // [x performSelector:... withObject:... afterDelay:...]; + Selector S = Msg.getSelector(); + const ObjCMethodDecl *Method = Msg.getDecl(); + if (!Method && ReceiverClass) + Method = ReceiverClass->getInstanceMethod(S); + + return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(), + ObjCMethodSummaries); } const RetainSummary * -RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, +RetainSummaryManager::getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries) { // Look up a summary in our summary cache. - const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S); + const RetainSummary *Summ = CachedSummaries.find(ID, S); if (!Summ) { Summ = getStandardMethodSummary(MD, S, RetTy); @@ -1413,7 +1516,7 @@ RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, updateSummaryFromAnnotations(Summ, MD); // Memoize the summary. - CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + CachedSummaries[ObjCSummaryKey(ID, S)] = Summ; } return Summ; @@ -1430,29 +1533,6 @@ void RetainSummaryManager::InitializeClassMethodSummaries() { addClassMethSummary("NSAutoreleasePool", "addObject", getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Autorelease)); - - // Create the summaries for [NSObject performSelector...]. We treat - // these as 'stop tracking' for the arguments because they are often - // used for delegates that can release the object. When we have better - // inter-procedural analysis we can potentially do something better. This - // workaround is to remove false positives. - const RetainSummary *Summ = - getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); - IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", "inModes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", - "withObject", NULL); } void RetainSummaryManager::InitializeMethodSummaries() { @@ -1558,6 +1638,10 @@ void RetainSummaryManager::InitializeMethodSummaries() { //===----------------------------------------------------------------------===// // AutoreleaseBindings - State used to track objects in autorelease pools. //===----------------------------------------------------------------------===// +#define AUTORELEASE_POOL_MODELING (0) +// We do not currently have complete modeling of autorelease pools. + +#if AUTORELEASE_POOL_MODELING typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; @@ -1605,6 +1689,7 @@ SendAutorelease(ProgramStateRef state, return state->set<AutoreleasePoolContents>(pool, newCnts); } +#endif //===----------------------------------------------------------------------===// // Error reporting. @@ -1690,32 +1775,18 @@ namespace { }; class Leak : public CFRefBug { - const bool isReturn; - protected: - Leak(StringRef name, bool isRet) - : CFRefBug(name), isReturn(isRet) { + public: + Leak(StringRef name) + : CFRefBug(name) { // Leaks should not be reported if they are post-dominated by a sink. setSuppressOnSink(true); } - public: const char *getDescription() const { return ""; } bool isLeak() const { return true; } }; - class LeakAtReturn : public Leak { - public: - LeakAtReturn(StringRef name) - : Leak(name, true) {} - }; - - class LeakWithinFunction : public Leak { - public: - LeakWithinFunction(StringRef name) - : Leak(name, false) {} - }; - //===---------===// // Bug Reports. // //===---------===// @@ -1854,25 +1925,21 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V, return false; } -static bool isPropertyAccess(const Stmt *S, ParentMap &PM) { - unsigned maxDepth = 4; - while (S && maxDepth) { - if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) { - if (!isa<ObjCMessageExpr>(PO->getSyntacticForm())) - return true; - return false; - } - S = PM.getParent(S); - --maxDepth; - } - return false; +static bool isNumericLiteralExpression(const Expr *E) { + // FIXME: This set of cases was copied from SemaExprObjC. + return isa<IntegerLiteral>(E) || + isa<CharacterLiteral>(E) || + isa<FloatingLiteral>(E) || + isa<ObjCBoolLiteralExpr>(E) || + isa<CXXBoolLiteralExpr>(E); } PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). if (!isa<StmtPoint>(N->getLocation())) return NULL; @@ -1881,11 +1948,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, ProgramStateRef CurrSt = N->getState(); const LocationContext *LCtx = N->getLocationContext(); - const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); + const RefVal* CurrT = getRefBinding(CurrSt, Sym); if (!CurrT) return NULL; const RefVal &CurrV = *CurrT; - const RefVal *PrevT = PrevSt->get<RefBindings>(Sym); + const RefVal *PrevT = getRefBinding(PrevSt, Sym); // Create a string buffer to constain all the useful things we want // to tell the user. @@ -1903,6 +1970,24 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, else if (isa<ObjCDictionaryLiteral>(S)) { os << "NSDictionary literal is an object with a +0 retain count"; } + else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { + if (isNumericLiteralExpression(BL->getSubExpr())) + os << "NSNumber literal is an object with a +0 retain count"; + else { + const ObjCInterfaceDecl *BoxClass = 0; + if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) + BoxClass = Method->getClassInterface(); + + // We should always be able to find the boxing class interface, + // but consider this future-proofing. + if (BoxClass) + os << *BoxClass << " b"; + else + os << "B"; + + os << "oxed expression produces an object with a +0 retain count"; + } + } else { if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available). @@ -1913,10 +1998,22 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, os << "function call"; } else { - assert(isa<ObjCMessageExpr>(S)); - // The message expression may have between written directly or as - // a property access. Lazily determine which case we are looking at. - os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); + assert(isa<ObjCMessageExpr>(S)); + CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); + CallEventRef<ObjCMethodCall> Call + = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); + + switch (Call->getMessageKind()) { + case OCM_Message: + os << "Method"; + break; + case OCM_PropertyAccess: + os << "Property"; + break; + case OCM_Subscript: + os << "Subscript"; + break; + } } if (CurrV.getObjKind() == RetEffect::CF) { @@ -2143,9 +2240,8 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, while (N) { ProgramStateRef St = N->getState(); - RefBindings B = St->get<RefBindings>(); - if (!B.lookup(Sym)) + if (!getRefBinding(St, Sym)) break; StoreManager::FindUniqueBinding FB(Sym); @@ -2216,7 +2312,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "allocated object"; // Get the retain count. - const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); + const RefVal* RV = getRefBinding(EndN->getState(), Sym); if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF @@ -2276,8 +2372,15 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); // Get the SourceLocation for the allocation site. + // FIXME: This will crash the analyzer if an allocation comes from an + // implicit call. (Currently there are no such allocations in Cocoa, though.) + const Stmt *AllocStmt; ProgramPoint P = AllocNode->getLocation(); - const Stmt *AllocStmt = cast<PostStmt>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + AllocStmt = Exit->getCalleeContext()->getCallSite(); + else + AllocStmt = cast<PostStmt>(P).getStmt(); + assert(AllocStmt && "All allocations must come from explicit calls"); Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, n->getLocationContext()); // Fill in the description of the bug. @@ -2307,11 +2410,10 @@ class RetainCountChecker check::EndPath, check::PostStmt<BlockExpr>, check::PostStmt<CastExpr>, - check::PostStmt<CallExpr>, - check::PostStmt<CXXConstructExpr>, check::PostStmt<ObjCArrayLiteral>, check::PostStmt<ObjCDictionaryLiteral>, - check::PostObjCMessage, + check::PostStmt<ObjCBoxedExpr>, + check::PostCall, check::PreStmt<ReturnStmt>, check::RegionChanges, eval::Assume, @@ -2330,7 +2432,9 @@ class RetainCountChecker mutable OwningPtr<RetainSummaryManager> Summaries; mutable OwningPtr<RetainSummaryManager> SummariesGC; +#if AUTORELEASE_POOL_MODELING mutable ARCounts::Factory ARCountFactory; +#endif mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; @@ -2382,20 +2486,17 @@ public: bool GCEnabled) const { if (GCEnabled) { if (!leakWithinFunctionGC) - leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when " - "using garbage " - "collection")); + leakWithinFunctionGC.reset(new Leak("Leak of object when using " + "garbage collection")); return leakWithinFunctionGC.get(); } else { if (!leakWithinFunction) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakWithinFunction.reset(new LeakWithinFunction("Leak of object when " - "not using garbage " - "collection (GC) in " - "dual GC/non-GC " - "code")); + leakWithinFunction.reset(new Leak("Leak of object when not using " + "garbage collection (GC) in " + "dual GC/non-GC code")); } else { - leakWithinFunction.reset(new LeakWithinFunction("Leak")); + leakWithinFunction.reset(new Leak("Leak")); } } return leakWithinFunction.get(); @@ -2405,17 +2506,17 @@ public: CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { if (GCEnabled) { if (!leakAtReturnGC) - leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when " - "using garbage collection")); + leakAtReturnGC.reset(new Leak("Leak of returned object when using " + "garbage collection")); return leakAtReturnGC.get(); } else { if (!leakAtReturn) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakAtReturn.reset(new LeakAtReturn("Leak of returned object when " - "not using garbage collection " - "(GC) in dual GC/non-GC code")); + leakAtReturn.reset(new Leak("Leak of returned object when not using " + "garbage collection (GC) in dual " + "GC/non-GC code")); } else { - leakAtReturn.reset(new LeakAtReturn("Leak of returned object")); + leakAtReturn.reset(new Leak("Leak of returned object")); } } return leakAtReturn.get(); @@ -2453,13 +2554,13 @@ public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; - void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; + void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call, + void checkSummary(const RetainSummary &Summ, const CallEvent &Call, CheckerContext &C) const; bool evalCall(const CallExpr *CE, CheckerContext &C) const; @@ -2472,7 +2573,7 @@ public: const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; bool wantsRegionChangeUpdate(ProgramStateRef state) const { return true; @@ -2499,8 +2600,8 @@ public: const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; ProgramStateRef handleSymbolDeath(ProgramStateRef state, - SymbolRef sid, RefVal V, - SmallVectorImpl<SymbolRef> &Leaked) const; + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const; std::pair<ExplodedNode *, ProgramStateRef > handleAutoreleaseCounts(ProgramStateRef state, @@ -2597,7 +2698,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol(); if (!Sym) return; - const RefVal* T = state->get<RefBindings>(Sym); + const RefVal* T = getRefBinding(state, Sym); if (!T) return; @@ -2613,54 +2714,6 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, C.addTransition(state); } -void RetainCountChecker::checkPostStmt(const CallExpr *CE, - CheckerContext &C) const { - if (C.wasInlined) - return; - - // Get the callee. - ProgramStateRef state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee, C.getLocationContext()); - - RetainSummaryManager &Summaries = getSummaryManager(C); - const RetainSummary *Summ = 0; - - // FIXME: Better support for blocks. For now we stop tracking anything - // that is passed to blocks. - // FIXME: Need to handle variables that are "captured" by the block. - if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { - Summ = Summaries.getPersistentStopSummary(); - } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) { - Summ = Summaries.getSummary(FD); - } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) { - if (const CXXMethodDecl *MD = me->getMethodDecl()) - Summ = Summaries.getSummary(MD); - } - - if (!Summ) - Summ = Summaries.getDefaultSummary(); - - checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); -} - -void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, - CheckerContext &C) const { - const CXXConstructorDecl *Ctor = CE->getConstructor(); - if (!Ctor) - return; - - RetainSummaryManager &Summaries = getSummaryManager(C); - const RetainSummary *Summ = Summaries.getSummary(Ctor); - - // If we didn't get a summary, this constructor doesn't affect retain counts. - if (!Summ) - return; - - ProgramStateRef state = C.getState(); - checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); -} - void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Expr *Ex) const { ProgramStateRef state = C.getState(); @@ -2670,7 +2723,7 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Stmt *child = *it; SVal V = state->getSVal(child, pred->getLocationContext()); if (SymbolRef sym = V.getAsSymbol()) - if (const RefVal* T = state->get<RefBindings>(sym)) { + if (const RefVal* T = getRefBinding(state, sym)) { RefVal::Kind hasErr = (RefVal::Kind) 0; state = updateSymbol(state, sym, *T, MayEscape, hasErr, C); if (hasErr) { @@ -2685,8 +2738,8 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C, if (SymbolRef sym = state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { QualType ResultTy = Ex->getType(); - state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC, - ResultTy)); + state = setRefBinding(state, sym, + RefVal::makeNotOwned(RetEffect::ObjC, ResultTy)); } C.addTransition(state); @@ -2704,30 +2757,34 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, processObjCLiterals(C, DL); } -void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, - CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - RetainSummaryManager &Summaries = getSummaryManager(C); +void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, + CheckerContext &C) const { + const ExplodedNode *Pred = C.getPredecessor(); + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); - const RetainSummary *Summ; - if (Msg.isInstanceMessage()) { - const LocationContext *LC = C.getLocationContext(); - Summ = Summaries.getInstanceMethodSummary(Msg, state, LC); - } else { - Summ = Summaries.getClassMethodSummary(Msg); + if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + State = setRefBinding(State, Sym, + RefVal::makeNotOwned(RetEffect::ObjC, ResultTy)); } - // If we didn't get a summary, this message doesn't affect retain counts. - if (!Summ) + C.addTransition(State); +} + +void RetainCountChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (C.wasInlined) return; - checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C); + RetainSummaryManager &Summaries = getSummaryManager(C); + const RetainSummary *Summ = Summaries.getSummary(Call, C.getState()); + checkSummary(*Summ, Call, C); } /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. -/// While the the return type can be queried directly from RetEx, when +/// While the return type can be queried directly from RetEx, when /// invoking class methods we augment to the return type to be that of /// a pointer to the class (as opposed it just being id). // FIXME: We may be able to do this with related result types instead. @@ -2754,7 +2811,7 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { } void RetainCountChecker::checkSummary(const RetainSummary &Summ, - const CallOrObjCMessage &CallOrMsg, + const CallEvent &CallOrMsg, CheckerContext &C) const { ProgramStateRef state = C.getState(); @@ -2767,7 +2824,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, SVal V = CallOrMsg.getArgSVal(idx); if (SymbolRef Sym = V.getAsLocSymbol()) { - if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) { + if (const RefVal *T = getRefBinding(state, Sym)) { state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C); if (hasErr) { ErrorRange = CallOrMsg.getArgSourceRange(idx); @@ -2780,17 +2837,18 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Evaluate the effect on the message receiver. bool ReceiverIsTracked = false; - if (!hasErr && CallOrMsg.isObjCMessage()) { - const LocationContext *LC = C.getLocationContext(); - SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC); - if (SymbolRef Sym = Receiver.getAsLocSymbol()) { - if (const RefVal *T = state->get<RefBindings>(Sym)) { - ReceiverIsTracked = true; - state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), - hasErr, C); - if (hasErr) { - ErrorRange = CallOrMsg.getReceiverSourceRange(); - ErrorSym = Sym; + if (!hasErr) { + const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg); + if (MsgInvocation) { + if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { + if (const RefVal *T = getRefBinding(state, Sym)) { + ReceiverIsTracked = true; + state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), + hasErr, C); + if (hasErr) { + ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange(); + ErrorSym = Sym; + } } } } @@ -2827,11 +2885,11 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, if (!Sym) break; - // Use the result type from callOrMsg as it automatically adjusts + // Use the result type from the CallEvent as it automatically adjusts // for methods/functions that return references. - QualType ResultTy = CallOrMsg.getResultType(C.getASTContext()); - state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), - ResultTy)); + QualType ResultTy = CallOrMsg.getResultType(); + state = setRefBinding(state, Sym, RefVal::makeOwned(RE.getObjKind(), + ResultTy)); // FIXME: Add a flag to the checker where allocations are assumed to // *not* fail. (The code below is out-of-date, though.) @@ -2856,8 +2914,8 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *. QualType ResultTy = GetReturnType(Ex, C.getASTContext()); - state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), - ResultTy)); + state = setRefBinding(state, Sym, RefVal::makeNotOwned(RE.getObjKind(), + ResultTy)); break; } } @@ -2895,25 +2953,37 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; switch (E) { - default: break; - case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break; - case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break; - case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing : - NewAutoreleasePool; break; + default: + break; + case IncRefMsg: + E = IgnoreRetainMsg ? DoNothing : IncRef; + break; + case DecRefMsg: + E = IgnoreRetainMsg ? DoNothing : DecRef; + break; + case DecRefMsgAndStopTracking: + E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTracking; + break; + case MakeCollectable: + E = C.isObjCGCEnabled() ? DecRef : DoNothing; + break; + case NewAutoreleasePool: + E = C.isObjCGCEnabled() ? DoNothing : NewAutoreleasePool; + break; } // Handle all use-after-releases. if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) { V = V ^ RefVal::ErrorUseAfterRelease; hasErr = V.getKind(); - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); } switch (E) { case DecRefMsg: case IncRefMsg: case MakeCollectable: + case DecRefMsgAndStopTracking: llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); case Dealloc: @@ -2931,7 +3001,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, // The object immediately transitions to the released state. V = V ^ RefVal::Released; V.clearCounts(); - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); case RefVal::NotOwned: V = V ^ RefVal::ErrorDeallocNotOwned; hasErr = V.getKind(); @@ -2941,7 +3011,10 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case NewAutoreleasePool: assert(!C.isObjCGCEnabled()); - return state->add<AutoreleaseStack>(sym); +#if AUTORELEASE_POOL_MODELING + state = state->add<AutoreleaseStack>(sym); +#endif + return state; case MayEscape: if (V.getKind() == RefVal::Owned) { @@ -2959,12 +3032,16 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, return state; // Update the autorelease counts. + // TODO: AutoreleasePoolContents are not currently used. We will need to + // call SendAutorelease after it's wired up. +#if AUTORELEASE_POOL_MODELING state = SendAutorelease(state, ARCountFactory, sym); +#endif V = V.autorelease(); break; case StopTracking: - return state->remove<RefBindings>(sym); + return removeRefBinding(state, sym); case IncRef: switch (V.getKind()) { @@ -2982,11 +3059,9 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, } break; - case SelfOwn: - V = V ^ RefVal::NotOwned; - // Fall-through. case DecRef: case DecRefBridgedTransfered: + case DecRefAndStopTracking: switch (V.getKind()) { default: // case 'RefVal::Released' handled above. @@ -2997,13 +3072,18 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, if (V.getCount() == 1) V = V ^ (E == DecRefBridgedTransfered ? RefVal::NotOwned : RefVal::Released); + else if (E == DecRefAndStopTracking) + return removeRefBinding(state, sym); + V = V - 1; break; case RefVal::NotOwned: - if (V.getCount() > 0) + if (V.getCount() > 0) { + if (E == DecRefAndStopTracking) + return removeRefBinding(state, sym); V = V - 1; - else { + } else { V = V ^ RefVal::ErrorReleaseNotOwned; hasErr = V.getKind(); } @@ -3018,7 +3098,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, } break; } - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); } void RetainCountChecker::processNonLeakError(ProgramStateRef St, @@ -3117,7 +3197,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // If the receiver is unknown, conjure a return value. SValBuilder &SVB = C.getSValBuilder(); unsigned Count = C.getCurrentBlockCount(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); + RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); } state = state->BindExpr(CE, LCtx, RetVal, false); @@ -3126,9 +3206,9 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (const MemRegion *ArgRegion = RetVal.getAsRegion()) { // Save the refcount status of the argument. SymbolRef Sym = RetVal.getAsLocSymbol(); - RefBindings::data_type *Binding = 0; + const RefVal *Binding = 0; if (Sym) - Binding = state->get<RefBindings>(Sym); + Binding = getRefBinding(state, Sym); // Invalidate the argument region. unsigned Count = C.getCurrentBlockCount(); @@ -3136,7 +3216,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Restore the refcount status of the argument. if (Binding) - state = state->set<RefBindings>(Sym, *Binding); + state = setRefBinding(state, Sym, *Binding); } C.addTransition(state); @@ -3175,7 +3255,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, return; // Get the reference count binding (if any). - const RefVal *T = state->get<RefBindings>(Sym); + const RefVal *T = getRefBinding(state, Sym); if (!T) return; @@ -3208,7 +3288,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, } // Update the binding. - state = state->set<RefBindings>(Sym, X); + state = setRefBinding(state, Sym, X); ExplodedNode *Pred = C.addTransition(state); // At this point we have updated the state properly. @@ -3230,29 +3310,27 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, return; // Get the updated binding. - T = state->get<RefBindings>(Sym); + T = getRefBinding(state, Sym); assert(T); X = *T; // Consult the summary of the enclosing method. RetainSummaryManager &Summaries = getSummaryManager(C); const Decl *CD = &Pred->getCodeDecl(); + RetEffect RE = RetEffect::MakeNoRet(); + // FIXME: What is the convention for blocks? Is there one? if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { - // Unlike regular functions, /all/ ObjC methods are assumed to always - // follow Cocoa retain-count conventions, not just those with special - // names or attributes. const RetainSummary *Summ = Summaries.getMethodSummary(MD); - RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet(); - checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); + RE = Summ->getRetEffect(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { + if (!isa<CXXMethodDecl>(FD)) { + const RetainSummary *Summ = Summaries.getFunctionSummary(FD); + RE = Summ->getRetEffect(); + } } - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { - if (!isa<CXXMethodDecl>(FD)) - if (const RetainSummary *Summ = Summaries.getSummary(FD)) - checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X, - Sym, state); - } + checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); } void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, @@ -3283,7 +3361,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (hasError) { // Generate an error node. - state = state->set<RefBindings>(Sym, X); + state = setRefBinding(state, Sym, X); static SimpleProgramPointTag ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak"); @@ -3303,7 +3381,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (RE.isOwned()) { // Trying to return a not owned object to a caller expecting an // owned object. - state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); + state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned); static SimpleProgramPointTag ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned"); @@ -3346,7 +3424,11 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // To test (3), generate a new state with the binding added. If it is // the same state, then it escapes (since the store cannot represent // the binding). - escapes = (state == (state->bindLoc(*regionLoc, val))); + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = state->getSVal(regionLoc->getRegion()); + if (StoredVal != val) + escapes = (state == (state->bindLoc(*regionLoc, val))); } if (!escapes) { // Case 4: We do not currently model what happens when a symbol is @@ -3406,7 +3488,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!invalidated) return state; @@ -3423,7 +3505,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, if (WhitelistedSymbols.count(sym)) continue; // Remove any existing reference-count binding. - state = state->remove<RefBindings>(sym); + state = removeRefBinding(state, sym); } return state; } @@ -3463,7 +3545,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, V.setCount(Cnt - ACnt); V.setAutoreleaseCount(0); } - state = state->set<RefBindings>(Sym, V); + state = setRefBinding(state, Sym, V); ExplodedNode *N = Bd.MakeNode(state, Pred); if (N == 0) state = 0; @@ -3473,7 +3555,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, // Woah! More autorelease counts then retain counts left. // Emit hard error. V = V ^ RefVal::ErrorOverAutorelease; - state = state->set<RefBindings>(Sym, V); + state = setRefBinding(state, Sym, V); if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) { SmallString<128> sbuf; @@ -3507,10 +3589,10 @@ RetainCountChecker::handleSymbolDeath(ProgramStateRef state, hasLeak = (V.getCount() > 0); if (!hasLeak) - return state->remove<RefBindings>(sid); + return removeRefBinding(state, sid); Leaked.push_back(sid); - return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); + return setRefBinding(state, sid, V ^ RefVal::ErrorLeak); } ExplodedNode * @@ -3559,7 +3641,7 @@ void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const { // If the current LocationContext has a parent, don't check for leaks. // We will do that later. - // FIXME: we should instead check for imblances of the retain/releases, + // FIXME: we should instead check for imbalances of the retain/releases, // and suggest annotations. if (Ctx.getLocationContext()->getParent()) return; @@ -3641,6 +3723,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Debug printing of refcount bindings and autorelease pools. //===----------------------------------------------------------------------===// +#if AUTORELEASE_POOL_MODELING static void PrintPool(raw_ostream &Out, SymbolRef Sym, ProgramStateRef State) { Out << ' '; @@ -3654,7 +3737,6 @@ static void PrintPool(raw_ostream &Out, SymbolRef Sym, if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) Out << '(' << I.getKey() << ',' << I.getData() << ')'; - Out << '}'; } @@ -3664,6 +3746,7 @@ static bool UsesAutorelease(ProgramStateRef state) { return !state->get<AutoreleaseStack>().isEmpty() || state->get<AutoreleasePoolContents>(SymbolRef()); } +#endif void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { @@ -3679,6 +3762,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << NL; } +#if AUTORELEASE_POOL_MODELING // Print the autorelease stack. if (UsesAutorelease(State)) { Out << Sep << NL << "AR pool stack:"; @@ -3690,6 +3774,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << NL; } +#endif } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7b1f0b139c11..ca2a55d1e7bb 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -53,9 +53,9 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, BugReport *report = new BugReport(*BT, BT->getDescription(), N); + report->disablePathPruning(); report->addRange(RetE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, RetE, report); C.EmitReport(report); } diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 3745d4ad3943..731dd66b460b 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -116,7 +116,7 @@ namespace ento { bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) + if (!FD || FD->getKind() != Decl::Function) return false; ASTContext &Ctx = C.getASTContext(); diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp new file mode 100644 index 000000000000..b97cd6c66b93 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -0,0 +1,84 @@ +//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These checkers print various aspects of the ExprEngine's traversal of the CFG +// as it builds the ExplodedGraph. +// +//===----------------------------------------------------------------------===// +#include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class TraversalDumper : public Checker< check::BranchCondition, + check::EndPath > { +public: + void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkEndPath(CheckerContext &C) const; +}; +} + +void TraversalDumper::checkBranchCondition(const Stmt *Condition, + CheckerContext &C) const { + // Special-case Objective-C's for-in loop, which uses the entire loop as its + // condition. We just print the collection expression. + const Stmt *Parent = dyn_cast<ObjCForCollectionStmt>(Condition); + if (!Parent) { + const ParentMap &Parents = C.getLocationContext()->getParentMap(); + Parent = Parents.getParent(Condition); + } + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + SourceLocation Loc = Parent->getLocStart(); + llvm::outs() << C.getSourceManager().getSpellingLineNumber(Loc) << " " + << Parent->getStmtClassName() << "\n"; +} + +void TraversalDumper::checkEndPath(CheckerContext &C) const { + llvm::outs() << "--END PATH--\n"; +} + +void ento::registerTraversalDumper(CheckerManager &mgr) { + mgr.registerChecker<TraversalDumper>(); +} + +//------------------------------------------------------------------------------ + +namespace { +class CallDumper : public Checker< check::PreCall > { +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; +} + +void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + unsigned Indentation = 0; + for (const LocationContext *LC = C.getLocationContext()->getParent(); + LC != 0; LC = LC->getParent()) + ++Indentation; + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + llvm::outs().indent(Indentation); + Call.dump(llvm::outs()); +} + +void ento::registerCallDumper(CheckerManager &mgr) { + mgr.registerChecker<CallDumper>(); +} diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index a30f6d53287f..70a33c76db0c 100644 --- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -99,8 +99,9 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, // Emit the bug report. BugReport *R = new BugReport(*BT, BT->getDescription(), N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, R); R->addRange(Ex->getSourceRange()); + R->disablePathPruning(); Ctx.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index d57767eec9fb..675b38a5df26 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -94,6 +94,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); + R->disablePathPruning(); // need location of block C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index c3c9ed72345e..e220499d73f4 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -76,12 +76,12 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); } else - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, B, report); + + report->disablePathPruning(); C.EmitReport(report); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index 0297c4eb14e9..6ae3c1875fbf 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -42,9 +42,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - A->getIdx(), - R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, A->getIdx(), R); C.EmitReport(R); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 78f7fa61b288..14a884e01b64 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -78,8 +78,9 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex, R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, ex, R); } + R->disablePathPruning(); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 60e665fed3b0..d35455c2191b 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -224,8 +224,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, arg, report); C.EmitReport(report); return true; @@ -256,7 +255,7 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, (void) ReportZeroByteAllocation(C, falseState, arg, fn); return; } - // Assume the the value is non-zero going forward. + // Assume the value is non-zero going forward. assert(trueState); if (trueState != state) C.addTransition(trueState); @@ -292,7 +291,7 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C, } } - // Assume the the value is non-zero going forward. + // Assume the value is non-zero going forward. assert(trueState); if (trueState != state) C.addTransition(trueState); @@ -325,7 +324,11 @@ void UnixAPIChecker::CheckVallocZero(CheckerContext &C, void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - StringRef FName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef FName = C.getCalleeName(FD); if (FName.empty()) return; diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 38c9cc1f333c..fab4adf3e0e2 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -69,8 +69,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind, BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(SizeE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SizeE, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, SizeE, report); C.EmitReport(report); return; } diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index f7c7c0ce3469..bdc96278f76a 100644 --- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -46,7 +46,7 @@ class WalkAST : public StmtVisitor<WalkAST> { visited. */ PostVisited /**< A CallExpr to this FunctionDecl is in the worklist, and the body has been visited. */ - } K; + }; /// A DenseMap that records visited states of FunctionDecls. llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp new file mode 100644 index 000000000000..884b0faa9ed4 --- /dev/null +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -0,0 +1,38 @@ +//===--- APSIntType.cpp - Simple record of the type of APSInts ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" + +using namespace clang; +using namespace ento; + +APSIntType::RangeTestResultKind +APSIntType::testInRange(const llvm::APSInt &Value) const { + // Negative numbers cannot be losslessly converted to unsigned type. + if (IsUnsigned && Value.isSigned() && Value.isNegative()) + return RTR_Below; + + // Signed integers can be converted to signed integers of the same width + // or (if positive) unsigned integers with one fewer bit. + // Unsigned integers can be converted to unsigned integers of the same width + // or signed integers with one more bit. + unsigned MinBits; + if (Value.isSigned()) + MinBits = Value.getMinSignedBits() - IsUnsigned; + else + MinBits = Value.getActiveBits() + !IsUnsigned; + + if (MinBits <= BitWidth) + return RTR_Within; + + if (Value.isSigned() && Value.isNegative()) + return RTR_Below; + else + return RTR_Above; +} diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index eeaed2de8367..5aac6406f69f 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -25,18 +25,18 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnalysisPurgeMode purge, bool eager, bool trim, bool useUnoptimizedCFG, - bool addImplicitDtors, bool addInitializers, + bool addImplicitDtors, bool eagerlyTrimEGraph, AnalysisIPAMode ipa, unsigned inlineMaxStack, unsigned inlineMaxFunctionSize, AnalysisInliningMode IMode, bool NoRetry) - : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), + : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true), Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), - AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), + MaxNodes(maxnodes), MaxVisit(maxvisit), VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), EagerlyAssume(eager), TrimGraph(trim), EagerlyTrimEGraph(eagerlyTrimEGraph), @@ -59,7 +59,6 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, CreateStoreMgr(ParentAM.CreateStoreMgr), CreateConstraintMgr(ParentAM.CreateConstraintMgr), CheckerMgr(ParentAM.CheckerMgr), - AScope(ScopeDecl), MaxNodes(ParentAM.MaxNodes), MaxVisit(ParentAM.MaxVisit), VisualizeEGDot(ParentAM.VisualizeEGDot), diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 2d9addd2cef9..8897756a92d9 100644 --- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" @@ -53,18 +54,25 @@ class BasicConstraintManager ProgramState::IntSetTy::Factory ISetFactory; public: BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine) - : SimpleConstraintManager(subengine), + : SimpleConstraintManager(subengine, statemgr.getBasicVals()), ISetFactory(statemgr.getAllocator()) {} - ProgramStateRef assumeSymNE(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); + ProgramStateRef assumeSymEquality(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment, + bool Assumption); - ProgramStateRef assumeSymEQ(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); + ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + return assumeSymEquality(State, Sym, V, Adjustment, false); + } + + ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + return assumeSymEquality(State, Sym, V, Adjustment, true); + } ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, @@ -108,6 +116,9 @@ public: ProgramStateRef removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper); + bool performTest(llvm::APSInt SymVal, llvm::APSInt Adjustment, + BinaryOperator::Opcode Op, llvm::APSInt ComparisonVal); + void print(ProgramStateRef state, raw_ostream &Out, const char* nl, @@ -122,60 +133,94 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, return new BasicConstraintManager(statemgr, subengine); } -ProgramStateRef -BasicConstraintManager::assumeSymNE(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) { - // First, determine if sym == X, where X+Adjustment != V. - llvm::APSInt Adjusted = V-Adjustment; - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X != Adjusted); - return isFeasible ? state : NULL; - } - - // Second, determine if sym+Adjustment != V. - if (isNotEqual(state, sym, Adjusted)) - return state; - - // If we reach here, sym is not a constant and we don't know if it is != V. - // Make that assumption. - return AddNE(state, sym, Adjusted); +// FIXME: This is a more general utility and should live somewhere else. +bool BasicConstraintManager::performTest(llvm::APSInt SymVal, + llvm::APSInt Adjustment, + BinaryOperator::Opcode Op, + llvm::APSInt ComparisonVal) { + APSIntType Type(Adjustment); + Type.apply(SymVal); + Type.apply(ComparisonVal); + SymVal += Adjustment; + + assert(BinaryOperator::isComparisonOp(Op)); + BasicValueFactory &BVF = getBasicVals(); + const llvm::APSInt *Result = BVF.evalAPSInt(Op, SymVal, ComparisonVal); + assert(Result && "Comparisons should always have valid results."); + + return Result->getBoolValue(); } -ProgramStateRef -BasicConstraintManager::assumeSymEQ(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) { - // First, determine if sym == X, where X+Adjustment != V. - llvm::APSInt Adjusted = V-Adjustment; - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X == Adjusted); - return isFeasible ? state : NULL; +ProgramStateRef +BasicConstraintManager::assumeSymEquality(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment, + bool Assumption) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(V) != APSIntType::RTR_Within) + return Assumption ? NULL : State; + + // Get the symbol type. + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + APSIntType SymbolType = BVF.getAPSIntType(Sym->getType(Ctx)); + + // First, see if the adjusted value is within range for the symbol. + llvm::APSInt Adjusted = AdjustmentType.convert(V) - Adjustment; + if (SymbolType.testInRange(Adjusted) != APSIntType::RTR_Within) + return Assumption ? NULL : State; + + // Now we can do things properly in the symbol space. + SymbolType.apply(Adjusted); + + // Second, determine if sym == X, where X+Adjustment != V. + if (const llvm::APSInt *X = getSymVal(State, Sym)) { + bool IsFeasible = (*X == Adjusted); + return (IsFeasible == Assumption) ? State : NULL; } - // Second, determine if sym+Adjustment != V. - if (isNotEqual(state, sym, Adjusted)) - return NULL; + // Third, determine if we already know sym+Adjustment != V. + if (isNotEqual(State, Sym, Adjusted)) + return Assumption ? NULL : State; - // If we reach here, sym is not a constant and we don't know if it is == V. - // Make that assumption. - return AddEQ(state, sym, Adjusted); + // If we reach here, sym is not a constant and we don't know if it is != V. + // Make the correct assumption. + if (Assumption) + return AddEQ(State, Sym, Adjusted); + else + return AddNE(State, Sym, Adjusted); } // The logic for these will be handled in another ConstraintManager. +// Approximate it here anyway by handling some edge cases. ProgramStateRef BasicConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Is 'V' the smallest possible value? - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + APSIntType ComparisonType(V), AdjustmentType(Adjustment); + + // Is 'V' out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V > ComparisonType.convert(Max)) { + // This path is trivially feasible. + return state; + } + + // Is 'V' the smallest possible value, or out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V <= ComparisonType.convert(Min)) { // sym cannot be any value less than 'V'. This path is infeasible. return NULL; } + // Reject a path if the value of sym is a constant X and !(X+Adj < V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_LT, V); + return isFeasible ? state : NULL; + } + // FIXME: For now have assuming x < y be the same as assuming sym != V; return assumeSymNE(state, sym, V, Adjustment); } @@ -185,12 +230,28 @@ BasicConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Is 'V' the largest possible value? - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + APSIntType ComparisonType(V), AdjustmentType(Adjustment); + + // Is 'V' the largest possible value, or out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V >= ComparisonType.convert(Max)) { // sym cannot be any value greater than 'V'. This path is infeasible. return NULL; } + // Is 'V' out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V < ComparisonType.convert(Min)) { + // This path is trivially feasible. + return state; + } + + // Reject a path if the value of sym is a constant X and !(X+Adj > V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_GT, V); + return isFeasible ? state : NULL; + } + // FIXME: For now have assuming x > y be the same as assuming sym != V; return assumeSymNE(state, sym, V, Adjustment); } @@ -200,25 +261,33 @@ BasicConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Reject a path if the value of sym is a constant X and !(X+Adj >= V). - if (const llvm::APSInt *X = getSymVal(state, sym)) { - bool isFeasible = (*X >= V-Adjustment); - return isFeasible ? state : NULL; - } + APSIntType ComparisonType(V), AdjustmentType(Adjustment); - // Sym is not a constant, but it is worth looking to see if V is the - // maximum integer value. - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - llvm::APSInt Adjusted = V-Adjustment; + // Is 'V' the largest possible value, or out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + ComparisonType.apply(Max); - // If we know that sym != V (after adjustment), then this condition - // is infeasible since there is no other value greater than V. - bool isFeasible = !isNotEqual(state, sym, Adjusted); - - // If the path is still feasible then as a consequence we know that + if (V > Max) { + // sym cannot be any value greater than 'V'. This path is infeasible. + return NULL; + } else if (V == Max) { + // If the path is feasible then as a consequence we know that // 'sym+Adjustment == V' because there are no larger values. // Add this constraint. - return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + return assumeSymEQ(state, sym, V, Adjustment); + } + + // Is 'V' out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V < ComparisonType.convert(Min)) { + // This path is trivially feasible. + return state; + } + + // Reject a path if the value of sym is a constant X and !(X+Adj >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_GE, V); + return isFeasible ? state : NULL; } return state; @@ -229,25 +298,33 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Reject a path if the value of sym is a constant X and !(X+Adj <= V). - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X <= V-Adjustment); - return isFeasible ? state : NULL; - } + APSIntType ComparisonType(V), AdjustmentType(Adjustment); - // Sym is not a constant, but it is worth looking to see if V is the - // minimum integer value. - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - llvm::APSInt Adjusted = V-Adjustment; + // Is 'V' out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V > ComparisonType.convert(Max)) { + // This path is trivially feasible. + return state; + } - // If we know that sym != V (after adjustment), then this condition - // is infeasible since there is no other value less than V. - bool isFeasible = !isNotEqual(state, sym, Adjusted); + // Is 'V' the smallest possible value, or out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + ComparisonType.apply(Min); - // If the path is still feasible then as a consequence we know that + if (V < Min) { + // sym cannot be any value less than 'V'. This path is infeasible. + return NULL; + } else if (V == Min) { + // If the path is feasible then as a consequence we know that // 'sym+Adjustment == V' because there are no smaller values. // Add this constraint. - return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + return assumeSymEQ(state, sym, V, Adjustment); + } + + // Reject a path if the value of sym is a constant X and !(X+Adj >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_LE, V); + return isFeasible ? state : NULL; } return state; @@ -256,8 +333,10 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state, ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) { - // Create a new state with the old binding replaced. - return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V)); + // Now that we have an actual value, we can throw out the NE-set. + // Create a new state with the old bindings replaced. + state = state->remove<ConstNotEq>(sym); + return state->set<ConstEq>(sym, &getBasicVals().getValue(V)); } ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, @@ -269,7 +348,7 @@ ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); // Now add V to the NE set. - S = ISetFactory.add(S, &state->getBasicVals().getValue(V)); + S = ISetFactory.add(S, &getBasicVals().getValue(V)); // Create a new state with the old binding replaced. return state->set<ConstNotEq>(sym, S); @@ -289,7 +368,7 @@ bool BasicConstraintManager::isNotEqual(ProgramStateRef state, const ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); // See if V is present in the NE-set. - return T ? T->contains(&state->getBasicVals().getValue(V)) : false; + return T ? T->contains(&getBasicVals().getValue(V)) : false; } bool BasicConstraintManager::isEqual(ProgramStateRef state, diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index fe96700772d6..20c73612c481 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -13,6 +13,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index a2642120b70f..7ba2fa7fdd0d 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -48,6 +48,10 @@ static inline const Stmt *GetStmt(const ProgramPoint &P) { return SP->getStmt(); else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) return BE->getSrc()->getTerminator(); + else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) + return CE->getCallExpr(); + else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) + return CEE->getCalleeContext()->getCallSite(); return 0; } @@ -427,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); PD.getActivePath().push_front(C); @@ -437,21 +441,23 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); PD.popActivePath(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - assert(!PD.getActivePath().empty()); - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SMgr); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -864,6 +870,7 @@ public: void rawAddEdge(PathDiagnosticLocation NewLoc); void addContext(const Stmt *S); + void addContext(const PathDiagnosticLocation &L); void addExtendedContext(const Stmt *S); }; } // end anonymous namespace @@ -1031,7 +1038,10 @@ void EdgeBuilder::addContext(const Stmt *S) { return; PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); + addContext(L); +} +void EdgeBuilder::addContext(const PathDiagnosticLocation &L) { while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1051,6 +1061,78 @@ void EdgeBuilder::addContext(const Stmt *S) { CLocs.push_back(L); } +// Cone-of-influence: support the reverse propagation of "interesting" symbols +// and values by tracing interesting calculations backwards through evaluated +// expressions along a path. This is probably overly complicated, but the idea +// is that if an expression computed an "interesting" value, the child +// expressions are are also likely to be "interesting" as well (which then +// propagates to the values they in turn compute). This reverse propagation +// is needed to track interesting correlations across function call boundaries, +// where formal arguments bind to actual arguments, etc. This is also needed +// because the constraint solver sometimes simplifies certain symbolic values +// into constants when appropriate, and this complicates reasoning about +// interesting values. +typedef llvm::DenseSet<const Expr *> InterestingExprs; + +static void reversePropagateIntererstingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const Expr *Ex, + const LocationContext *LCtx) { + SVal V = State->getSVal(Ex, LCtx); + if (!(R.isInteresting(V) || IE.count(Ex))) + return; + + switch (Ex->getStmtClass()) { + default: + if (!isa<CastExpr>(Ex)) + break; + // Fall through. + case Stmt::BinaryOperatorClass: + case Stmt::UnaryOperatorClass: { + for (Stmt::const_child_iterator CI = Ex->child_begin(), + CE = Ex->child_end(); + CI != CE; ++CI) { + if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) { + IE.insert(child); + SVal ChildV = State->getSVal(child, LCtx); + R.markInteresting(ChildV); + } + break; + } + } + } + + R.markInteresting(V); +} + +static void reversePropagateInterestingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const LocationContext *CalleeCtx, + const LocationContext *CallerCtx) +{ + // FIXME: Handle non-CallExpr-based CallEvents. + const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame(); + const Stmt *CallSite = Callee->getCallSite(); + if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { + FunctionDecl::param_const_iterator PI = FD->param_begin(), + PE = FD->param_end(); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + if (const Expr *ArgE = *AI) { + if (const ParmVarDecl *PD = *PI) { + Loc LV = State->getLValue(PD, CalleeCtx); + if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) + IE.insert(ArgE); + } + } + } + } + } +} + static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, @@ -1058,6 +1140,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); StackDiagVector CallStack; + InterestingExprs IE; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1066,16 +1149,27 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { - const StackFrameContext *LCtx = - CE->getLocationContext()->getCurrentStackFrame(); - PathDiagnosticLocation Loc(LCtx->getCallSite(), - PDB.getSourceManager(), - LCtx); - EB.addEdge(Loc, true); - EB.flushLocations(); + if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + if (const Expr *Ex = PS->getStmtAs<Expr>()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SM); + + EB.addEdge(C->callReturn, true); + EB.flushLocations(); + PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); CallStack.push_back(StackDiagPair(C, N)); @@ -1092,26 +1186,26 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addEdge(pos); // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); EB.flushLocations(); PD.popActivePath(); - assert(!PD.getActivePath().empty()); PDB.LC = N->getLocationContext(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { - const Decl * Caller = CE->getLocationContext()->getDecl(); + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SM); - EB.addContext(CE->getCallExpr()); + EB.addContext(C->getLocation()); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -1127,7 +1221,19 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PDB.LC = N->getLocationContext(); // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + // Does this represent entering a call? If so, look at propagating + // interesting symbols across call boundaries. + if (NextNode) { + const LocationContext *CallerCtx = NextNode->getLocationContext(); + const LocationContext *CalleeCtx = PDB.LC; + if (CallerCtx != CalleeCtx) { + reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), + CalleeCtx, CallerCtx); + } + } + const CFGBlock &Blk = *BE->getSrc(); const Stmt *Term = Blk.getTerminator(); @@ -1430,9 +1536,12 @@ void BugReporter::FlushReports() { I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); - typedef llvm::FoldingSet<BugReportEquivClass> SetTy; - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; + // We need to flush reports in deterministic order to ensure the order + // of the reports is consistent between runs. + typedef std::vector<BugReportEquivClass *> ContVecTy; + for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end(); + EI != EE; ++EI){ + BugReportEquivClass& EQ = **EI; FlushReport(EQ); } @@ -1768,9 +1877,11 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, } while(finalReportConfigToken != originalReportConfigToken); // Finally, prune the diagnostic path of uninteresting stuff. - bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); - assert(hasSomethingInteresting); - (void) hasSomethingInteresting; + if (R->shouldPrunePath()) { + bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); + assert(hasSomethingInteresting); + (void) hasSomethingInteresting; + } } void BugReporter::Register(BugType *BT) { diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 65324868518e..46aa9e2b9123 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -34,15 +34,23 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { // a[0], p->f, *p const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { - if (U->getOpcode() == UO_Deref) - return U->getSubExpr()->IgnoreParenCasts(); - } - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { - return ME->getBase()->IgnoreParenCasts(); - } - else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { - return AE->getBase(); + while (true) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { + assert(B->isAssignmentOp()); + S = B->getLHS()->IgnoreParenCasts(); + continue; + } + else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { + if (U->getOpcode() == UO_Deref) + return U->getSubExpr()->IgnoreParenCasts(); + } + else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { + return ME->getBase()->IgnoreParenCasts(); + } + else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { + return AE->getBase(); + } + break; } return NULL; @@ -55,14 +63,6 @@ const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { return NULL; } -const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) { - // Callee is checked as a PreVisit to the CallExpr. - const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) - return CE->getCallee(); - return NULL; -} - const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) @@ -119,10 +119,16 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, return NULL; if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; + // Make sure the region is actually bound to value V here. + // This is necessary because the region may not actually be live at the + // report's error node. + if (N->getState()->getSVal(R) != V) + return NULL; - for ( ; Node ; Node = Node->getFirstPred()) { + const ExplodedNode *Node = N, *Last = N; + // Now look for the store of V. + for ( ; Node ; Node = Node->getFirstPred()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (const PostStmt *P = Node->getLocationAs<PostStmt>()) if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) @@ -137,9 +143,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, // looking for store sites. if (Node->getState()->getSVal(R) != V) break; + + Last = Node; } - if (!Node || !Last) { + if (!Node) { satisfied = true; return NULL; } @@ -189,6 +197,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, os << "declared without an initial value"; } } + else { + os << "initialized here"; + } } } @@ -215,7 +226,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, << " is assigned to "; } else - return NULL; + os << "Value assigned to "; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << '\'' << *VR->getDecl() << '\''; @@ -285,12 +296,11 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return NULL; } -BugReporterVisitor * -bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, - const Stmt *S, - BugReport *report) { +void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N, + const Stmt *S, + BugReport *report) { if (!S || !N) - return 0; + return; ProgramStateManager &StateMgr = N->getState()->getStateManager(); @@ -306,7 +316,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, } if (!N) - return 0; + return; ProgramStateRef state = N->getState(); @@ -320,10 +330,18 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? - SVal V = state->getSVal(loc::MemRegionVal(R)); + SVal V = state->getRawSVal(loc::MemRegionVal(R)); report->markInteresting(R); report->markInteresting(V); - return new FindLastStoreBRVisitor(V, R); + + if (V.getAsLocSymbol()) { + BugReporterVisitor *ConstraintTracker + = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false); + report->addVisitor(ConstraintTracker); + } + + report->addVisitor(new FindLastStoreBRVisitor(V, R)); + return; } } } @@ -343,11 +361,10 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, if (R) { report->markInteresting(R); - return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); + report->addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(R), + false)); } } - - return 0; } BugReporterVisitor * @@ -389,7 +406,7 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Receiver, &BR); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -699,6 +716,9 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { + // FIXME: If there's already a constraint tracker for this variable, + // we shouldn't emit anything here (c.f. the double note in + // test/Analysis/inlining/path-notes.c) SmallString<256> buf; llvm::raw_svector_ostream Out(buf); Out << "Assuming " << LhsString << " is "; diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 1ea25bd01bab..b16b233d6346 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -1,14 +1,14 @@ set(LLVM_LINK_COMPONENTS support) -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) - add_clang_library(clangStaticAnalyzerCore AnalysisManager.cpp + APSIntType.cpp BasicConstraintManager.cpp BasicValueFactory.cpp BlockCounter.cpp BugReporter.cpp BugReporterVisitors.cpp + CallEvent.cpp Checker.cpp CheckerContext.cpp CheckerHelpers.cpp @@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore FunctionSummary.cpp HTMLDiagnostics.cpp MemRegion.cpp - ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp ProgramState.cpp @@ -41,5 +40,19 @@ add_clang_library(clangStaticAnalyzerCore TextPathDiagnostics.cpp ) -add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) +add_dependencies(clangStaticAnalyzerCore + ClangAttrClasses + ClangAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangStmtNodes + ) + +target_link_libraries(clangStaticAnalyzerCore + clangBasic + clangLex + clangAST + clangFrontend + clangRewrite + ) diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp new file mode 100644 index 000000000000..e3f4c61e905e --- /dev/null +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -0,0 +1,856 @@ +//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file This file defines CallEvent and its subclasses, which represent path- +/// sensitive instances of different kinds of function and method calls +/// (C, C++, and Objective-C). +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/AST/ParentMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringExtras.h" + +using namespace clang; +using namespace ento; + +QualType CallEvent::getResultType() const { + QualType ResultTy = getDeclaredResultType(); + + if (ResultTy.isNull()) + ResultTy = getOriginExpr()->getType(); + + return ResultTy; +} + +static bool isCallbackArg(SVal V, QualType T) { + // If the parameter is 0, it's harmless. + if (V.isZeroConstant()) + return false; + + // If a parameter is a block or a callback, assume it can modify pointer. + if (T->isBlockPointerType() || + T->isFunctionPointerType() || + T->isObjCSelType()) + return true; + + // Check if a callback is passed inside a struct (for both, struct passed by + // reference and by value). Dig just one level into the struct for now. + + if (isa<PointerType>(T) || isa<ReferenceType>(T)) + T = T->getPointeeType(); + + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + QualType FieldT = I->getType(); + if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType()) + return true; + } + } + + return false; +} + +bool CallEvent::hasNonZeroCallbackArg() const { + unsigned NumOfArgs = getNumArgs(); + + // If calling using a function pointer, assume the function does not + // have a callback. TODO: We could check the types of the arguments here. + if (!getDecl()) + return false; + + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = param_type_begin(), + E = param_type_end(); + I != E && Idx < NumOfArgs; ++I, ++Idx) { + if (NumOfArgs <= Idx) + break; + + if (isCallbackArg(getArgSVal(Idx), *I)) + return true; + } + + return false; +} + +/// \brief Returns true if a type is a pointer-to-const or reference-to-const +/// with no further indirection. +static bool isPointerToConst(QualType Ty) { + QualType PointeeTy = Ty->getPointeeType(); + if (PointeeTy == QualType()) + return false; + if (!PointeeTy.isConstQualified()) + return false; + if (PointeeTy->isAnyPointerType()) + return false; + return true; +} + +// Try to retrieve the function declaration and find the function parameter +// types which are pointers/references to a non-pointer const. +// We will not invalidate the corresponding argument regions. +static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, + const CallEvent &Call) { + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = Call.param_type_begin(), + E = Call.param_type_end(); + I != E; ++I, ++Idx) { + if (isPointerToConst(*I)) + PreserveArgs.insert(Idx); + } +} + +ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, + ProgramStateRef Orig) const { + ProgramStateRef Result = (Orig ? Orig : getState()); + + SmallVector<const MemRegion *, 8> RegionsToInvalidate; + getExtraInvalidatedRegions(RegionsToInvalidate); + + // Indexes of arguments whose values will be preserved by the call. + llvm::SmallSet<unsigned, 1> PreserveArgs; + if (!argumentsMayEscape()) + findPtrToConstParams(PreserveArgs, *this); + + for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { + if (PreserveArgs.count(Idx)) + continue; + + SVal V = getArgSVal(Idx); + + // If we are passing a location wrapped as an integer, unwrap it and + // invalidate the values referred by the location. + if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) + V = Wrapped->getLoc(); + else if (!isa<Loc>(V)) + continue; + + if (const MemRegion *R = V.getAsRegion()) { + // Invalidate the value of the variable passed by reference. + + // Are we dealing with an ElementRegion? If the element type is + // a basic integer type (e.g., char, int) and the underlying region + // is a variable region then strip off the ElementRegion. + // FIXME: We really need to think about this for the general case + // as sometimes we are reasoning about arrays and other times + // about (char*), etc., is just a form of passing raw bytes. + // e.g., void *p = alloca(); foo((char*)p); + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + // Checking for 'integral type' is probably too promiscuous, but + // we'll leave it in for now until we have a systematic way of + // handling all of these cases. Eventually we need to come up + // with an interface to StoreManager so that this logic can be + // appropriately delegated to the respective StoreManagers while + // still allowing us to do checker-specific logic (e.g., + // invalidating reference counts), probably via callbacks. + if (ER->getElementType()->isIntegralOrEnumerationType()) { + const MemRegion *superReg = ER->getSuperRegion(); + if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || + isa<ObjCIvarRegion>(superReg)) + R = cast<TypedRegion>(superReg); + } + // FIXME: What about layers of ElementRegions? + } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + } + } + + // Invalidate designated regions using the batch invalidation API. + // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate + // global variables. + return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(), + BlockCount, getLocationContext(), + /*Symbols=*/0, this); +} + +ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, + const ProgramPointTag *Tag) const { + if (const Expr *E = getOriginExpr()) { + if (IsPreVisit) + return PreStmt(E, getLocationContext(), Tag); + return PostStmt(E, getLocationContext(), Tag); + } + + const Decl *D = getDecl(); + assert(D && "Cannot get a program point without a statement or decl"); + + SourceLocation Loc = getSourceRange().getBegin(); + if (IsPreVisit) + return PreImplicitCall(D, Loc, getLocationContext(), Tag); + return PostImplicitCall(D, Loc, getLocationContext(), Tag); +} + +SVal CallEvent::getArgSVal(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return UnknownVal(); + return getSVal(ArgE); +} + +SourceRange CallEvent::getArgSourceRange(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return SourceRange(); + return ArgE->getSourceRange(); +} + +void CallEvent::dump(raw_ostream &Out) const { + ASTContext &Ctx = getState()->getStateManager().getContext(); + if (const Expr *E = getOriginExpr()) { + E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy()); + Out << "\n"; + return; + } + + if (const Decl *D = getDecl()) { + Out << "Call to "; + D->print(Out, Ctx.getPrintingPolicy()); + return; + } + + // FIXME: a string representation of the kind would be nice. + Out << "Unknown call (type " << getKind() << ")"; +} + + +bool CallEvent::mayBeInlined(const Stmt *S) { + // FIXME: Kill this. + return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) + || isa<CXXConstructExpr>(S); +} + +static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, + CallEvent::BindingsTy &Bindings, + SValBuilder &SVB, + const CallEvent &Call, + CallEvent::param_iterator I, + CallEvent::param_iterator E) { + MemRegionManager &MRMgr = SVB.getRegionManager(); + + unsigned Idx = 0; + for (; I != E; ++I, ++Idx) { + const ParmVarDecl *ParamDecl = *I; + assert(ParamDecl && "Formal parameter has no decl?"); + + SVal ArgVal = Call.getArgSVal(Idx); + if (!ArgVal.isUnknown()) { + Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); + Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + } + } + + // FIXME: Variadic arguments are not handled at all right now. +} + + +CallEvent::param_iterator AnyFunctionCall::param_begin() const { + const FunctionDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_begin(); +} + +CallEvent::param_iterator AnyFunctionCall::param_end() const { + const FunctionDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_end(); +} + +void AnyFunctionCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); +} + +QualType AnyFunctionCall::getDeclaredResultType() const { + const FunctionDecl *D = getDecl(); + if (!D) + return QualType(); + + return D->getResultType(); +} + +bool AnyFunctionCall::argumentsMayEscape() const { + if (hasNonZeroCallbackArg()) + return true; + + const FunctionDecl *D = getDecl(); + if (!D) + return true; + + const IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + + // This set of "escaping" APIs is + + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + if (II->isStr("pthread_setspecific")) + return true; + + // - xpc_connection_set_context stores a value which can be retrieved later + // with xpc_connection_get_context. + if (II->isStr("xpc_connection_set_context")) + return true; + + // - funopen - sets a buffer for future IO calls. + if (II->isStr("funopen")) + return true; + + StringRef FName = II->getName(); + + // - CoreFoundation functions that end with "NoCopy" can free a passed-in + // buffer even if it is const. + if (FName.endswith("NoCopy")) + return true; + + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + return true; + + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. (PR12101) + if (FName.startswith("CF") || FName.startswith("CG")) { + return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || + StrInStrNoCase(FName, "AddValue") != StringRef::npos || + StrInStrNoCase(FName, "SetValue") != StringRef::npos || + StrInStrNoCase(FName, "WithData") != StringRef::npos || + StrInStrNoCase(FName, "AppendValue") != StringRef::npos || + StrInStrNoCase(FName, "SetAttribute") != StringRef::npos; + } + + return false; +} + + +const FunctionDecl *SimpleCall::getDecl() const { + const FunctionDecl *D = getOriginExpr()->getDirectCallee(); + if (D) + return D; + + return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl(); +} + + +const FunctionDecl *CXXInstanceCall::getDecl() const { + const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr()); + if (!CE) + return AnyFunctionCall::getDecl(); + + const FunctionDecl *D = CE->getDirectCallee(); + if (D) + return D; + + return getSVal(CE->getCallee()).getAsFunctionDecl(); +} + +void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (const MemRegion *R = getCXXThisVal().getAsRegion()) + Regions.push_back(R); +} + +static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){ + const MemRegion *R = ThisVal.getAsRegion(); + if (!R) + return 0; + + const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts()); + if (!TR) + return 0; + + const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl(); + if (!RD) + return 0; + + const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD); + const FunctionDecl *Definition; + if (!Result->hasBody(Definition)) + return 0; + + return cast<CXXMethodDecl>(Definition); +} + + +RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + const Decl *D = getDecl(); + if (!D) + return RuntimeDefinition(); + + const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); + if (!MD->isVirtual()) + return AnyFunctionCall::getRuntimeDefinition(); + + // If the method is virtual, see if we can find the actual implementation + // based on context-sensitivity. + // FIXME: Virtual method calls behave differently when an object is being + // constructed or destructed. It's not as simple as "no devirtualization" + // because a /partially/ constructed object can be referred to through a + // base pointer. We'll eventually want to use DynamicTypeInfo here. + if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) + return RuntimeDefinition(Devirtualized); + + return RuntimeDefinition(); +} + +void CXXInstanceCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + // Handle the binding of 'this' in the new stack frame. + // We need to make sure we have the proper layering of CXXBaseObjectRegions. + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + ProgramStateManager &StateMgr = getState()->getStateManager(); + SValBuilder &SVB = StateMgr.getSValBuilder(); + + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + + if (const MemRegion *ThisReg = ThisVal.getAsRegion()) { + ASTContext &Ctx = SVB.getContext(); + const CXXRecordDecl *Class = MD->getParent(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); + + // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. + bool Failed; + ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed); + assert(!Failed && "Calling an incorrectly devirtualized method"); + + // If we couldn't build the correct cast, just strip off all casts. + if (ThisVal.isUnknown()) + ThisVal = loc::MemRegionVal(ThisReg->StripCasts()); + } + + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + + + +const Expr *CXXMemberCall::getCXXThisExpr() const { + return getOriginExpr()->getImplicitObjectArgument(); +} + + +const Expr *CXXMemberOperatorCall::getCXXThisExpr() const { + return getOriginExpr()->getArg(0); +} + + +const BlockDataRegion *BlockCall::getBlockRegion() const { + const Expr *Callee = getOriginExpr()->getCallee(); + const MemRegion *DataReg = getSVal(Callee).getAsRegion(); + + return dyn_cast_or_null<BlockDataRegion>(DataReg); +} + +CallEvent::param_iterator BlockCall::param_begin() const { + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_begin(); +} + +CallEvent::param_iterator BlockCall::param_end() const { + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_end(); +} + +void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const { + // FIXME: This also needs to invalidate captured globals. + if (const MemRegion *R = getBlockRegion()) + Regions.push_back(R); +} + +void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); +} + + +QualType BlockCall::getDeclaredResultType() const { + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return QualType(); + QualType BlockTy = BR->getCodeRegion()->getLocationType(); + return cast<FunctionType>(BlockTy->getPointeeType())->getResultType(); +} + + +SVal CXXConstructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); + return UnknownVal(); +} + +void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (Data) + Regions.push_back(static_cast<const MemRegion *>(Data)); +} + +void CXXConstructorCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + + + +SVal CXXDestructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); + return UnknownVal(); +} + + +CallEvent::param_iterator ObjCMethodCall::param_begin() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_begin(); +} + +CallEvent::param_iterator ObjCMethodCall::param_end() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_end(); +} + +void +ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (const MemRegion *R = getReceiverSVal().getAsRegion()) + Regions.push_back(R); +} + +QualType ObjCMethodCall::getDeclaredResultType() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return QualType(); + + return D->getResultType(); +} + +SVal ObjCMethodCall::getReceiverSVal() const { + // FIXME: Is this the best way to handle class receivers? + if (!isInstanceMessage()) + return UnknownVal(); + + if (const Expr *RecE = getOriginExpr()->getInstanceReceiver()) + return getSVal(RecE); + + // An instance message with no expression means we are sending to super. + // In this case the object reference is the same as 'self'. + const LocationContext *LCtx = getLocationContext(); + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + assert(SelfDecl && "No message receiver Expr, but not in an ObjC method"); + return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx)); +} + +SourceRange ObjCMethodCall::getSourceRange() const { + switch (getMessageKind()) { + case OCM_Message: + return getOriginExpr()->getSourceRange(); + case OCM_PropertyAccess: + case OCM_Subscript: + return getContainingPseudoObjectExpr()->getSourceRange(); + } + llvm_unreachable("unknown message kind"); +} + +typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy; + +const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const { + assert(Data != 0 && "Lazy lookup not yet performed."); + assert(getMessageKind() != OCM_Message && "Explicit message send."); + return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer(); +} + +ObjCMessageKind ObjCMethodCall::getMessageKind() const { + if (Data == 0) { + ParentMap &PM = getLocationContext()->getParentMap(); + const Stmt *S = PM.getParent(getOriginExpr()); + if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) { + const Expr *Syntactic = POE->getSyntacticForm(); + + // This handles the funny case of assigning to the result of a getter. + // This can happen if the getter returns a non-const reference. + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic)) + Syntactic = BO->getLHS(); + + ObjCMessageKind K; + switch (Syntactic->getStmtClass()) { + case Stmt::ObjCPropertyRefExprClass: + K = OCM_PropertyAccess; + break; + case Stmt::ObjCSubscriptRefExprClass: + K = OCM_Subscript; + break; + default: + // FIXME: Can this ever happen? + K = OCM_Message; + break; + } + + if (K != OCM_Message) { + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(POE, K).getOpaqueValue(); + assert(getMessageKind() == K); + return K; + } + } + + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(0, 1).getOpaqueValue(); + assert(getMessageKind() == OCM_Message); + return OCM_Message; + } + + ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data); + if (!Info.getPointer()) + return OCM_Message; + return static_cast<ObjCMessageKind>(Info.getInt()); +} + + +bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, + Selector Sel) const { + assert(IDecl); + const SourceManager &SM = + getState()->getStateManager().getContext().getSourceManager(); + + // If the class interface is declared inside the main file, assume it is not + // subcassed. + // TODO: It could actually be subclassed if the subclass is private as well. + // This is probably very rare. + SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); + if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc)) + return false; + + + // We assume that if the method is public (declared outside of main file) or + // has a parent which publicly declares the method, the method could be + // overridden in a subclass. + + // Find the first declaration in the class hierarchy that declares + // the selector. + ObjCMethodDecl *D = 0; + while (true) { + D = IDecl->lookupMethod(Sel, true); + + // Cannot find a public definition. + if (!D) + return false; + + // If outside the main file, + if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation())) + return true; + + if (D->isOverriding()) { + // Search in the superclass on the next iteration. + IDecl = D->getClassInterface(); + if (!IDecl) + return false; + + IDecl = IDecl->getSuperClass(); + if (!IDecl) + return false; + + continue; + } + + return false; + }; + + llvm_unreachable("The while loop should always terminate."); +} + +RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { + const ObjCMessageExpr *E = getOriginExpr(); + assert(E); + Selector Sel = E->getSelector(); + + if (E->isInstanceMessage()) { + + // Find the the receiver type. + const ObjCObjectPointerType *ReceiverT = 0; + bool CanBeSubClassed = false; + QualType SupersType = E->getSuperType(); + const MemRegion *Receiver = 0; + + if (!SupersType.isNull()) { + // Super always means the type of immediate predecessor to the method + // where the call occurs. + ReceiverT = cast<ObjCObjectPointerType>(SupersType); + } else { + Receiver = getReceiverSVal().getAsRegion(); + if (!Receiver) + return RuntimeDefinition(); + + DynamicTypeInfo DTI = getState()->getDynamicTypeInfo(Receiver); + QualType DynType = DTI.getType(); + CanBeSubClassed = DTI.canBeASubClass(); + ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); + + if (ReceiverT && CanBeSubClassed) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) + if (!canBeOverridenInSubclass(IDecl, Sel)) + CanBeSubClassed = false; + } + + // Lookup the method implementation. + if (ReceiverT) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { + const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel); + if (CanBeSubClassed) + return RuntimeDefinition(MD, Receiver); + else + return RuntimeDefinition(MD, 0); + } + + } else { + // This is a class method. + // If we have type info for the receiver class, we are calling via + // class name. + if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) { + // Find/Return the method implementation. + return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel)); + } + } + + return RuntimeDefinition(); +} + +void ObjCMethodCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); + + SVal SelfVal = getReceiverSVal(); + if (!SelfVal.isUnknown()) { + const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl(); + MemRegionManager &MRMgr = SVB.getRegionManager(); + Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx)); + Bindings.push_back(std::make_pair(SelfLoc, SelfVal)); + } +} + +CallEventRef<> +CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) { + if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) + return create<CXXMemberCall>(MCE, State, LCtx); + + if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) { + const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) + if (MD->isInstance()) + return create<CXXMemberOperatorCall>(OpCE, State, LCtx); + + } else if (CE->getCallee()->getType()->isBlockPointerType()) { + return create<BlockCall>(CE, State, LCtx); + } + + // Otherwise, it's a normal function call, static member function call, or + // something we can't reason about. + return create<FunctionCall>(CE, State, LCtx); +} + + +CallEventRef<> +CallEventManager::getCaller(const StackFrameContext *CalleeCtx, + ProgramStateRef State) { + const LocationContext *ParentCtx = CalleeCtx->getParent(); + const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame(); + assert(CallerCtx && "This should not be used for top-level stack frames"); + + const Stmt *CallSite = CalleeCtx->getCallSite(); + + if (CallSite) { + if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite)) + return getSimpleCall(CE, State, CallerCtx); + + switch (CallSite->getStmtClass()) { + case Stmt::CXXConstructExprClass: { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite), + ThisVal.getAsRegion(), State, CallerCtx); + } + case Stmt::CXXNewExprClass: + return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx); + case Stmt::ObjCMessageExprClass: + return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite), + State, CallerCtx); + default: + llvm_unreachable("This is not an inlineable statement."); + } + } + + // Fall back to the CFG. The only thing we haven't handled yet is + // destructors, though this could change in the future. + const CFGBlock *B = CalleeCtx->getCallSiteBlock(); + CFGElement E = (*B)[CalleeCtx->getIndex()]; + assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs"); + assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet"); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + const Stmt *Trigger; + if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E)) + Trigger = AutoDtor->getTriggerStmt(); + else + Trigger = Dtor->getBody(); + + return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), + State, CallerCtx); +} + diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 0bcc343fba85..c7866559c6d7 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -14,7 +14,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" @@ -25,6 +25,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const { return !StmtCheckers.empty() || !PreObjCMessageCheckers.empty() || !PostObjCMessageCheckers.empty() || + !PreCallCheckers.empty() || + !PostCallCheckers.empty() || !LocationCheckers.empty() || !BindCheckers.empty() || !EndAnalysisCheckers.empty() || @@ -138,7 +140,7 @@ namespace { const CheckersTy &Checkers; const Stmt *S; ExprEngine &Eng; - bool wasInlined; + bool WasInlined; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } @@ -146,7 +148,7 @@ namespace { CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, const Stmt *s, ExprEngine &eng, bool wasInlined = false) : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), - wasInlined(wasInlined) {} + WasInlined(wasInlined) {} void runChecker(CheckerManager::CheckStmtFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -155,7 +157,7 @@ namespace { ProgramPoint::PostStmtKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Bldr, Eng, Pred, L, wasInlined); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); checkFn(S, C); } }; @@ -167,38 +169,35 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng, - bool wasInlined) { + bool WasInlined) { CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), - S, Eng, wasInlined); + S, Eng, WasInlined); expandGraphWithCheckers(C, Dst, Src); } namespace { struct CheckObjCMessageContext { typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy; - bool IsPreVisit; + bool IsPreVisit, WasInlined; const CheckersTy &Checkers; - const ObjCMessage &Msg; + const ObjCMethodCall &Msg; ExprEngine &Eng; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers, - const ObjCMessage &msg, ExprEngine &eng) - : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { } + const ObjCMethodCall &msg, ExprEngine &eng, + bool wasInlined) + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Msg(msg), Eng(eng) { } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : - ProgramPoint::PostStmtKind; - const ProgramPoint &L = - ProgramPoint::getProgramPoint(Msg.getMessageExpr(), - K, Pred->getLocationContext(), - checkFn.Checker); - CheckerContext C(Bldr, Eng, Pred, L); + const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); - checkFn(Msg, C); + checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C); } }; } @@ -207,12 +206,56 @@ namespace { void CheckerManager::runCheckersForObjCMessage(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const ObjCMessage &msg, - ExprEngine &Eng) { + const ObjCMethodCall &msg, + ExprEngine &Eng, + bool WasInlined) { CheckObjCMessageContext C(isPreVisit, isPreVisit ? PreObjCMessageCheckers : PostObjCMessageCheckers, - msg, Eng); + msg, Eng, WasInlined); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + // FIXME: This has all the same signatures as CheckObjCMessageContext. + // Is there a way we can merge the two? + struct CheckCallContext { + typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy; + bool IsPreVisit, WasInlined; + const CheckersTy &Checkers; + const CallEvent &Call; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckCallContext(bool isPreVisit, const CheckersTy &checkers, + const CallEvent &call, ExprEngine &eng, + bool wasInlined) + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Call(call), Eng(eng) { } + + void runChecker(CheckerManager::CheckCallFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + + checkFn(*Call.cloneWithState(Pred->getState()), C); + } + }; +} + +/// \brief Run checkers for visiting an abstract call event. +void CheckerManager::runCheckersForCallEvent(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const CallEvent &Call, + ExprEngine &Eng, + bool WasInlined) { + CheckCallContext C(isPreVisit, + isPreVisit ? PreCallCheckers + : PostCallCheckers, + Call, Eng, WasInlined); expandGraphWithCheckers(C, Dst, Src); } @@ -381,21 +424,25 @@ namespace { SymbolReaper &SR; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind ProgarmPointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, - const Stmt *s, ExprEngine &eng) - : Checkers(checkers), SR(sr), S(s), Eng(eng) { } + const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind K) + : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind, Pred->getLocationContext(), checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); + // Note, do not pass the statement to the checkers without letting them + // differentiate if we ran remove dead bindings before or after the + // statement. checkFn(SR, C); } }; @@ -406,8 +453,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, const Stmt *S, - ExprEngine &Eng) { - CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng); + ExprEngine &Eng, + ProgramPoint::Kind K) { + CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); expandGraphWithCheckers(C, Dst, Src); } @@ -426,7 +474,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) { + const CallEvent *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. @@ -456,16 +504,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const CallExpr *CE, - ExprEngine &Eng, - GraphExpander *defaultEval) { - if (EvalCallCheckers.empty() && - InlineCallCheckers.empty() && - defaultEval == 0) { - Dst.insert(Src); - return; - } - + const CallEvent &Call, + ExprEngine &Eng) { + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); for (ExplodedNodeSet::iterator NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) { @@ -529,10 +570,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { - if (defaultEval) - defaultEval->expandGraph(Dst, Pred); - else - Dst.insert(Pred); + NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); + Eng.defaultEvalCall(B, Pred, Call); } } } @@ -590,6 +629,13 @@ void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { PostObjCMessageCheckers.push_back(checkfn); } +void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) { + PreCallCheckers.push_back(checkfn); +} +void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) { + PostCallCheckers.push_back(checkfn); +} + void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { LocationCheckers.push_back(checkfn); } @@ -673,6 +719,3 @@ CheckerManager::~CheckerManager() { for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) CheckerDtors[i](); } - -// Anchor for the vtable. -GraphExpander::~GraphExpander() { } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index ca662c799211..1f137424d450 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -26,6 +26,8 @@ using namespace clang; using namespace ento; +STATISTIC(NumSteps, + "The # of steps executed."); STATISTIC(NumReachedMaxSteps, "The # of times we reached the max number of steps."); STATISTIC(NumPathsExplored, @@ -74,7 +76,7 @@ public: } virtual void enqueue(const WorkListUnit& U) { - Queue.push_front(U); + Queue.push_back(U); } virtual WorkListUnit dequeue() { @@ -207,6 +209,8 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, --Steps; } + NumSteps++; + const WorkListUnit& WU = WList->dequeue(); // Set the current block counter. @@ -248,7 +252,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; } - case ProgramPoint::CallExitKind: + case ProgramPoint::CallExitBeginKind: SubEng.processCallExit(Pred); break; @@ -261,7 +265,9 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, } default: assert(isa<PostStmt>(Loc) || - isa<PostInitializer>(Loc)); + isa<PostInitializer>(Loc) || + isa<PostImplicitCall>(Loc) || + isa<CallExitEnd>(Loc)); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -502,7 +508,8 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, } // Do not create extra nodes. Move to the next CFG element. - if (isa<PostInitializer>(N->getLocation())) { + if (isa<PostInitializer>(N->getLocation()) || + isa<PostImplicitCall>(N->getLocation())) { WList->enqueue(N, Block, Idx+1); return; } @@ -531,14 +538,13 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) { - // Create a CallExit node and enqueue it. +ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { + // Create a CallExitBegin node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(N->getLocationContext()); - const Stmt *CE = LocCtx->getCallSite(); - // Use the the callee location context. - CallExit Loc(CE, LocCtx); + // Use the callee location context. + CallExitBegin Loc(LocCtx); bool isNew; ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); @@ -565,12 +571,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set, void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { ExplodedNode *N = *I; - // If we are in an inlined call, generate CallExit node. + // If we are in an inlined call, generate CallExitBegin node. if (N->getLocationContext()->getParent()) { - N = generateCallExitNode(N); + N = generateCallExitBeginNode(N); if (N) WList->enqueue(N); } else { + // TODO: We should run remove dead bindings here. G->addEndOfPath(N); NumPathsExplored++; } diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index b5ea3db7f31c..52644f7702fc 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -71,6 +71,11 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, else return svalBuilder.makeBoolVal(cast<CXXBoolLiteralExpr>(E)); } + case Stmt::CXXScalarValueInitExprClass: + case Stmt::ImplicitValueInitExprClass: { + QualType Ty = cast<Expr>(E)->getType(); + return svalBuilder.makeZeroVal(Ty); + } case Stmt::IntegerLiteralClass: { // In C++, this expression may have been bound to a temporary object. SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); @@ -91,8 +96,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::CXXBindTemporaryExprClass: E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); continue; - case Stmt::ObjCPropertyRefExprClass: - return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E)); + case Stmt::SubstNonTypeTemplateParmExprClass: + E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); + continue; case Stmt::ObjCStringLiteralClass: { MemRegionManager &MRMgr = svalBuilder.getRegionManager(); const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E); @@ -227,13 +233,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, RSScaner.scan(X); continue; } - - // Otherwise the expression is dead with a couple exceptions. - // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the - // beginning of itself, but we need its UndefinedVal to determine its - // SVal. - if (X.isUndef() && cast<UndefinedVal>(X).getData()) - EBMapRef = EBMapRef.add(BlkExpr, X); } // Go through he deferred locations and add them to the new environment if diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 0dcbe1ff4ab7..b79f3f5d1eaa 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,12 +13,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" #include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include <vector> using namespace clang; @@ -57,7 +59,7 @@ ExplodedGraph::~ExplodedGraph() {} //===----------------------------------------------------------------------===// bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { - // Reclaimn all nodes that match *all* the following criteria: + // Reclaim all nodes that match *all* the following criteria: // // (1) 1 predecessor (that has one successor) // (2) 1 successor (that has one predecessor) @@ -67,6 +69,9 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. // (8) The PostStmt is for a non-consumed Stmt or Expr. + // (9) The successor is not a CallExpr StmtPoint (so that we would be able to + // find it when retrying a call with no inlining). + // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. // Conditions 1 and 2. if (node->pred_size() != 1 || node->succ_size() != 1) @@ -82,8 +87,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 3. ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint) || - (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint))) + if (!isa<PostStmt>(progPoint)) return false; // Condition 4. @@ -108,7 +112,13 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { return false; } - return true; + // Condition 9. + const ProgramPoint SuccLoc = succ->getLocation(); + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc)) + if (CallEvent::mayBeInlined(SP->getStmt())) + return false; + + return true; } void ExplodedGraph::collectNode(ExplodedNode *node) { diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fd90685186e..b0435fb562e3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -18,13 +18,12 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtCXX.h" -#include "clang/AST/DeclCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" @@ -42,8 +41,6 @@ using llvm::APSInt; STATISTIC(NumRemoveDeadBindings, "The # of times RemoveDeadBindings is called"); -STATISTIC(NumRemoveDeadBindingsSkipped, - "The # of times RemoveDeadBindings is skipped"); STATISTIC(NumMaxBlockCountReached, "The # of aborted paths due to reaching the maximum block count in " "a top level function"); @@ -54,15 +51,6 @@ STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); //===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); -} - -//===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -163,7 +151,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { // analyzing an "open" program. const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); if (SFC->getParent() == 0) { - loc::MemRegionVal L(getCXXThisRegion(MD, SFC)); + loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); if (const Loc *LV = dyn_cast<Loc>(&V)) { state = state->assume(*LV, true); @@ -196,7 +184,7 @@ ExprEngine::processRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) { + const CallEvent *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, Explicits, Regions, Call); } @@ -231,6 +219,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } + currentBuilderContext = 0; } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, @@ -251,7 +240,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Run before processing a call. - if (isa<CallExpr>(S.getStmt())) + if (CallEvent::mayBeInlined(S.getStmt())) return true; // Is this an expression that is consumed by another expression? If so, @@ -260,62 +249,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { - // Reclaim any unnecessary nodes in the ExplodedGraph. - G.reclaimRecentlyAllocatedNodes(); - - currentStmt = S.getStmt(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - currentStmt->getLocStart(), - "Error evaluating statement"); - - EntryNode = Pred; - - ProgramStateRef EntryState = EntryNode->getState(); - CleanedState = EntryState; - - // Create the cleaned state. - const LocationContext *LC = EntryNode->getLocationContext(); - SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - - if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) { - NumRemoveDeadBindings++; - getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); - - const StackFrameContext *SFC = LC->getCurrentStackFrame(); - - // Create a state in which dead bindings are removed from the environment - // and the store. TODO: The function should just return new env and store, - // not a new state. - CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); - } else { - NumRemoveDeadBindingsSkipped++; - } +void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, + const LocationContext *LC, + const Stmt *DiagnosticStmt, + ProgramPoint::Kind K) { + assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || + ReferenceStmt == 0) && "PreStmt is not generally supported by " + "the SymbolReaper yet"); + NumRemoveDeadBindings++; + CleanedState = Pred->getState(); + SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); + + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); + + // Create a state in which dead bindings are removed from the environment + // and the store. TODO: The function should just return new env and store, + // not a new state. + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. - ExplodedNodeSet Tmp; // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); - if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. - StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); - Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag); + StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext); + Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K); } else { // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode, - SymReaper, currentStmt, *this); + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. - StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext); + StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { ProgramStateRef CheckerState = (*I)->getState(); @@ -324,10 +298,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, CheckerState = getConstraintManager().removeDeadBindings(CheckerState, SymReaper); - assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) && + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Environment as a part of " "checkDeadSymbols processing."); - assert(StateMgr.haveEqualStores(CheckerState, EntryState) && + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Store as a part of " "checkDeadSymbols processing."); @@ -335,13 +309,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag, - ProgramPoint::PostPurgeDeadSymbolsKind); + Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false, + &cleanupTag, K); } } +} + +void ExprEngine::ProcessStmt(const CFGStmt S, + ExplodedNode *Pred) { + // Reclaim any unnecessary nodes in the ExplodedGraph. + G.reclaimRecentlyAllocatedNodes(); + + currentStmt = S.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currentStmt->getLocStart(), + "Error evaluating statement"); + // Remove dead bindings and symbols. + EntryNode = Pred; + ExplodedNodeSet CleanedStates; + if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){ + removeDead(EntryNode, CleanedStates, currentStmt, + Pred->getLocationContext(), currentStmt); + } else + CleanedStates.Add(EntryNode); + + // Visit the statement. ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + for (ExplodedNodeSet::iterator I = CleanedStates.begin(), + E = CleanedStates.end(); I != E; ++I) { ExplodedNodeSet DstI; // Visit the statement. Visit(currentStmt, *I, DstI); @@ -360,49 +356,49 @@ void ExprEngine::ProcessStmt(const CFGStmt S, void ExprEngine::ProcessInitializer(const CFGInitializer Init, ExplodedNode *Pred) { ExplodedNodeSet Dst; + NodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + + ProgramStateRef State = Pred->getState(); - // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + BMI->getSourceLocation(), + "Error evaluating initializer"); + + // We don't set EntryNode and currentStmt. And we don't clean up state. const StackFrameContext *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); - const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame); - - SVal thisVal = Pred->getState()->getSVal(thisReg); + SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); + // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { - // Evaluate the initializer. - - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - ProgramStateRef state = Pred->getState(); - - const FieldDecl *FD = BMI->getAnyMember(); - - SVal FieldLoc = state->getLValue(FD, thisVal); - SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext()); - state = state->bindLoc(FieldLoc, InitVal); + // Constructors build the object directly in the field, + // but non-objects must be copied in from the initializer. + if (!isa<CXXConstructExpr>(BMI->getInit())) { + SVal FieldLoc; + if (BMI->isIndirectMemberInitializer()) + FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); + else + FieldLoc = State->getLValue(BMI->getMember(), thisVal); - // Use a custom node building process. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - Bldr.generateNode(PP, Pred, state); + SVal InitVal = State->getSVal(BMI->getInit(), stackFrame); + State = State->bindLoc(FieldLoc, InitVal); + } } else { - assert(BMI->isBaseInitializer()); - - // Get the base class declaration. - const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); - - // Create the base object region. - SVal baseVal = - getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); - const MemRegion *baseReg = baseVal.getAsRegion(); - assert(baseReg); - - VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst); + assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); + // We already did all the work when visiting the CXXConstructExpr. } + // Construct a PostInitializer node whether the state changed or not, + // so that the diagnostics don't get confused. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + Bldr.generateNode(PP, State, Pred); + // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } @@ -442,27 +438,51 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, if (const ReferenceType *refType = varType->getAs<ReferenceType>()) varType = refType->getPointeeType(); - const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl(); - assert(recordDecl && "get CXXRecordDecl fail"); - const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); - Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); - VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(), + VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(), Dtor.getTriggerStmt(), Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, - ExplodedNode *Pred, ExplodedNodeSet &Dst) {} + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = Pred->getState()->getSVal(ThisPtr); + + // Create the base object region. + QualType BaseTy = D.getBaseSpecifier()->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + + VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(), + CurDtor->getBody(), Pred, Dst); +} void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, - ExplodedNode *Pred, ExplodedNodeSet &Dst) {} + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const FieldDecl *Member = D.getFieldDecl(); + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, + LCtx->getCurrentStackFrame()); + SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal))); + + VisitCXXDestructor(Member->getType(), + cast<loc::MemRegionVal>(FieldVal).getRegion(), + CurDtor->getBody(), Pred, Dst); +} void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) {} -void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, +void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), @@ -490,7 +510,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: case Stmt::CXXUnresolvedConstructExprClass: - case Stmt::CXXScalarValueInitExprClass: case Stmt::DependentScopeDeclRefExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::BinaryTypeTraitExprClass: @@ -514,7 +533,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. - case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXDefaultArgExprClass: break; @@ -544,6 +562,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); + case Stmt::ObjCSubscriptRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + llvm_unreachable("These are handled by PseudoObjectExpr"); + case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. ProgramStateRef state = Pred->getState(); @@ -559,23 +581,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - // FIXME. - case Stmt::ObjCSubscriptRefExprClass: - break; - - case Stmt::ObjCPropertyRefExprClass: - // Implicitly handled by Environment::getSVal(). - break; - - case Stmt::ImplicitValueInitExprClass: { - ProgramStateRef state = Pred->getState(); - QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); - SVal val = svalBuilder.makeZeroVal(ty); - Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), - val)); - break; - } - case Stmt::ExprWithCleanupsClass: // Handled due to fully linearised CFG. break; @@ -592,7 +597,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: - case Expr::ObjCNumericLiteralClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -613,6 +617,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AddrLabelExprClass: case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXBoolLiteralExprClass: case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: @@ -620,6 +626,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; @@ -630,22 +637,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Expr::ObjCArrayLiteralClass: - case Expr::ObjCDictionaryLiteralClass: { + case Expr::ObjCDictionaryLiteralClass: + // FIXME: explicitly model with a region and the actual contents + // of the container. For now, conjure a symbol. + case Expr::ObjCBoxedExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); - // FIXME: explicitly model with a region and the actual contents - // of the container. For now, conjure a symbol. ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext); + const Expr *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); it != et; ++it) { ExplodedNode *N = *it; - const Expr *Ex = cast<Expr>(S); - QualType resultType = Ex->getType(); const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, @@ -671,6 +680,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::MSAsmStmtClass: + Bldr.takeNodes(Pred); + VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + case Stmt::BlockExprClass: Bldr.takeNodes(Pred); VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); @@ -727,12 +742,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXConstructExprClass: { - const CXXConstructExpr *C = cast<CXXConstructExpr>(S); - // For block-level CXXConstructExpr, we don't have a destination region. - // Let VisitCXXConstructExpr() create one. + case Stmt::CXXConstructExprClass: { Bldr.takeNodes(Pred); - VisitCXXConstructExpr(C, 0, Pred, Dst); + VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; } @@ -868,33 +880,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - case Stmt::ObjCMessageExprClass: { + case Stmt::ObjCMessageExprClass: Bldr.takeNodes(Pred); - // Is this a property access? - const ParentMap &PM = Pred->getLocationContext()->getParentMap(); - const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S); - bool evaluated = false; - - if (const PseudoObjectExpr *PO = - dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) { - const Expr *syntactic = PO->getSyntacticForm(); - if (const ObjCPropertyRefExpr *PR = - dyn_cast<ObjCPropertyRefExpr>(syntactic)) { - bool isSetter = ME->getNumArgs() > 0; - VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst); - evaluated = true; - } - else if (isa<BinaryOperator>(syntactic)) { - VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst); - } - } - - if (!evaluated) - VisitObjCMessage(ME, Pred, Dst); - + VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is not complete. We basically treat @throw as @@ -982,6 +972,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = 0; + const Stmt *CE = CalleeSF->getCallSite(); // Find the first node before we started processing the call expression. while (N) { @@ -993,11 +984,15 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started - // processing the CallExpr. - if (isa<PostPurgeDeadSymbols>(L)) + // processing the call. + if (L.isPurgeKind()) + continue; + if (isa<PreImplicitCall>(&L)) + continue; + if (isa<CallEnter>(&L)) continue; if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) - if (SP->getStmt() == CalleeSF->getCallSite()) + if (SP->getStmt() == CE) continue; break; } @@ -1008,7 +1003,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // TODO: Clean up the unneeded nodes. // Build an Epsilon node from which we will restart the analyzes. - const Stmt *CE = CalleeSF->getCallSite(); + // Note that CE is permitted to be NULL! ProgramPoint NewNodeLoc = EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); // Add the special flag to GDM to signal retrying with no inlining. @@ -1073,63 +1068,6 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // Branch processing. //===----------------------------------------------------------------------===// -ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state, - const Stmt *Terminator, - const LocationContext *LCtx, - bool branchTaken) { - - switch (Terminator->getStmtClass()) { - default: - return state; - - case Stmt::BinaryOperatorClass: { // '&&' and '||' - - const BinaryOperator* B = cast<BinaryOperator>(Terminator); - BinaryOperator::Opcode Op = B->getOpcode(); - - assert (Op == BO_LAnd || Op == BO_LOr); - - // For &&, if we take the true branch, then the value of the whole - // expression is that of the RHS expression. - // - // For ||, if we take the false branch, then the value of the whole - // expression is that of the RHS expression. - - const Expr *Ex = (Op == BO_LAnd && branchTaken) || - (Op == BO_LOr && !branchTaken) - ? B->getRHS() : B->getLHS(); - - return state->BindExpr(B, LCtx, UndefinedVal(Ex)); - } - - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: { // ?: - const AbstractConditionalOperator* C - = cast<AbstractConditionalOperator>(Terminator); - - // For ?, if branchTaken == true then the value is either the LHS or - // the condition itself. (GNU extension). - - const Expr *Ex; - - if (branchTaken) - Ex = C->getTrueExpr(); - else - Ex = C->getFalseExpr(); - - return state->BindExpr(C, LCtx, UndefinedVal(Ex)); - } - - case Stmt::ChooseExprClass: { // ?: - - const ChooseExpr *C = cast<ChooseExpr>(Terminator); - - const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, LCtx, UndefinedVal(Ex)); - } - } -} - /// RecoverCastedSymbol - A helper function for ProcessBranch that is used /// to try to recover some path-sensitivity for casts of symbolic /// integers that promote their values (which are currently not tracked well). @@ -1172,6 +1110,45 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, return state->getSVal(Ex, LCtx); } +static const Stmt *ResolveCondition(const Stmt *Condition, + const CFGBlock *B) { + if (const Expr *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) + return Condition; + + // For logical operations, we still have the case where some branches + // use the traditional "merge" approach and others sink the branch + // directly into the basic blocks representing the logical operation. + // We need to distinguish between those two cases here. + + // The invariants are still shifting, but it is possible that the + // last element in a CFGBlock is not a CFGStmt. Look for the last + // CFGStmt as the value of the condition. + CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); + for (; I != E; ++I) { + CFGElement Elem = *I; + CFGStmt *CS = dyn_cast<CFGStmt>(&Elem); + if (!CS) + continue; + if (CS->getStmt() != Condition) + break; + return Condition; + } + + assert(I != E); + + while (Condition) { + BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) + return Condition; + Condition = BO->getRHS()->IgnoreParens(); + } + llvm_unreachable("could not resolve condition"); +} + void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, NodeBuilderContext& BldCtx, ExplodedNode *Pred, @@ -1188,6 +1165,12 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; } + + // Resolve the condition in the precense of nested '||' and '&&'. + if (const Expr *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + Condition = ResolveCondition(Condition, BldCtx.getBlock()); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Condition->getLocStart(), "Error evaluating branch"); @@ -1230,14 +1213,10 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, } } - const LocationContext *LCtx = PredI->getLocationContext(); - // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { - builder.generateNode(MarkBranch(PrevState, Term, LCtx, true), - true, PredI); - builder.generateNode(MarkBranch(PrevState, Term, LCtx, false), - false, PredI); + builder.generateNode(PrevState, true, PredI); + builder.generateNode(PrevState, false, PredI); continue; } @@ -1246,8 +1225,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // Process the true branch. if (builder.isFeasible(true)) { if (ProgramStateRef state = PrevState->assume(V, true)) - builder.generateNode(MarkBranch(state, Term, LCtx, true), - true, PredI); + builder.generateNode(state, true, PredI); else builder.markInfeasible(true); } @@ -1255,8 +1233,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // Process the false branch. if (builder.isFeasible(false)) { if (ProgramStateRef state = PrevState->assume(V, false)) - builder.generateNode(MarkBranch(state, Term, LCtx, false), - false, PredI); + builder.generateNode(state, false, PredI); else builder.markInfeasible(false); } @@ -1433,7 +1410,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, const LocationContext *LCtx = Pred->getLocationContext(); if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { - assert(Ex->isLValue()); + assert(Ex->isGLValue()); SVal V = state->getLValue(VD, Pred->getLocationContext()); // For references, the 'lvalue' is the pointer address stored in the @@ -1450,7 +1427,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, return; } if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { - assert(!Ex->isLValue()); + assert(!Ex->isGLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; @@ -1493,7 +1470,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, SVal V = state->getLValue(A->getType(), state->getSVal(Idx, LCtx), state->getSVal(Base, LCtx)); - assert(A->isLValue()); + assert(A->isGLValue()); Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), false, 0, ProgramPoint::PostLValueKind); } @@ -1506,14 +1483,26 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext); ExplodedNodeSet Dst; Decl *member = M->getMemberDecl(); + if (VarDecl *VD = dyn_cast<VarDecl>(member)) { - assert(M->isLValue()); + assert(M->isGLValue()); Bldr.takeNodes(Pred); VisitCommonDeclRefExpr(M, VD, Pred, Dst); Bldr.addNodes(Dst); return; } - + + // Handle C++ method calls. + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(member)) { + Bldr.takeNodes(Pred); + SVal MDVal = svalBuilder.getFunctionPointer(MD); + ProgramStateRef state = + Pred->getState()->BindExpr(M, Pred->getLocationContext(), MDVal); + Bldr.generateNode(M, Pred, state); + return; + } + + FieldDecl *field = dyn_cast<FieldDecl>(member); if (!field) // FIXME: skipping member expressions for non-fields return; @@ -1538,10 +1527,17 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // For all other cases, compute an lvalue. SVal L = state->getLValue(field, baseExprVal); - if (M->isLValue()) + if (M->isGLValue()) { + if (field->getType()->isReferenceType()) { + if (const MemRegion *R = L.getAsRegion()) + L = state->getSVal(R); + else + L = UnknownVal(); + } + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0, ProgramPoint::PostLValueKind); - else { + } else { Bldr.takeNodes(Pred); evalLoad(Dst, M, M, Pred, state, L); Bldr.addNodes(Dst); @@ -1591,9 +1587,9 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, /// evalStore - Handle the semantics of a store via an assignment. /// @param Dst The node set to store generated state nodes -/// @param AssignE The assignment expression if the store happens in an +/// @param AssignE The assignment expression if the store happens in an /// assignment. -/// @param LocatioinE The location expression that is stored to. +/// @param LocationE The location expression that is stored to. /// @param state The current simulation state /// @param location The location to store the value /// @param Val The value to be stored @@ -1606,10 +1602,6 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, // ProgramPoint if it is non-NULL, and LocationE otherwise. const Expr *StoreE = AssignE ? AssignE : LocationE; - if (isa<loc::ObjCPropRef>(location)) { - assert(false); - } - // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false); @@ -1634,7 +1626,6 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, QualType LoadTy) { assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); - assert(!isa<loc::ObjCPropRef>(location)); // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the @@ -1814,6 +1805,12 @@ void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, Bldr.generateNode(A, Pred, state); } +void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + Bldr.generateNode(A, Pred, Pred->getState()); +} + //===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// @@ -1852,6 +1849,16 @@ struct DOTGraphTraits<ExplodedNode*> : return ""; } + static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) { + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getExpansionLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) + << "\\l"; + } + } + static std::string getNodeLabel(const ExplodedNode *N, void*){ std::string sbuf; @@ -1861,10 +1868,17 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: + case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + if (const NamedDecl *ND = + dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { + Out << " ("; + ND->printName(Out); + Out << ")"; + } break; + } case ProgramPoint::BlockExitKind: assert (false); @@ -1874,30 +1888,54 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallEnter"; break; - case ProgramPoint::CallExitKind: - Out << "CallExit"; + case ProgramPoint::CallExitBeginKind: + Out << "CallExitBegin"; + break; + + case ProgramPoint::CallExitEndKind: + Out << "CallExitEnd"; + break; + + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + Out << "PostStmtPurgeDeadSymbols"; + break; + + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + Out << "PreStmtPurgeDeadSymbols"; break; case ProgramPoint::EpsilonKind: Out << "Epsilon Point"; break; + case ProgramPoint::PreImplicitCallKind: { + ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + Out << "PreCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + + case ProgramPoint::PostImplicitCallKind: { + ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + Out << "PostCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { const Stmt *S = L->getStmt(); - SourceLocation SLoc = S->getLocStart(); Out << S->getStmtClassName() << ' ' << (void*) S << ' '; LangOptions LO; // FIXME. S->printPretty(Out, 0, PrintingPolicy(LO)); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getExpansionLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) - << "\\l"; - } + printLocation(Out, S->getLocStart()); if (isa<PreStmt>(Loc)) Out << "\\lPreStmt\\l;"; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 93e598a27375..46cba81b14f7 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -50,7 +50,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, } // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. - SVal ExprVal = B->isLValue() ? LeftV : RightV; + SVal ExprVal = B->isGLValue() ? LeftV : RightV; evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal), LeftV, RightV); continue; @@ -58,6 +58,26 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, if (!B->isAssignmentOp()) { StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext); + + if (B->isAdditiveOp()) { + // If one of the operands is a location, conjure a symbol for the other + // one (offset) if it's unknown so that memory arithmetic always + // results in an ElementRegion. + // TODO: This can be removed after we enable history tracking with + // SymSymExpr. + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + if (isa<Loc>(LeftV) && + RHS->getType()->isIntegerType() && RightV.isUnknown()) { + RightV = svalBuilder.getConjuredSymbolVal(RHS, LCtx, + RHS->getType(), Count); + } + if (isa<Loc>(RightV) && + LHS->getType()->isIntegerType() && LeftV.isUnknown()) { + LeftV = svalBuilder.getConjuredSymbolVal(LHS, LCtx, + LHS->getType(), Count); + } + } + // Process non-assignments except commas or short-circuited // logical expressions (LAnd and LOr). SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); @@ -145,7 +165,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // In C++, assignment and compound assignment operators return an // lvalue. - if (B->isLValue()) + if (B->isGLValue()) state = state->BindExpr(B, LCtx, location); else state = state->BindExpr(B, LCtx, Result); @@ -162,14 +182,35 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { CanQualType T = getContext().getCanonicalType(BE->getType()); + + // Get the value of the block itself. SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, Pred->getLocationContext()); + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the block, we should explicitly bind + // the captured variables. + if (const BlockDataRegion *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + + for (; I != E; ++I) { + const MemRegion *capturedR = I.getCapturedRegion(); + const MemRegion *originalR = I.getOriginalRegion(); + if (capturedR != originalR) { + SVal originalV = State->getSVal(loc::MemRegionVal(originalR)); + State = State->bindLoc(loc::MemRegionVal(capturedR), originalV); + } + } + } + ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); Bldr.generateNode(BE, Pred, - Pred->getState()->BindExpr(BE, Pred->getLocationContext(), - V), + State->BindExpr(BE, Pred->getLocationContext(), V), false, 0, ProgramPoint::PostLValueKind); @@ -238,7 +279,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: - case CK_LValueBitCast: case CK_IntegralCast: case CK_NullToPointer: case CK_IntegralToPointer: @@ -278,7 +318,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); SVal val = state->getSVal(Ex, LCtx); - val = getStoreManager().evalDerivedToBase(val, T); + val = getStoreManager().evalDerivedToBase(val, CastE); state = state->BindExpr(CastE, LCtx, val); Bldr.generateNode(CastE, Pred, state); continue; @@ -291,7 +331,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, // Compute the type of the result. QualType resultType = CastE->getType(); - if (CastE->isLValue()) + if (CastE->isGLValue()) resultType = getContext().getPointerType(resultType); bool Failed = false; @@ -337,10 +377,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_VectorSplat: - case CK_MemberPointerToBoolean: { + case CK_MemberPointerToBoolean: + case CK_LValueBitCast: { // Recover some path-sensitivty by conjuring a new value. QualType resultType = CastE->getType(); - if (CastE->isLValue()) + if (CastE->isGLValue()) resultType = getContext().getPointerType(resultType); const LocationContext *LCtx = Pred->getLocationContext(); SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx, @@ -366,8 +407,16 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, SVal ILV = state->getSVal(ILE, Pred->getLocationContext()); const LocationContext *LC = Pred->getLocationContext(); state = state->bindCompoundLiteral(CL, LC, ILV); - - if (CL->isLValue()) + + // Compound literal expressions are a GNU extension in C++. + // Unlike in C, where CLs are lvalues, in C++ CLs are prvalues, + // and like temporary objects created by the functional notation T() + // CLs are destroyed at the end of the containing full-expression. + // HOWEVER, an rvalue of array type is not something the analyzer can + // reason about, since we expect all regions to be wrapped in Locs. + // So we treat array CLs as lvalues as well, knowing that they will decay + // to pointers as soon as they are used. + if (CL->isGLValue() || CL->getType()->isArrayType()) B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC))); else B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); @@ -404,31 +453,39 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, const LocationContext *LC = N->getLocationContext(); if (const Expr *InitEx = VD->getInit()) { - SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext()); - - // We bound the temp obj region to the CXXConstructExpr. Now recover - // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && - !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ - InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); - assert(isa<nonloc::LazyCompoundVal>(InitVal)); - } - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown()) { - QualType Ty = InitEx->getType(); - if (InitEx->isLValue()) { - Ty = getContext().getPointerType(Ty); - } - - InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, - currentBuilderContext->getCurrentBlockCount()); + SVal InitVal = state->getSVal(InitEx, LC); + + if (InitVal == state->getLValue(VD, LC) || + (VD->getType()->isArrayType() && + isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) { + // We constructed the object directly in the variable. + // No need to bind anything. + B.generateNode(DS, N, state); + } else { + // We bound the temp obj region to the CXXConstructExpr. Now recover + // the lazy compound value when the variable is not a reference. + if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ + InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); + assert(isa<nonloc::LazyCompoundVal>(InitVal)); + } + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown()) { + QualType Ty = InitEx->getType(); + if (InitEx->isGLValue()) { + Ty = getContext().getPointerType(Ty); + } + + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, + currentBuilderContext->getCurrentBlockCount()); + } + B.takeNodes(N); + ExplodedNodeSet Dst2; + evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + B.addNodes(Dst2); } - B.takeNodes(N); - ExplodedNodeSet Dst2; - evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); - B.addNodes(Dst2); } else { B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC))); @@ -443,48 +500,44 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); ProgramStateRef state = Pred->getState(); - const LocationContext *LCtx = Pred->getLocationContext(); - SVal X = state->getSVal(B, LCtx); - assert(X.isUndef()); - - const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); - assert(Ex); - - if (Ex == B->getRHS()) { - X = state->getSVal(Ex, LCtx); - - // Handle undefined values. - if (X.isUndef()) { - Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); - return; - } - - DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); - - // We took the RHS. Because the value of the '&&' or '||' expression must - // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 - // or 1. Alternatively, we could take a lazy approach, and calculate this - // value later when necessary. We don't have the machinery in place for - // this right now, and since most logical expressions are used for branches, - // the payoff is not likely to be large. Instead, we do eager evaluation. - if (ProgramStateRef newState = state->assume(XD, true)) - Bldr.generateNode(B, Pred, - newState->BindExpr(B, LCtx, - svalBuilder.makeIntVal(1U, B->getType()))); - - if (ProgramStateRef newState = state->assume(XD, false)) - Bldr.generateNode(B, Pred, - newState->BindExpr(B, LCtx, - svalBuilder.makeIntVal(0U, B->getType()))); + + ExplodedNode *N = Pred; + while (!isa<BlockEntrance>(N->getLocation())) { + ProgramPoint P = N->getLocation(); + assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P)); + (void) P; + assert(N->pred_size() == 1); + N = *N->pred_begin(); + } + assert(N->pred_size() == 1); + N = *N->pred_begin(); + BlockEdge BE = cast<BlockEdge>(N->getLocation()); + SVal X; + + // Determine the value of the expression by introspecting how we + // got this location in the CFG. This requires looking at the previous + // block we were in and what kind of control-flow transfer was involved. + const CFGBlock *SrcBlock = BE.getSrc(); + // The only terminator (if there is one) that makes sense is a logical op. + CFGTerminator T = SrcBlock->getTerminator(); + if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) { + (void) Term; + assert(Term->isLogicalOp()); + assert(SrcBlock->succ_size() == 2); + // Did we take the true or false branch? + unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0; + X = svalBuilder.makeIntVal(constant, B->getType()); } else { - // We took the LHS expression. Depending on whether we are '&&' or - // '||' we know what the value of the expression is via properties of - // the short-circuiting. - X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, - B->getType()); - Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); + // If there is no terminator, by construction the last statement + // in SrcBlock is the value of the enclosing expression. + assert(!SrcBlock->empty()); + CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin()); + const Stmt *S = Elem.getStmt(); + X = N->getState()->getSVal(S, Pred->getLocationContext()); } + + Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); } void ExprEngine::VisitInitListExpr(const InitListExpr *IE, @@ -519,16 +572,17 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, svalBuilder.makeCompoundVal(T, vals))); return; } - - if (Loc::isLocType(T) || T->isIntegerType()) { - assert(IE->getNumInits() == 1); - const Expr *initEx = IE->getInit(0); - B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, - state->getSVal(initEx, LCtx))); - return; - } - - llvm_unreachable("unprocessed InitListExpr type"); + + // Handle scalars: int{5} and int{}. + assert(NumInitElements <= 1); + + SVal V; + if (NumInitElements == 0) + V = getSValBuilder().makeZeroVal(T); + else + V = state->getSVal(IE->getInit(0), LCtx); + + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); } void ExprEngine::VisitGuardedExpr(const Expr *Ex, @@ -537,17 +591,41 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - SVal X = state->getSVal(Ex, LCtx); - assert (X.isUndef()); - const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); - assert(SE); - X = state->getSVal(SE, LCtx); - - // Make sure that we invalidate the previous binding. - B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true)); + const CFGBlock *SrcBlock = 0; + + for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { + ProgramPoint PP = N->getLocation(); + if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) { + assert(N->pred_size() == 1); + continue; + } + SrcBlock = cast<BlockEdge>(&PP)->getSrc(); + break; + } + + // Find the last expression in the predecessor block. That is the + // expression that is used for the value of the ternary expression. + bool hasValue = false; + SVal V; + + for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(), + E = SrcBlock->rend(); I != E; ++I) { + CFGElement CE = *I; + if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) { + const Expr *ValEx = cast<Expr>(CS->getStmt()); + hasValue = true; + V = state->getSVal(ValEx, LCtx); + break; + } + } + + assert(hasValue); + (void) hasValue; + + // Generate a new node with the binding from the appropriate path. + B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true)); } void ExprEngine:: @@ -648,7 +726,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, } case UO_Plus: - assert(!U->isLValue()); + assert(!U->isGLValue()); // FALL-THROUGH. case UO_Deref: case UO_AddrOf: @@ -671,7 +749,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_LNot: case UO_Minus: case UO_Not: { - assert (!U->isLValue()); + assert (!U->isGLValue()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); @@ -796,7 +874,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, // Since the lvalue-to-rvalue conversion is explicit in the AST, // we bind an l-value if the operator is prefix and an lvalue (in C++). - if (U->isLValue()) + if (U->isGLValue()) state = state->BindExpr(U, LCtx, loc); else state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index a14a491333f2..44a860f689da 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -14,26 +14,14 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/PrettyStackTrace.h" using namespace clang; using namespace ento; -const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D, - const StackFrameContext *SFC) { - const Type *T = D->getTypeForDecl(); - QualType PT = getContext().getPointerType(QualType(T, 0)); - return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC); -} - -const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, - const StackFrameContext *frameCtx) { - return svalBuilder.getRegionManager(). - getCXXThisRegion(decl->getThisType(getContext()), frameCtx); -} - void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -53,208 +41,220 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R))); } -void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - VisitCXXConstructExpr(expr, 0, Pred, Dst); -} - -void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, - const MemRegion *Dest, +void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + const MemRegion *Target = 0; + + switch (CE->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: { + // See if we're constructing an existing region by looking at the next + // element in the CFG. + const CFGBlock *B = currentBuilderContext->getBlock(); + if (currentStmtIdx + 1 < B->size()) { + CFGElement Next = (*B)[currentStmtIdx+1]; + + // Is this a constructor for a local variable? + if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) { + if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { + if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { + if (Var->getInit()->IgnoreImplicit() == CE) { + QualType Ty = Var->getType(); + if (const ArrayType *AT = getContext().getAsArrayType(Ty)) { + // FIXME: Handle arrays, which run the same constructor for + // every element. This workaround will just run the first + // constructor (which should still invalidate the entire array). + SVal Base = State->getLValue(Var, LCtx); + Target = State->getLValue(AT->getElementType(), + getSValBuilder().makeZeroArrayIndex(), + Base).getAsRegion(); + } else { + Target = State->getLValue(Var, LCtx).getAsRegion(); + } + } + } + } + } + + // Is this a constructor for a member? + if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) { + const CXXCtorInitializer *Init = InitElem->getInitializer(); + assert(Init->isAnyMemberInitializer()); + + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (Init->isIndirectMemberInitializer()) { + SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal); + Target = Field.getAsRegion(); + } else { + SVal Field = State->getLValue(Init->getMember(), ThisVal); + Target = Field.getAsRegion(); + } + } -#if 0 - const CXXConstructorDecl *CD = E->getConstructor(); - assert(CD); -#endif - -#if 0 - if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) - // FIXME: invalidate the object. - return; -#endif - -#if 0 - // Is the constructor elidable? - if (E->isElidable()) { - destNodes.Add(Pred); - return; - } -#endif - - // Perform the previsit of the constructor. - ExplodedNodeSet SrcNodes; - SrcNodes.Add(Pred); - ExplodedNodeSet TmpNodes; - getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this); - - // Evaluate the constructor. Currently we don't now allow checker-specific - // implementations of specific constructors (as we do with ordinary - // function calls. We can re-evaluate this in the future. - -#if 0 - // Inlining currently isn't fully implemented. - - if (AMgr.shouldInlineCall()) { - if (!Dest) - Dest = - svalBuilder.getRegionManager().getCXXTempObjectRegion(E, - Pred->getLocationContext()); - - // The callee stack frame context used to create the 'this' - // parameter region. - const StackFrameContext *SFC = - AMgr.getStackFrame(CD, Pred->getLocationContext(), - E, currentBuilderContext->getBlock(), - currentStmtIdx); - - // Create the 'this' region. - const CXXThisRegion *ThisR = - getCXXThisRegion(E->getConstructor()->getParent(), SFC); - - CallEnter Loc(E, SFC, Pred->getLocationContext()); - - StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext); - for (ExplodedNodeSet::iterator NI = SrcNodes.begin(), - NE = SrcNodes.end(); NI != NE; ++NI) { - ProgramStateRef state = (*NI)->getState(); - // Setup 'this' region, so that the ctor is evaluated on the object pointed - // by 'Dest'. - state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - Bldr.generateNode(Loc, *NI, state); + // FIXME: This will eventually need to handle new-expressions as well. } + + // If we couldn't find an existing region to construct into, we'll just + // generate a symbolic region, which is fine. + + break; } -#endif - - // Default semantics: invalidate all regions passed as arguments. - ExplodedNodeSet destCall; - { - StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext); - for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end(); - i != e; ++i) - { - ExplodedNode *Pred = *i; - const LocationContext *LC = Pred->getLocationContext(); - ProgramStateRef state = Pred->getState(); - - state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC); - Bldr.generateNode(E, Pred, state); + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + case CXXConstructExpr::CK_Delegating: { + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { + Target = ThisVal.getAsRegion(); + } else { + // Cast to the base type. + QualType BaseTy = CE->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + Target = BaseVal.getAsRegion(); } + break; + } } - // Do the post visit. - getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXConstructorCall> Call = + CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); + + ExplodedNodeSet DstPreVisit; + getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit, + *Call, *this); + + ExplodedNodeSet DstInvalidated; + StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated, + *Call, *this); + getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } -void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, - const MemRegion *Dest, - const Stmt *S, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) - return; +void ExprEngine::VisitCXXDestructor(QualType ObjectType, + const MemRegion *Dest, + const Stmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) { + ObjectType = AT->getElementType(); + Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(), + loc::MemRegionVal(Dest)).getAsRegion(); + } - // Create the context for 'this' region. - const StackFrameContext *SFC = - AnalysisDeclContexts.getContext(DD)-> - getStackFrame(Pred->getLocationContext(), S, - currentBuilderContext->getBlock(), currentStmtIdx); + const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); + assert(RecordDecl && "Only CXXRecordDecls should have destructors"); + const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); - const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXDestructorCall> Call = + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx); - CallEnter PP(S, SFC, Pred->getLocationContext()); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Call->getSourceRange().getBegin(), + "Error evaluating destructor"); - ProgramStateRef state = Pred->getState(); - state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - Bldr.generateNode(PP, Pred, state); + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); + + ExplodedNodeSet DstInvalidated; + StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, + *Call, *this); } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // FIXME: Much of this should eventually migrate to CXXAllocatorCall. + // Also, we need to decide how allocators actually work -- they're not + // really part of the CXXNewExpr because they happen BEFORE the + // CXXConstructExpr subexpression. See PR12014 for some discussion. StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); unsigned blockCount = currentBuilderContext->getCurrentBlockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = - svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount); - const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); - const ElementRegion *EleReg = - getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount); + ProgramStateRef State = Pred->getState(); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXAllocatorCall> Call = + CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + + // Invalidate placement args. + // FIXME: Once we figure out how we want allocators to work, + // we should be using the usual pre-/(default-)eval-/post-call checks here. + State = Call->invalidateRegions(blockCount); if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - ProgramStateRef state = Pred->getState(); - state = state->BindExpr(CNE, Pred->getLocationContext(), + const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + State = State->BindExpr(CNE, Pred->getLocationContext(), loc::MemRegionVal(EleReg)); - Bldr.generateNode(CNE, Pred, state); + Bldr.generateNode(CNE, Pred, State); return; } - // FIXME: Update for AST changes. -#if 0 - // Evaluate constructor arguments. - const FunctionProtoType *FnType = NULL; - const CXXConstructorDecl *CD = CNE->getConstructor(); - if (CD) - FnType = CD->getType()->getAs<FunctionProtoType>(); - ExplodedNodeSet argsEvaluated; - Bldr.takeNodes(Pred); - evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(), - FnType, Pred, argsEvaluated); - Bldr.addNodes(argsEvaluated); - - // Initialize the object region and bind the 'new' expression. - for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), - E = argsEvaluated.end(); I != E; ++I) { - - ProgramStateRef state = (*I)->getState(); - - // Accumulate list of regions that are invalidated. - // FIXME: Eventually we should unify the logic for constructor - // processing in one place. - SmallVector<const MemRegion*, 10> regionsToInvalidate; - for (CXXNewExpr::const_arg_iterator - ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end(); - ai != ae; ++ai) - { - SVal val = state->getSVal(*ai, (*I)->getLocationContext()); - if (const MemRegion *region = val.getAsRegion()) - regionsToInvalidate.push_back(region); - } + // FIXME: Once we have proper support for CXXConstructExprs inside + // CXXNewExpr, we need to make sure that the constructed object is not + // immediately invalidated here. (The placement call should happen before + // the constructor call anyway.) + FunctionDecl *FD = CNE->getOperatorNew(); + if (FD && FD->isReservedGlobalPlacementOperator()) { + // Non-array placement new should always return the placement location. + SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); + State = State->BindExpr(CNE, LCtx, PlacementLoc); + } else { + State = State->BindExpr(CNE, LCtx, symVal); + } - if (ObjTy->isRecordType()) { - regionsToInvalidate.push_back(EleReg); - // Invalidate the regions. - // TODO: Pass the call to new information as the last argument, to limit - // the globals which will get invalidated. - state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, 0); - - } else { - // Invalidate the regions. - // TODO: Pass the call to new information as the last argument, to limit - // the globals which will get invalidated. - state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, 0); - - if (CNE->hasInitializer()) { - SVal V = state->getSVal(*CNE->constructor_arg_begin(), - (*I)->getLocationContext()); - state = state->bindLoc(loc::MemRegionVal(EleReg), V); - } else { - // Explicitly set to undefined, because currently we retrieve symbolic - // value from symbolic region. - state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal()); - } + // If the type is not a record, we won't have a CXXConstructExpr as an + // initializer. Copy the value over. + if (const Expr *Init = CNE->getInitializer()) { + if (!isa<CXXConstructExpr>(Init)) { + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + (void)ObjTy; + assert(!ObjTy->isRecordType()); + SVal Location = State->getSVal(CNE, LCtx); + if (isa<Loc>(Location)) + State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx)); } - state = state->BindExpr(CNE, (*I)->getLocationContext(), - loc::MemRegionVal(EleReg)); - Bldr.generateNode(CNE, *I, state); } -#endif + + Bldr.generateNode(CNE, Pred, State); } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b9f4e153d7e1..8ee6723057a6 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -11,16 +11,25 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ExprEngine" + +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" +#define CXX_INLINING_ENABLED 1 + using namespace clang; using namespace ento; +STATISTIC(NumOfDynamicDispatchPathSplits, + "The # of times we split the path due to imprecise dynamic dispatch info"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -37,11 +46,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); - // Construct a new state which contains the mapping from actual to - // formal arguments. - const LocationContext *callerCtx = Pred->getLocationContext(); - ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx, - calleeCtx); + ProgramStateRef state = Pred->getState(); // Construct a new node and add it to the worklist. bool isNew; @@ -51,71 +56,178 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { Engine.getWorkList()->enqueue(Node); } -static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) { +// Find the last statement on the path to the exploded node and the +// corresponding Block. +static std::pair<const Stmt*, + const CFGBlock*> getLastStmt(const ExplodedNode *Node) { + const Stmt *S = 0; + const StackFrameContext *SF = + Node->getLocation().getLocationContext()->getCurrentStackFrame(); + + // Back up through the ExplodedGraph until we reach a statement node. while (Node) { const ProgramPoint &PP = Node->getLocation(); - // Skip any BlockEdges. - if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) { - assert(Node->pred_size() == 1); - Node = *Node->pred_begin(); - continue; - } + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { - const Stmt *S = SP->getStmt(); - return dyn_cast<ReturnStmt>(S); + S = SP->getStmt(); + break; + } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) { + S = CEE->getCalleeContext()->getCallSite(); + if (S) + break; + // If we have an implicit call, we'll probably end up with a + // StmtPoint inside the callee, which is acceptable. + // (It's possible a function ONLY contains implicit calls -- such as an + // implicitly-generated destructor -- so we shouldn't just skip back to + // the CallEnter node and keep going.) + } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) { + // If we reached the CallEnter for this function, it has no statements. + if (CE->getCalleeContext() == SF) + break; } - break; + + Node = *Node->pred_begin(); } - return 0; + + const CFGBlock *Blk = 0; + if (S) { + // Now, get the enclosing basic block. + while (Node && Node->pred_size() >=1 ) { + const ProgramPoint &PP = Node->getLocation(); + if (isa<BlockEdge>(PP) && + (PP.getLocationContext()->getCurrentStackFrame() == SF)) { + BlockEdge &EPP = cast<BlockEdge>(PP); + Blk = EPP.getDst(); + break; + } + Node = *Node->pred_begin(); + } + } + + return std::pair<const Stmt*, const CFGBlock*>(S, Blk); } -void ExprEngine::processCallExit(ExplodedNode *Pred) { - ProgramStateRef state = Pred->getState(); - const StackFrameContext *calleeCtx = - Pred->getLocationContext()->getCurrentStackFrame(); - const LocationContext *callerCtx = calleeCtx->getParent(); - const Stmt *CE = calleeCtx->getCallSite(); +/// The call exit is simulated with a sequence of nodes, which occur between +/// CallExitBegin and CallExitEnd. The following operations occur between the +/// two program points: +/// 1. CallExitBegin (triggers the start of call exit sequence) +/// 2. Bind the return value +/// 3. Run Remove dead bindings to clean up the dead symbols from the callee. +/// 4. CallExitEnd (switch to the caller context) +/// 5. PostStmt<CallExpr> +void ExprEngine::processCallExit(ExplodedNode *CEBNode) { + // Step 1 CEBNode was generated before the call. + + const StackFrameContext *calleeCtx = + CEBNode->getLocationContext()->getCurrentStackFrame(); + + // The parent context might not be a stack frame, so make sure we + // look up the first enclosing stack frame. + const StackFrameContext *callerCtx = + calleeCtx->getParent()->getCurrentStackFrame(); + const Stmt *CE = calleeCtx->getCallSite(); + ProgramStateRef state = CEBNode->getState(); + // Find the last statement in the function and the corresponding basic block. + const Stmt *LastSt = 0; + const CFGBlock *Blk = 0; + llvm::tie(LastSt, Blk) = getLastStmt(CEBNode); + + // Step 2: generate node with bound return value: CEBNode -> BindedRetNode. + // If the callee returns an expression, bind its value to CallExpr. - if (const ReturnStmt *RS = getReturnStmt(Pred)) { - const LocationContext *LCtx = Pred->getLocationContext(); - SVal V = state->getSVal(RS, LCtx); - state = state->BindExpr(CE, callerCtx, V); + if (CE) { + if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { + const LocationContext *LCtx = CEBNode->getLocationContext(); + SVal V = state->getSVal(RS, LCtx); + state = state->BindExpr(CE, callerCtx, V); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { + loc::MemRegionVal This = + svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); + SVal ThisV = state->getSVal(This); + + // Always bind the region to the CXXConstructExpr. + state = state->BindExpr(CCE, callerCtx, ThisV); + } } - - // Bind the constructed object value to CXXConstructExpr. - if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { - const CXXThisRegion *ThisR = - getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); - - SVal ThisV = state->getSVal(ThisR); - // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); + + // Step 3: BindedRetNode -> CleanedNodes + // If we can find a statement and a block in the inlined function, run remove + // dead bindings before returning from the call. This is important to ensure + // that we report the issues such as leaks in the stack contexts in which + // they occurred. + ExplodedNodeSet CleanedNodes; + if (LastSt && Blk) { + static SimpleProgramPointTag retValBind("ExprEngine : Bind Return Value"); + PostStmt Loc(LastSt, calleeCtx, &retValBind); + bool isNew; + ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); + BindedRetNode->addPredecessor(CEBNode, G); + if (!isNew) + return; + + NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); + currentBuilderContext = &Ctx; + // Here, we call the Symbol Reaper with 0 statement and caller location + // context, telling it to clean up everything in the callee's context + // (and it's children). We use LastStmt as a diagnostic statement, which + // which the PreStmtPurge Dead point will be associated. + removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, + ProgramPoint::PostStmtPurgeDeadSymbolsKind); + currentBuilderContext = 0; + } else { + CleanedNodes.Add(CEBNode); } - - static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); - PostStmt Loc(CE, callerCtx, &returnTag); - bool isNew; - ExplodedNode *N = G.getNode(Loc, state, false, &isNew); - N->addPredecessor(Pred, G); - if (!isNew) - return; - - // Perform the post-condition check of the CallExpr. - ExplodedNodeSet Dst; - NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N); - SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, - &Ctx); - SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); - - getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this, - /* wasInlined */ true); - - // Enqueue the next element in the block. - for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { - Engine.getWorkList()->enqueue(*I, - calleeCtx->getCallSiteBlock(), - calleeCtx->getIndex()+1); + + for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), + E = CleanedNodes.end(); I != E; ++I) { + + // Step 4: Generate the CallExit and leave the callee's context. + // CleanedNodes -> CEENode + CallExitEnd Loc(calleeCtx, callerCtx); + bool isNew; + ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); + ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); + CEENode->addPredecessor(*I, G); + if (!isNew) + return; + + // Step 5: Perform the post-condition check of the CallExpr and enqueue the + // result onto the work list. + // CEENode -> Dst -> WorkList + NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); + SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, + &Ctx); + SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call, + *this, true); + + ExplodedNodeSet Dst; + if (isa<ObjCMethodCall>(Call)) { + getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, + cast<ObjCMethodCall>(*Call), + *this, true); + } else if (CE) { + getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, + *this, true); + } else { + Dst.insert(DstPostCall); + } + + // Enqueue the next element in the block. + for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); + PSI != PSE; ++PSI) { + Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), + calleeCtx->getIndex()+1); + } } } @@ -130,8 +242,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { } // Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); +bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const CFG *CalleeCFG = CalleeADC->getCFG(); // It is possible that the CFG cannot be constructed. @@ -143,258 +255,185 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { == AMgr.InlineMaxStackDepth) return false; - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) return false; if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) return false; - return true; -} + // Do not inline variadic calls (for now). + if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { + if (BD->isVariadic()) + return false; + } + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isVariadic()) + return false; + } -// For now, skip inlining variadic functions. -// We also don't inline blocks. -static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { - if (!E->getAnalysisManager().shouldInlineCall()) + // It is possible that the live variables analysis cannot be + // run. If so, bail out. + if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) return false; - QualType callee = CE->getCallee()->getType(); - const FunctionProtoType *FT = 0; - if (const PointerType *PT = callee->getAs<PointerType>()) - FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); - else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { - // FIXME: inline blocks. - // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); - (void) BT; - return false; - } - // If we have no prototype, assume the function is okay. - if (!FT) - return true; - // Skip inlining of variadic functions. - return !FT->isVariadic(); + return true; } -bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, - const CallExpr *CE, - ExplodedNode *Pred) { - if (!shouldInlineCallExpr(CE, this)) - return false; - - ProgramStateRef state = Pred->getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = - state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); - if (!FD || !FD->hasBody(FD)) - return false; - - switch (CE->getStmtClass()) { - default: - // FIXME: Handle C++. - break; - case Stmt::CallExprClass: { - if (!shouldInlineDecl(FD, Pred)) +/// The GDM component containing the dynamic dispatch bifurcation info. When +/// the exact type of the receiver is not known, we want to explore both paths - +/// one on which we do inline it and the other one on which we don't. This is +/// done to ensure we do not drop coverage. +/// This is the map from the receiver region to a bool, specifying either we +/// consider this region's information precise or not along the given path. +namespace clang { +namespace ento { +enum DynamicDispatchMode { DynamicDispatchModeInlined = 1, + DynamicDispatchModeConservative }; + +struct DynamicDispatchBifurcationMap {}; +typedef llvm::ImmutableMap<const MemRegion*, + unsigned int> DynamicDispatchBifur; +template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap> + : public ProgramStatePartialTrait<DynamicDispatchBifur> { + static void *GDMIndex() { static int index; return &index; } +}; + +}} + +bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, + ProgramStateRef State) { + assert(D); + + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + const LocationContext *ParentOfCallee = 0; + + // FIXME: Refactor this check into a hypothetical CallEvent::canInline. + switch (Call.getKind()) { + case CE_Function: + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + if (!CXX_INLINING_ENABLED) + return false; + break; + case CE_CXXConstructor: { + if (!CXX_INLINING_ENABLED) + return false; + + // Only inline constructors and destructors if we built the CFGs for them + // properly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddImplicitDtors || + !ADC->getCFGBuildOptions().AddInitializers) + return false; + + const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + + // FIXME: We don't handle constructors or destructors for arrays properly. + const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return false; + + // FIXME: This is a hack. We don't handle temporary destructors + // right now, so we shouldn't inline their constructors. + const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); + if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) + if (!Target || !isa<DeclRegion>(Target)) return false; - // Construct a new stack frame for the callee. - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); - const StackFrameContext *CallerSFC = - Pred->getLocationContext()->getCurrentStackFrame(); - const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(CallerSFC, CE, - currentBuilderContext->getBlock(), - currentStmtIdx); - - CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); - bool isNew; - if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { - N->addPredecessor(Pred, G); - if (isNew) - Engine.getWorkList()->enqueue(N); - } - return true; - } + break; } - return false; -} + case CE_CXXDestructor: { + if (!CXX_INLINING_ENABLED) + return false; -static bool isPointerToConst(const ParmVarDecl *ParamDecl) { - QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType(); - if (PointeeTy != QualType() && PointeeTy.isConstQualified() && - !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) { - return true; - } - return false; -} + // Only inline constructors and destructors if we built the CFGs for them + // properly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddImplicitDtors || + !ADC->getCFGBuildOptions().AddInitializers) + return false; -// Try to retrieve the function declaration and find the function parameter -// types which are pointers/references to a non-pointer const. -// We do not invalidate the corresponding argument regions. -static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, - const CallOrObjCMessage &Call) { - const Decl *CallDecl = Call.getDecl(); - if (!CallDecl) - return; + const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call); - if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) { - const IdentifierInfo *II = FDecl->getIdentifier(); - - // List the cases, where the region should be invalidated even if the - // argument is const. - if (II) { - StringRef FName = II->getName(); - // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a - // value into thread local storage. The value can later be retrieved with - // 'void *ptheread_getspecific(pthread_key)'. So even thought the - // parameter is 'const void *', the region escapes through the call. - // - funopen - sets a buffer for future IO calls. - // - ObjC functions that end with "NoCopy" can free memory, of the passed - // in buffer. - // - Many CF containers allow objects to escape through custom - // allocators/deallocators upon container construction. - // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - if (FName == "pthread_setspecific" || - FName == "funopen" || - FName.endswith("NoCopy") || - (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) || - Call.isCFCGAllowingEscape(FName)) - return; - } + // FIXME: We don't handle constructors or destructors for arrays properly. + const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return false; - for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) { - if (FDecl && Idx < FDecl->getNumParams()) { - if (isPointerToConst(FDecl->getParamDecl(Idx))) - PreserveArgs.insert(Idx); - } - } - return; + break; } + case CE_CXXAllocator: + if (!CXX_INLINING_ENABLED) + return false; - if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) { - assert(MDecl->param_size() <= Call.getNumArgs()); - unsigned Idx = 0; - for (clang::ObjCMethodDecl::param_const_iterator - I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) { - if (isPointerToConst(*I)) - PreserveArgs.insert(Idx); - } - return; + // Do not inline allocators until we model deallocators. + // This is unfortunate, but basically necessary for smart pointers and such. + return false; + case CE_Block: { + const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); + assert(BR && "If we have the block definition we should have its region"); + AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); + ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + cast<BlockDecl>(D), + BR); + break; } -} - -ProgramStateRef -ExprEngine::invalidateArguments(ProgramStateRef State, - const CallOrObjCMessage &Call, - const LocationContext *LC) { - SmallVector<const MemRegion *, 8> RegionsToInvalidate; - - if (Call.isObjCMessage()) { - // Invalidate all instance variables of the receiver of an ObjC message. - // FIXME: We should be able to do better with inter-procedural analysis. - if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion()) - RegionsToInvalidate.push_back(MR); - - } else if (Call.isCXXCall()) { - // Invalidate all instance variables for the callee of a C++ method call. - // FIXME: We should be able to do better with inter-procedural analysis. - // FIXME: We can probably do better for const versus non-const methods. - if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion()) - RegionsToInvalidate.push_back(Callee); - - } else if (Call.isFunctionCall()) { - // Block calls invalidate all captured-by-reference values. - SVal CalleeVal = Call.getFunctionCallee(); - if (const MemRegion *Callee = CalleeVal.getAsRegion()) { - if (isa<BlockDataRegion>(Callee)) - RegionsToInvalidate.push_back(Callee); - } + case CE_ObjCMessage: + if (!(getAnalysisManager().IPAMode == DynamicDispatch || + getAnalysisManager().IPAMode == DynamicDispatchBifurcate)) + return false; + break; } - // Indexes of arguments whose values will be preserved by the call. - llvm::SmallSet<unsigned, 1> PreserveArgs; - findPtrToConstParams(PreserveArgs, Call); - - for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) { - if (PreserveArgs.count(idx)) - continue; - - SVal V = Call.getArgSVal(idx); - - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) - V = Wrapped->getLoc(); - else if (!isa<Loc>(V)) - continue; - - if (const MemRegion *R = V.getAsRegion()) { - // Invalidate the value of the variable passed by reference. - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underlying region - // is a variable region then strip off the ElementRegion. - // FIXME: We really need to think about this for the general case - // as sometimes we are reasoning about arrays and other times - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // appropriately delegated to the respective StoreManagers while - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralOrEnumerationType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - } else { - // Nuke all other arguments passed by reference. - // FIXME: is this necessary or correct? This handles the non-Region - // cases. Is it ever valid to store to these? - State = State->unbindLoc(cast<Loc>(V)); - } - } + if (!shouldInlineDecl(D, Pred)) + return false; + + if (!ParentOfCallee) + ParentOfCallee = CallerSFC; + + // This may be NULL, but that's fine. + const Expr *CallE = Call.getOriginExpr(); + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(ParentOfCallee, CallE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CallE, CalleeSFC, CurLC); - // Invalidate designated regions using the batch invalidation API. + // Construct a new state which contains the mapping from actual to + // formal arguments. + State = State->enterStackFrame(Call, CalleeSFC); - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned Count = currentBuilderContext->getCurrentBlockCount(); - StoreManager::InvalidatedSymbols IS; + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } - // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate - // global variables. - return State->invalidateRegions(RegionsToInvalidate, - Call.getOriginExpr(), Count, LC, - &IS, &Call); + // If we decided to inline the call, the successor has been manually + // added onto the work list so remove it from the node builder. + Bldr.takeNodes(Pred); + return true; } -static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, - const CallExpr *CE) { - void *ReplayState = N->getState()->get<ReplayWithoutInlining>(); +static ProgramStateRef getInlineFailedState(ProgramStateRef State, + const Stmt *CallE) { + void *ReplayState = State->get<ReplayWithoutInlining>(); if (!ReplayState) return 0; - const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); - if (CE == ReplayCE) { - return N->getState()->remove<ReplayWithoutInlining>(); - } - return 0; + + assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); + (void)CallE; + + return State->remove<ReplayWithoutInlining>(); } void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, @@ -402,74 +441,175 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Perform the previsit of the CallExpr. ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); - - // Now evaluate the call itself. - class DefaultEval : public GraphExpander { - ExprEngine &Eng; - const CallExpr *CE; - public: - - DefaultEval(ExprEngine &eng, const CallExpr *ce) - : Eng(eng), CE(ce) {} - virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - - ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); - - // First, try to inline the call. - if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) - return; - // First handle the return value. - StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); + // Get the call in its initial state. We use this as a template to perform + // all the checks. + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> CallTemplate + = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); - // Get the callee. - const Expr *Callee = CE->getCallee()->IgnoreParens(); - if (state == 0) - state = Pred->getState(); - SVal L = state->getSVal(Callee, Pred->getLocationContext()); + // Evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet dstCallEvaluated; + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I != E; ++I) { + evalCall(dstCallEvaluated, *I, *CallTemplate); + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + // Note that if the call was inlined, dstCallEvaluated will be empty. + // The post-CallExpr check will occur in processCallExit. + getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, + *this); +} - // Figure out the result type. We do this dance to handle references. - QualType ResultTy; - if (const FunctionDecl *FD = L.getAsFunctionDecl()) - ResultTy = FD->getResultType(); - else - ResultTy = CE->getType(); +void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call) { + // WARNING: At this time, the state attached to 'Call' may be older than the + // state in 'Pred'. This is a minor optimization since CheckerManager will + // use an updated CallEvent instance when calling checkers, but if 'Call' is + // ever used directly in this function all callers should be updated to pass + // the most recent state. (It is probably not worth doing the work here since + // for some callers this will not be necessary.) - if (CE->isLValue()) - ResultTy = Eng.getContext().getPointerType(ResultTy); + // Run any pre-call checks using the generic call interface. + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this); - // Conjure a symbol value to use as the result. - SValBuilder &SVB = Eng.getSValBuilder(); - unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); - const LocationContext *LCtx = Pred->getLocationContext(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); + // Actually evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call, and get a callback at + // defaultEvalCall if all of them fail. + ExplodedNodeSet dstCallEvaluated; + getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, + Call, *this); - // Generate a new state with the return value set. - state = state->BindExpr(CE, LCtx, RetVal); + // Finally, run any post-call checks. + getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated, + Call, *this); +} - // Invalidate the arguments. - state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), - LCtx); +ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, + const LocationContext *LCtx, + ProgramStateRef State) { + const Expr *E = Call.getOriginExpr(); + if (!E) + return State; - // And make the result node. - Bldr.generateNode(CE, Pred, state); + // Some method families have known return values. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + switch (Msg->getMethodFamily()) { + default: + break; + case OMF_autorelease: + case OMF_retain: + case OMF_self: { + // These methods return their receivers. + return State->BindExpr(E, LCtx, Msg->getReceiverSVal()); } - }; - - // Finally, evaluate the function call. We try each of the checkers - // to see if the can evaluate the function call. - ExplodedNodeSet dstCallEvaluated; - DefaultEval defEval(*this, CE); - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, - dstPreVisit, - CE, *this, &defEval); - - // Finally, perform the post-condition check of the CallExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, - *this); + } + } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ + return State->BindExpr(E, LCtx, C->getCXXThisVal()); + } + + // Conjure a symbol if the return value is unknown. + QualType ResultTy = Call.getResultType(); + SValBuilder &SVB = getSValBuilder(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count); + return State->BindExpr(E, LCtx, R); +} + +// Conservatively evaluate call by invalidating regions and binding +// a conjured return value. +void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State) { + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + State = Call.invalidateRegions(Count, State); + State = bindReturnValue(Call, Pred->getLocationContext(), State); + + // And make the result node. + Bldr.generateNode(Call.getProgramPoint(), State, Pred); +} + +void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &CallTemplate) { + // Make sure we have the most recent state attached to the call. + ProgramStateRef State = Pred->getState(); + CallEventRef<> Call = CallTemplate.cloneWithState(State); + + if (!getAnalysisManager().shouldInlineCall()) { + conservativeEvalCall(*Call, Bldr, Pred, State); + return; + } + // Try to inline the call. + // The origin expression here is just used as a kind of checksum; + // this should still be safe even for CallEvents that don't come from exprs. + const Expr *E = Call->getOriginExpr(); + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + + if (InlinedFailedState) { + // If we already tried once and failed, make sure we don't retry later. + State = InlinedFailedState; + } else { + RuntimeDefinition RD = Call->getRuntimeDefinition(); + const Decl *D = RD.getDecl(); + if (D) { + // Explore with and without inlining the call. + if (RD.mayHaveOtherDefinitions() && + getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { + BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + return; + } + // We are not bifurcating and we do have a Decl, so just inline. + if (inlineCall(*Call, D, Bldr, Pred, State)) + return; + } + } + + // If we can't inline it, handle the return value and invalidate the regions. + conservativeEvalCall(*Call, Bldr, Pred, State); } +void ExprEngine::BifurcateCall(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred) { + assert(BifurReg); + + // Check if we've performed the split already - note, we only want + // to split the path once per memory region. + ProgramStateRef State = Pred->getState(); + const unsigned int *BState = + State->get<DynamicDispatchBifurcationMap>(BifurReg); + if (BState) { + // If we are on "inline path", keep inlining if possible. + if (*BState == DynamicDispatchModeInlined) + if (inlineCall(Call, D, Bldr, Pred, State)) + return; + // If inline failed, or we are on the path where we assume we + // don't have enough info about the receiver to inline, conjure the + // return value and invalidate the regions. + conservativeEvalCall(Call, Bldr, Pred, State); + return; + } + + // If we got here, this is the first time we process a message to this + // region, so split the path. + ProgramStateRef IState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeInlined); + inlineCall(Call, D, Bldr, Pred, IState); + + ProgramStateRef NoIState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeConservative); + conservativeEvalCall(Call, Bldr, Pred, NoIState); + + NumOfDynamicDispatchPathSplits++; + return; +} + + void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index c8ad70ad0358..e3bc498b51e8 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -13,8 +13,8 @@ #include "clang/AST/StmtObjC.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" using namespace clang; using namespace ento; @@ -74,7 +74,6 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, const Stmt *elem = S->getElement(); ProgramStateRef state = Pred->getState(); SVal elementV; - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); @@ -86,10 +85,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, } ExplodedNodeSet dstLocation; - Bldr.takeNodes(Pred); evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false); - Bldr.addNodes(dstLocation); - + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + for (ExplodedNodeSet::iterator NI = dstLocation.begin(), NE = dstLocation.end(); NI!=NE; ++NI) { Pred = *NI; @@ -126,148 +126,135 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, Bldr.generateNode(S, Pred, hasElems); Bldr.generateNode(S, Pred, noElems); } + + // Finally, run any custom checkers. + // FIXME: Eventually all pre- and post-checks should live in VisitStmt. + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); +} + +static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) { + if (!Class) + return false; + if (Class->getIdentifier() == II) + return true; + return isSubclass(Class->getSuperClass(), II); } -void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, +void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<ObjCMethodCall> Msg = + CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + // Handle the previsits checks. ExplodedNodeSet dstPrevisit; - getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, - msg, *this); - + getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, + *Msg, *this); + ExplodedNodeSet dstGenericPrevisit; + getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, + *Msg, *this); + // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext); + StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext); - for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(), - DE = dstPrevisit.end(); DI != DE; ++DI) { - + for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), + DE = dstGenericPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; - bool RaisesException = false; + ProgramStateRef State = Pred->getState(); + CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); - if (const Expr *Receiver = msg.getInstanceReceiver()) { - ProgramStateRef state = Pred->getState(); - SVal recVal = state->getSVal(Receiver, Pred->getLocationContext()); + if (UpdatedMsg->isInstanceMessage()) { + SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); + llvm::tie(notNilState, nilState) = State->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. + // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). + // Revisit once we have lazier constraints. if (nilState && !notNilState) { continue; } // Check if the "raise" message was sent. assert(notNilState); - if (msg.getSelector() == RaiseSel) - RaisesException = true; + if (Msg->getSelector() == RaiseSel) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateNode(currentStmt, Pred, State, true); + continue; + } - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - // Dispatch to plug-in transfer function. - evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException); - } - } - else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { - IdentifierInfo* ClsName = Iface->getIdentifier(); - Selector S = msg.getSelector(); - - // Check for special instance methods. - if (!NSExceptionII) { - ASTContext &Ctx = getContext(); - NSExceptionII = &Ctx.Idents.get("NSException"); + // Generate a transition to non-Nil state. + if (notNilState != State) + Pred = Bldr.generateNode(currentStmt, Pred, notNilState); } - - if (ClsName == NSExceptionII) { - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. - if (!NSExceptionInstanceRaiseSelectors) { + } else { + // Check for special class methods. + if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) { + if (!NSExceptionII) { ASTContext &Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = - new Selector[NUM_RAISE_SELECTORS]; - SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; - unsigned idx = 0; - - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); + NSExceptionII = &Ctx.Idents.get("NSException"); } - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; - break; + if (isSubclass(Iface, NSExceptionII)) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext &Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format:arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); } + + Selector S = Msg->getSelector(); + bool RaisesException = false; + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) { + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + if (RaisesException) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateNode(currentStmt, Pred, Pred->getState(), true); + continue; + } + + } } - - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - // Dispatch to plug-in transfer function. - evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException); } + + // Evaluate the call. + defaultEvalCall(Bldr, Pred, *UpdatedMsg); } + ExplodedNodeSet dstPostvisit; + getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, + *Msg, *this); + // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); + getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, + *Msg, *this); } - -void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr, - const ObjCMessage &msg, - ExplodedNode *Pred, - ProgramStateRef state, - bool GenSink) { - // First handle the return value. - SVal ReturnValue = UnknownVal(); - - // Some method families have known return values. - switch (msg.getMethodFamily()) { - default: - break; - case OMF_autorelease: - case OMF_retain: - case OMF_self: { - // These methods return their receivers. - const Expr *ReceiverE = msg.getInstanceReceiver(); - if (ReceiverE) - ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext()); - break; - } - } - - // If we failed to figure out the return value, use a conjured value instead. - if (ReturnValue.isUnknown()) { - SValBuilder &SVB = getSValBuilder(); - QualType ResultTy = msg.getResultType(getContext()); - unsigned Count = currentBuilderContext->getCurrentBlockCount(); - const Expr *CurrentE = cast<Expr>(currentStmt); - const LocationContext *LCtx = Pred->getLocationContext(); - ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count); - } - - // Bind the return value. - const LocationContext *LCtx = Pred->getLocationContext(); - state = state->BindExpr(currentStmt, LCtx, ReturnValue); - - // Invalidate the arguments (and the receiver) - state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx); - - // And create the new node. - Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink); - assert(Bldr.hasGeneratedNodes()); -} - diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 629f1eab4356..0152e328e508 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -95,37 +95,6 @@ void HTMLDiagnostics::FlushDiagnosticsImpl( } } -static void flattenPath(PathPieces &primaryPath, PathPieces ¤tPath, - const PathPieces &oldPath) { - for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end(); - it != et; ++it ) { - PathDiagnosticPiece *piece = it->getPtr(); - if (const PathDiagnosticCallPiece *call = - dyn_cast<PathDiagnosticCallPiece>(piece)) { - IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = - call->getCallEnterEvent(); - if (callEnter) - currentPath.push_back(callEnter); - flattenPath(primaryPath, primaryPath, call->path); - IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = - call->getCallExitEvent(); - if (callExit) - currentPath.push_back(callExit); - continue; - } - if (PathDiagnosticMacroPiece *macro = - dyn_cast<PathDiagnosticMacroPiece>(piece)) { - currentPath.push_back(piece); - PathPieces newPath; - flattenPath(primaryPath, newPath, macro->subPieces); - macro->subPieces = newPath; - continue; - } - - currentPath.push_back(piece); - } -} - void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, SmallVectorImpl<std::string> *FilesMade) { @@ -152,8 +121,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, return; // First flatten out the entire path to make it easier to use. - PathPieces path; - flattenPath(path, path, D.path); + PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false); // The path as already been prechecked that all parts of the path are // from the same file and that it is non-empty. @@ -428,6 +396,15 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; os << "\">" << num << "</div>"; + + if (num > 1) { + os << "</td><td><div class=\"PathNav\"><a href=\"#Path" + << (num - 1) + << "\" title=\"Previous event (" + << (num - 1) + << ")\">←</a></div></td>"; + } + os << "</td><td>"; } @@ -441,9 +418,10 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); - const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); - Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(), - MacroName, BufferInfo.end()); + std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc(); + const char* MacroName = LocInfo.second + BufferInfo.data(); + Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), + BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; rawLexer.LexFromRawLexer(TheTok); @@ -453,8 +431,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "':\n"; - if (max > 1) - os << "</td></tr></table>"; + if (max > 1) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } // Within a macro piece. Write out each event. ProcessMacroPiece(os, *MP, 0); @@ -462,8 +453,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, else { os << html::EscapeText(P.getString()); - if (max > 1) - os << "</td></tr></table>"; + if (max > 1) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } } os << "</div></td></tr>"; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index ed94c79df1b7..62e602a7e1e1 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -179,7 +179,7 @@ const StackFrameContext *VarRegion::getStackFrame() const { // Region extents. //===----------------------------------------------------------------------===// -DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const { +DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const { ASTContext &Ctx = svalBuilder.getContext(); QualType T = getDesugaredValueType(Ctx); @@ -470,7 +470,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { - os << "base " << decl->getName(); + os << "base{" << superRegion << ',' << decl->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { @@ -518,10 +518,6 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } -void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { - os << "NonStaticGlobalSpaceRegion"; -} - void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalInternalSpaceRegion"; } @@ -534,17 +530,45 @@ void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalImmutableSpaceRegion"; } -void MemRegion::dumpPretty(raw_ostream &os) const { +void HeapSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "HeapSpaceRegion"; +} + +void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "UnknownSpaceRegion"; +} + +void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackArgumentsSpaceRegion"; +} + +void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackLocalsSpaceRegion"; +} + +bool MemRegion::canPrintPretty() const { + return false; +} + +void MemRegion::printPretty(raw_ostream &os) const { return; } -void VarRegion::dumpPretty(raw_ostream &os) const { +bool VarRegion::canPrintPretty() const { + return true; +} + +void VarRegion::printPretty(raw_ostream &os) const { os << getDecl()->getName(); } -void FieldRegion::dumpPretty(raw_ostream &os) const { - superRegion->dumpPretty(os); - os << "->" << getDecl(); +bool FieldRegion::canPrintPretty() const { + return superRegion->canPrintPretty(); +} + +void FieldRegion::printPretty(raw_ostream &os) const { + superRegion->printPretty(os); + os << "." << getDecl()->getName(); } //===----------------------------------------------------------------------===// @@ -643,6 +667,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); } +/// Look through a chain of LocationContexts to either find the +/// StackFrameContext that matches a DeclContext, or find a VarRegion +/// for a variable captured by a block. +static llvm::PointerUnion<const StackFrameContext *, const VarRegion *> +getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD) { + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { + if (cast<DeclContext>(SFC->getDecl()) == DC) + return SFC; + } + if (const BlockInvocationContext *BC = + dyn_cast<BlockInvocationContext>(LC)) { + const BlockDataRegion *BR = + static_cast<const BlockDataRegion*>(BC->getContextData()); + // FIXME: This can be made more efficient. + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); I != E; ++I) { + if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion())) + if (VR->getDecl() == VD) + return cast<VarRegion>(I.getCapturedRegion()); + } + } + + LC = LC->getParent(); + } + return (const StackFrameContext*)0; +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; @@ -675,7 +730,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = + getStackOrCaptureRegionForDeclContext(LC, DC, D); + + if (V.is<const VarRegion*>()) + return V.get<const VarRegion*>(); + + const StackFrameContext *STC = V.get<const StackFrameContext*>(); if (!STC) sReg = getUnknownRegion(); @@ -800,6 +861,10 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); } +const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { + return getSubRegion<SymbolicRegion>(Sym, getHeapRegion()); +} + const FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl *d, const MemRegion* superRegion){ @@ -823,6 +888,37 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E, const CXXBaseObjectRegion * MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl, const MemRegion *superRegion) { + // Check that the base class is actually a direct base of this region. + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) { + if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){ + if (Class->isVirtuallyDerivedFrom(decl)) { + // Virtual base regions should not be layered, since the layout rules + // are different. + while (const CXXBaseObjectRegion *Base = + dyn_cast<CXXBaseObjectRegion>(superRegion)) { + superRegion = Base->getSuperRegion(); + } + assert(superRegion && !isa<MemSpaceRegion>(superRegion)); + + } else { + // Non-virtual bases should always be direct bases. +#ifndef NDEBUG + bool FoundBase = false; + for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), + E = Class->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl() == decl) { + FoundBase = true; + break; + } + } + + assert(FoundBase && "Not a direct base class of this region"); +#endif + } + } + } + return getSubRegion<CXXBaseObjectRegion>(decl, superRegion); } @@ -898,24 +994,26 @@ const MemRegion *MemRegion::getBaseRegion() const { // View handling. //===----------------------------------------------------------------------===// -const MemRegion *MemRegion::StripCasts() const { +const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { const MemRegion *R = this; while (true) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: generalize. Essentially we want to strip away ElementRegions - // that were layered on a symbolic region because of casts. We only - // want to strip away ElementRegions, however, where the index is 0. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { - if (CI->getValue().getSExtValue() == 0) { - R = ER->getSuperRegion(); - continue; - } - } + switch (R->getKind()) { + case ElementRegionKind: { + const ElementRegion *ER = cast<ElementRegion>(R); + if (!ER->getIndex().isZeroConstant()) + return R; + R = ER->getSuperRegion(); + break; + } + case CXXBaseObjectRegionKind: + if (!StripBaseCasts) + return R; + R = cast<CXXBaseObjectRegion>(R)->getSuperRegion(); + break; + default: + return R; } - break; } - return R; } // FIXME: Merge with the implementation of the same method in Store.cpp @@ -973,12 +1071,14 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { RegionOffset MemRegion::getAsOffset() const { const MemRegion *R = this; + const MemRegion *SymbolicOffsetBase = 0; int64_t Offset = 0; while (1) { switch (R->getKind()) { default: - return RegionOffset(0); + return RegionOffset(R, RegionOffset::Symbolic); + case SymbolicRegionKind: case AllocaRegionKind: case CompoundLiteralRegionKind: @@ -987,31 +1087,95 @@ RegionOffset MemRegion::getAsOffset() const { case VarRegionKind: case CXXTempObjectRegionKind: goto Finish; + + case ObjCIvarRegionKind: + // This is a little strange, but it's a compromise between + // ObjCIvarRegions having unknown compile-time offsets (when using the + // non-fragile runtime) and yet still being distinct, non-overlapping + // regions. Thus we treat them as "like" base regions for the purposes + // of computing offsets. + goto Finish; + + case CXXBaseObjectRegionKind: { + const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R); + R = BOR->getSuperRegion(); + + QualType Ty; + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) { + Ty = TVR->getDesugaredValueType(getContext()); + } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + // If our base region is symbolic, we don't know what type it really is. + // Pretend the type of the symbol is the true dynamic type. + // (This will at least be self-consistent for the life of the symbol.) + Ty = SR->getSymbol()->getType(getContext())->getPointeeType(); + } + + const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); + if (!Child) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); + + CharUnits BaseOffset; + const CXXRecordDecl *Base = BOR->getDecl(); + if (Child->isVirtuallyDerivedFrom(Base)) + BaseOffset = Layout.getVBaseClassOffset(Base); + else + BaseOffset = Layout.getBaseClassOffset(Base); + + // The base offset is in chars, not in bits. + Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); + break; + } case ElementRegionKind: { const ElementRegion *ER = cast<ElementRegion>(R); - QualType EleTy = ER->getValueType(); + R = ER->getSuperRegion(); - if (!IsCompleteType(getContext(), EleTy)) - return RegionOffset(0); + QualType EleTy = ER->getValueType(); + if (!IsCompleteType(getContext(), EleTy)) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + continue; + } SVal Index = ER->getIndex(); if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) { + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + int64_t i = CI->getValue().getSExtValue(); - CharUnits Size = getContext().getTypeSizeInChars(EleTy); - Offset += i * Size.getQuantity() * 8; + // This type size is in bits. + Offset += i * getContext().getTypeSize(EleTy); } else { // We cannot compute offset for non-concrete index. - return RegionOffset(0); + SymbolicOffsetBase = R; } - R = ER->getSuperRegion(); break; } case FieldRegionKind: { const FieldRegion *FR = cast<FieldRegion>(R); + R = FR->getSuperRegion(); + const RecordDecl *RD = FR->getDecl()->getParent(); - if (!RD->isCompleteDefinition()) + if (!RD->isCompleteDefinition()) { // We cannot compute offset for incomplete type. - return RegionOffset(0); + SymbolicOffsetBase = R; + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + // Get the field number. unsigned idx = 0; for (RecordDecl::field_iterator FI = RD->field_begin(), @@ -1022,13 +1186,14 @@ RegionOffset MemRegion::getAsOffset() const { const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); // This is offset in bits. Offset += Layout.getFieldOffset(idx); - R = FR->getSuperRegion(); break; } } } Finish: + if (SymbolicOffsetBase) + return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic); return RegionOffset(R, Offset); } @@ -1056,26 +1221,37 @@ void BlockDataRegion::LazyInitializeReferencedVars() { typedef BumpVector<const MemRegion*> VarVec; VarVec *BV = (VarVec*) A.Allocate<VarVec>(); new (BV) VarVec(BC, E - I); + VarVec *BVOriginal = (VarVec*) A.Allocate<VarVec>(); + new (BVOriginal) VarVec(BC, E - I); for ( ; I != E; ++I) { const VarDecl *VD = *I; const VarRegion *VR = 0; + const VarRegion *OriginalVR = 0; - if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) + if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { VR = MemMgr.getVarRegion(VD, this); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } else { - if (LC) + if (LC) { VR = MemMgr.getVarRegion(VD, LC); + OriginalVR = VR; + } else { VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + OriginalVR = MemMgr.getVarRegion(VD, LC); } } assert(VR); + assert(OriginalVR); BV->push_back(VR, BC); + BVOriginal->push_back(OriginalVR, BC); } ReferencedVars = BV; + OriginalVars = BVOriginal; } BlockDataRegion::referenced_vars_iterator @@ -1085,8 +1261,14 @@ BlockDataRegion::referenced_vars_begin() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->begin()); + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(0, 0); + + BumpVector<const MemRegion*> *VecOriginal = + static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->begin(), + VecOriginal->begin()); } BlockDataRegion::referenced_vars_iterator @@ -1096,6 +1278,12 @@ BlockDataRegion::referenced_vars_end() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->end()); + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(0, 0); + + BumpVector<const MemRegion*> *VecOriginal = + static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->end(), + VecOriginal->end()); } diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp deleted file mode 100644 index 65cdcd9d9924..000000000000 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ /dev/null @@ -1,90 +0,0 @@ -//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ObjCMessage which serves as a common wrapper for ObjC -// message expressions or implicit messages for loading/storing ObjC properties. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace ento; - -QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { - QualType resultTy; - bool isLVal = false; - - if (isObjCMessage()) { - resultTy = Msg.getResultType(ctx); - } else if (const CXXConstructExpr *Ctor = - CallE.dyn_cast<const CXXConstructExpr *>()) { - resultTy = Ctor->getType(); - } else { - const CallExpr *FunctionCall = CallE.get<const CallExpr *>(); - - isLVal = FunctionCall->isLValue(); - const Expr *Callee = FunctionCall->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl()) - resultTy = FD->getResultType(); - else - resultTy = FunctionCall->getType(); - } - - if (isLVal) - resultTy = ctx.getPointerType(resultTy); - - return resultTy; -} - -SVal CallOrObjCMessage::getFunctionCallee() const { - assert(isFunctionCall()); - assert(!isCXXCall()); - const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens(); - return State->getSVal(Fun, LCtx); -} - -SVal CallOrObjCMessage::getCXXCallee() const { - assert(isCXXCall()); - const CallExpr *ActualCall = CallE.get<const CallExpr *>(); - const Expr *callee = - cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument(); - - // FIXME: Will eventually need to cope with member pointers. This is - // a limitation in getImplicitObjectArgument(). - if (!callee) - return UnknownVal(); - - return State->getSVal(callee, LCtx); -} - -SVal -CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { - assert(isObjCMessage()); - return Msg.getInstanceReceiverSVal(State, LC); -} - -const Decl *CallOrObjCMessage::getDecl() const { - if (isCXXCall()) { - const CXXMemberCallExpr *CE = - cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>()); - assert(CE); - return CE->getMethodDecl(); - } else if (isObjCMessage()) { - return Msg.getMethodDecl(); - } else if (isFunctionCall()) { - // In case of a C style call, use the path sensitive information to find - // the function declaration. - SVal CalleeVal = getFunctionCallee(); - return CalleeVal.getAsFunctionDecl(); - } - return 0; -} - diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 01dd965ac55a..7d52aac71c25 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtCXX.h" @@ -58,6 +59,48 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} PathPieces::~PathPieces() {} + +void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, + bool ShouldFlattenMacros) const { + for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { + PathDiagnosticPiece *Piece = I->getPtr(); + + switch (Piece->getKind()) { + case PathDiagnosticPiece::Call: { + PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter = + Call->getCallEnterEvent(); + if (CallEnter) + Current.push_back(CallEnter); + Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + Call->getCallExitEvent(); + if (callExit) + Current.push_back(callExit); + break; + } + case PathDiagnosticPiece::Macro: { + PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece); + if (ShouldFlattenMacros) { + Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); + } else { + Current.push_back(Piece); + PathPieces NewPath; + Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); + // FIXME: This probably shouldn't mutate the original path piece. + Macro->subPieces = NewPath; + } + break; + } + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::ControlFlow: + Current.push_back(Piece); + break; + } + } +} + + PathDiagnostic::~PathDiagnostic() {} PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, @@ -147,23 +190,14 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. + // Note, the enclosing routine is called in deterministic order, so the + // results will be consistent between runs (no reason to break ties if the + // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); - - if (orig_size <= new_size) { - bool shouldKeepOriginal = true; - if (orig_size == new_size) { - // Here we break ties in a fairly arbitrary, but deterministic, way. - llvm::FoldingSetNodeID fullProfile, fullProfileOrig; - D->FullProfile(fullProfile); - orig->FullProfile(fullProfileOrig); - if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) - shouldKeepOriginal = false; - } + if (orig_size <= new_size) + return; - if (shouldKeepOriginal) - return; - } Diags.RemoveNode(orig); delete orig; } @@ -242,30 +276,86 @@ PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { //===----------------------------------------------------------------------===// static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisDeclContext LAC) { - SourceLocation L = S->getLocStart(); + LocationOrAnalysisDeclContext LAC, + bool UseEnd = false) { + SourceLocation L = UseEnd ? S->getLocEnd() : S->getLocStart(); assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " "be passed to PathDiagnosticLocation upon creation."); // S might be a temporary statement that does not have a location in the - // source code, so find an enclosing statement and use it's location. + // source code, so find an enclosing statement and use its location. if (!L.isValid()) { - ParentMap *PM = 0; + AnalysisDeclContext *ADC; if (LAC.is<const LocationContext*>()) - PM = &LAC.get<const LocationContext*>()->getParentMap(); + ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); else - PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); + ADC = LAC.get<AnalysisDeclContext*>(); + + ParentMap &PM = ADC->getParentMap(); + + const Stmt *Parent = S; + do { + Parent = PM.getParent(Parent); + + // In rare cases, we have implicit top-level expressions, + // such as arguments for implicit member initializers. + // In this case, fall back to the start of the body (even if we were + // asked for the statement end location). + if (!Parent) { + const Stmt *Body = ADC->getBody(); + if (Body) + L = Body->getLocStart(); + else + L = ADC->getDecl()->getLocEnd(); + break; + } - while (!L.isValid()) { - S = PM->getParent(S); - L = S->getLocStart(); - } + L = UseEnd ? Parent->getLocEnd() : Parent->getLocStart(); + } while (!L.isValid()); } return L; } +static PathDiagnosticLocation +getLocationForCaller(const StackFrameContext *SFC, + const LocationContext *CallerCtx, + const SourceManager &SM) { + const CFGBlock &Block = *SFC->getCallSiteBlock(); + CFGElement Source = Block[SFC->getIndex()]; + + switch (Source.getKind()) { + case CFGElement::Invalid: + llvm_unreachable("Invalid CFGElement"); + case CFGElement::Statement: + return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(), + SM, CallerCtx); + case CFGElement::Initializer: { + const CFGInitializer &Init = cast<CFGInitializer>(Source); + return PathDiagnosticLocation(Init.getInitializer()->getInit(), + SM, CallerCtx); + } + case CFGElement::AutomaticObjectDtor: { + const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source); + return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), + SM, CallerCtx); + } + case CFGElement::BaseDtor: + case CFGElement::MemberDtor: { + const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); + if (const Stmt *CallerBody = CallerInfo->getBody()) + return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); + return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); + } + case CFGElement::TemporaryDtor: + llvm_unreachable("not yet implemented!"); + } + + llvm_unreachable("Unknown CFGElement kind"); +} + + PathDiagnosticLocation PathDiagnosticLocation::createBegin(const Decl *D, const SourceManager &SM) { @@ -280,6 +370,17 @@ PathDiagnosticLocation SM, SingleLocK); } + +PathDiagnosticLocation +PathDiagnosticLocation::createEnd(const Stmt *S, + const SourceManager &SM, + LocationOrAnalysisDeclContext LAC) { + if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) + return createEndBrace(CS, SM); + return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), + SM, SingleLocK); +} + PathDiagnosticLocation PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, const SourceManager &SM) { @@ -339,6 +440,19 @@ PathDiagnosticLocation else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { S = PS->getStmt(); } + else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) { + return PathDiagnosticLocation(PIE->getLocation(), SMng); + } + else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + return getLocationForCaller(CE->getCalleeContext(), + CE->getLocationContext(), + SMng); + } + else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) { + return getLocationForCaller(CEE->getCalleeContext(), + CEE->getLocationContext(), + SMng); + } return PathDiagnosticLocation(S, SMng, P.getLocationContext()); } @@ -495,25 +609,14 @@ PathDiagnosticLocation PathDiagnostic::getLocation() const { // Manipulation of PathDiagnosticCallPieces. //===----------------------------------------------------------------------===// -static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N, - const SourceManager &SM) { - while (N) { - ProgramPoint PP = N->getLocation(); - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) - return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext()); - if (N->pred_empty()) - break; - N = *N->pred_begin(); - } - return PathDiagnosticLocation(); -} - PathDiagnosticCallPiece * PathDiagnosticCallPiece::construct(const ExplodedNode *N, - const CallExit &CE, + const CallExitEnd &CE, const SourceManager &SM) { - const Decl *caller = CE.getLocationContext()->getParent()->getDecl(); - PathDiagnosticLocation pos = getLastStmtLoc(N, SM); + const Decl *caller = CE.getLocationContext()->getDecl(); + PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), + CE.getLocationContext(), + SM); return new PathDiagnosticCallPiece(caller, pos); } @@ -528,11 +631,11 @@ PathDiagnosticCallPiece::construct(PathPieces &path, void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, const SourceManager &SM) { - const Decl *D = CE.getCalleeContext()->getDecl(); - Callee = D; - callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM, - CE.getLocationContext()); - callEnterWithin = PathDiagnosticLocation::createBegin(D, SM); + const StackFrameContext *CalleeCtx = CE.getCalleeContext(); + Callee = CalleeCtx->getDecl(); + + callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); + callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> @@ -667,16 +770,15 @@ StackHintGenerator::~StackHintGenerator() {} std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ ProgramPoint P = N->getLocation(); - const CallExit *CExit = dyn_cast<CallExit>(&P); - assert(CExit && "Stack Hints should be constructed at CallExit points."); + const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P); + assert(CExit && "Stack Hints should be constructed at CallExitEnd points."); - const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); + // FIXME: Use CallEvent to abstract this over all calls. + const Stmt *CallSite = CExit->getCalleeContext()->getCallSite(); + const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite); if (!CE) return ""; - // Get the successor node to make sure the return statement is evaluated and - // CE is set to the result value. - N = *N->succ_begin(); if (!N) return getMessageForSymbolNotFound(); diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 323cede7786e..58a4bba9483f 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -31,7 +31,6 @@ namespace { const std::string OutputFile; const LangOptions &LangOpts; OwningPtr<PathDiagnosticConsumer> SubPD; - bool flushed; const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, @@ -61,7 +60,7 @@ PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, bool supportsMultipleFiles, PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false), + : OutputFile(output), LangOpts(LO), SubPD(subPD), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} PathDiagnosticConsumer* @@ -183,10 +182,18 @@ static void ReportControlFlow(raw_ostream &o, I!=E; ++I) { Indent(o, indent) << "<dict>\n"; ++indent; + + // Make the ranges of the start and end point self-consistent with adjacent edges + // by forcing to use only the beginning of the range. This simplifies the layout + // logic for clients. Indent(o, indent) << "<key>start</key>\n"; - EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); + SourceLocation StartEdge = I->getStart().asRange().getBegin(); + EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1); + Indent(o, indent) << "<key>end</key>\n"; - EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); + SourceLocation EndEdge = I->getEnd().asRange().getBegin(); + EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1); + --indent; Indent(o, indent) << "</dict>\n"; } @@ -382,6 +389,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl( if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { + IntrusiveRefCntPtr<PathDiagnosticEventPiece> + callEnterWithin = call->getCallEnterWithinCallerEvent(); + if (callEnterWithin) + AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); + WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = @@ -476,6 +488,17 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } + + // Output the bug hash for issue unique-ing. Currently, it's just an + // offset from the beginning of the function. + if (const Stmt *Body = DeclWithIssue->getBody()) { + FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), + *SM); + FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); + o << " <key>issue_hash</key><integer>" + << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() + << "</integer>\n"; + } } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b9cfa27808ce..dc988cc5f1fc 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" @@ -70,6 +71,19 @@ ProgramState::~ProgramState() { stateMgr->getStoreManager().decrementReferenceCount(store); } +ProgramStateManager::ProgramStateManager(ASTContext &Ctx, + StoreManagerCreator CreateSMgr, + ConstraintManagerCreator CreateCMgr, + llvm::BumpPtrAllocator &alloc, + SubEngine &SubEng) + : Eng(&SubEng), EnvMgr(alloc), GDMFactory(alloc), + svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), + CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { + StoreMgr.reset((*CreateSMgr)(*this)); + ConstraintMgr.reset((*CreateCMgr)(*this, SubEng)); +} + + ProgramStateManager::~ProgramStateManager() { for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); I!=E; ++I) @@ -157,7 +171,7 @@ ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, StoreManager::InvalidatedSymbols *IS, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!IS) { StoreManager::InvalidatedSymbols invalidated; return invalidateRegionsImpl(Regions, E, Count, LCtx, @@ -171,7 +185,7 @@ ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, StoreManager::InvalidatedSymbols &IS, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); @@ -203,11 +217,11 @@ ProgramStateRef ProgramState::unbindLoc(Loc LV) const { } ProgramStateRef -ProgramState::enterStackFrame(const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) const { - const StoreRef &new_store = - getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx); - return makeWithStore(new_store); +ProgramState::enterStackFrame(const CallEvent &Call, + const StackFrameContext *CalleeCtx) const { + const StoreRef &NewStore = + getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); + return makeWithStore(NewStore); } SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { @@ -485,8 +499,6 @@ ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) return getPersistentState(NewState); } -void ScanReachableSymbols::anchor() { } - bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) if (!scan(*I)) @@ -530,6 +542,9 @@ bool ScanReachableSymbols::scan(SVal val) { if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) return scan(X->getRegion()); + if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val)) + return scan(X->getRegion()); + if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val)) return scan(X->getLoc()); @@ -564,20 +579,30 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { return false; // If this is a subregion, also visit the parent regions. - if (const SubRegion *SR = dyn_cast<SubRegion>(R)) - if (!scan(SR->getSuperRegion())) + if (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + const MemRegion *Super = SR->getSuperRegion(); + if (!scan(Super)) return false; - // Now look at the binding to this region (if any). - if (!scan(state->getSValAsScalarOrLoc(R))) - return false; + // When we reach the topmost region, scan all symbols in it. + if (isa<MemSpaceRegion>(Super)) { + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this)) + return false; + } + } - // Now look at the subregions. - if (!SRM.get()) - SRM.reset(state->getStateManager().getStoreManager(). - getSubRegionMap(state->getStore())); + // Regions captured by a block are also implicitly reachable. + if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + for ( ; I != E; ++I) { + if (!scan(I.getCapturedRegion())) + return false; + } + } - return SRM->iterSubRegions(R, *this); + return true; } bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { @@ -707,3 +732,39 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { return Tainted; } + +/// The GDM component containing the dynamic type info. This is a map from a +/// symbol to it's most likely type. +namespace clang { +namespace ento { +typedef llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo> DynamicTypeMap; +template<> struct ProgramStateTrait<DynamicTypeMap> + : public ProgramStatePartialTrait<DynamicTypeMap> { + static void *GDMIndex() { static int index; return &index; } +}; +}} + +DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { + // Look up the dynamic type in the GDM. + const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg); + if (GDMType) + return *GDMType; + + // Otherwise, fall back to what we know about the region. + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg)) + return DynamicTypeInfo(TR->getValueType()); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType(getStateManager().getContext())); + } + + return DynamicTypeInfo(); +} + +ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg, + DynamicTypeInfo NewTy) const { + ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy); + assert(NewState); + return NewState; +} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 98eb958e1e82..550404a51075 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/Debug.h" @@ -143,6 +144,92 @@ private: } } + const llvm::APSInt &getMinValue() const { + assert(!isEmpty()); + return ranges.begin()->From(); + } + + bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const { + // This function has nine cases, the cartesian product of range-testing + // both the upper and lower bounds against the symbol's type. + // Each case requires a different pinning operation. + // The function returns false if the described range is entirely outside + // the range of values for the associated symbol. + APSIntType Type(getMinValue()); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper); + + switch (LowerTest) { + case APSIntType::RTR_Below: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower < Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range starts below what's possible but ends within it. Pin. + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range spans all possible values for the symbol. Pin. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Within: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps around, but all lower values are not possible. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range may or may not wrap around, but both limits are valid. + Type.apply(Lower); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range starts within what's possible but ends above it. Pin. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Above: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps but is outside the symbol's set of possible values. + return false; + case APSIntType::RTR_Within: + // The range starts above what's possible but ends within it (wrap). + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower < Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + } + + return true; + } + public: // Returns a set containing the values in the receiving set, intersected with // the closed range [Lower, Upper]. Unlike the Range type, this range uses @@ -152,8 +239,10 @@ public: // intersection with the two ranges [Min, Upper] and [Lower, Max], // or, alternatively, /removing/ all integers between Upper and Lower. RangeSet Intersect(BasicValueFactory &BV, Factory &F, - const llvm::APSInt &Lower, - const llvm::APSInt &Upper) const { + llvm::APSInt Lower, llvm::APSInt Upper) const { + if (!pin(Lower, Upper)) + return F.getEmptySet(); + PrimRangeSet newRanges = F.getEmptySet(); PrimRangeSet::iterator i = begin(), e = end(); @@ -166,6 +255,7 @@ public: IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e); IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e); } + return newRanges; } @@ -206,8 +296,8 @@ namespace { class RangeConstraintManager : public SimpleConstraintManager{ RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: - RangeConstraintManager(SubEngine &subengine) - : SimpleConstraintManager(subengine) {} + RangeConstraintManager(SubEngine &subengine, BasicValueFactory &BVF) + : SimpleConstraintManager(subengine, BVF) {} ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, @@ -252,9 +342,9 @@ private: } // end anonymous namespace -ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&, - SubEngine &subeng) { - return new RangeConstraintManager(subeng); +ConstraintManager * +ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine &Eng) { + return new RangeConstraintManager(Eng, StMgr.getBasicVals()); } const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, @@ -288,8 +378,8 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { // Lazily generate a new RangeSet representing all possible values for the // given symbol type. - QualType T = state->getSymbolManager().getType(sym); - BasicValueFactory& BV = state->getBasicVals(); + BasicValueFactory &BV = getBasicVals(); + QualType T = sym->getType(BV.getContext()); return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); } @@ -306,117 +396,154 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { // UINT_MAX, 0, 1, and 2. ProgramStateRef -RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - llvm::APSInt Lower = Int-Adjustment; +RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + return St; + + llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; llvm::APSInt Upper = Lower; --Lower; ++Upper; // [Int-Adjustment+1, Int-Adjustment-1] // Notice that the lower bound is greater than the upper bound. - RangeSet New = GetRange(state, sym).Intersect(BV, F, Upper, Lower); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { +RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + return NULL; + // [Int-Adjustment, Int-Adjustment] - BasicValueFactory &BV = state->getBasicVals(); - llvm::APSInt AdjInt = Int-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, AdjInt, AdjInt); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Min = BV.getMinValue(T); +RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return NULL; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return St; + } // Special case for Int == Min. This is always false. - if (Int == Min) + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) return NULL; llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = Int-Adjustment; + llvm::APSInt Upper = ComparisonVal-Adjustment; --Upper; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Max = BV.getMaxValue(T); +RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return St; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return NULL; + } // Special case for Int == Max. This is always false. - if (Int == Max) + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) return NULL; - llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Lower = ComparisonVal-Adjustment; llvm::APSInt Upper = Max-Adjustment; ++Lower; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Min = BV.getMinValue(T); +RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return St; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return NULL; + } // Special case for Int == Min. This is always feasible. - if (Int == Min) - return state; - - const llvm::APSInt &Max = BV.getMaxValue(T); + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) + return St; - llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Max = AdjustmentType.getMaxValue(); + llvm::APSInt Lower = ComparisonVal-Adjustment; llvm::APSInt Upper = Max-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Max = BV.getMaxValue(T); +RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return NULL; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return St; + } // Special case for Int == Max. This is always feasible. - if (Int == Max) - return state; - - const llvm::APSInt &Min = BV.getMinValue(T); + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) + return St; + llvm::APSInt Min = AdjustmentType.getMinValue(); llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = Int-Adjustment; + llvm::APSInt Upper = ComparisonVal-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } //===------------------------------------------------------------------------=== diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index cc3ea8c3bf54..bc4e4bbf602b 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -17,10 +17,11 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/CXXInheritance.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -40,23 +41,49 @@ using llvm::Optional; namespace { class BindingKey { public: - enum Kind { Direct = 0x0, Default = 0x1 }; + enum Kind { Default = 0x0, Direct = 0x1 }; private: - llvm ::PointerIntPair<const MemRegion*, 1> P; - uint64_t Offset; + enum { Symbolic = 0x2 }; + llvm::PointerIntPair<const MemRegion *, 2> P; + uint64_t Data; + + explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k) + : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) { + assert(r && Base && "Must have known regions."); + assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); + } explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) - : P(r, (unsigned) k), Offset(offset) {} + : P(r, k), Data(offset) { + assert(r && "Must have known regions."); + assert(getOffset() == offset && "Failed to store offset"); + assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base"); + } public: - bool isDirect() const { return P.getInt() == Direct; } + bool isDirect() const { return P.getInt() & Direct; } + bool hasSymbolicOffset() const { return P.getInt() & Symbolic; } const MemRegion *getRegion() const { return P.getPointer(); } - uint64_t getOffset() const { return Offset; } + uint64_t getOffset() const { + assert(!hasSymbolicOffset()); + return Data; + } + + const MemRegion *getConcreteOffsetRegion() const { + assert(hasSymbolicOffset()); + return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data)); + } + + const MemRegion *getBaseRegion() const { + if (hasSymbolicOffset()) + return getConcreteOffsetRegion()->getBaseRegion(); + return getRegion()->getBaseRegion(); + } void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddPointer(P.getOpaqueValue()); - ID.AddInteger(Offset); + ID.AddInteger(Data); } static BindingKey Make(const MemRegion *R, Kind k); @@ -66,48 +93,48 @@ public: return true; if (P.getOpaqueValue() > X.P.getOpaqueValue()) return false; - return Offset < X.Offset; + return Data < X.Data; } bool operator==(const BindingKey &X) const { return P.getOpaqueValue() == X.P.getOpaqueValue() && - Offset == X.Offset; + Data == X.Data; } - bool isValid() const { - return getRegion() != NULL; - } + LLVM_ATTRIBUTE_USED void dump() const; }; } // end anonymous namespace BindingKey BindingKey::Make(const MemRegion *R, Kind k) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const RegionRawOffset &O = ER->getAsArrayOffset(); - - // FIXME: There are some ElementRegions for which we cannot compute - // raw offsets yet, including regions with symbolic offsets. These will be - // ignored by the store. - return BindingKey(O.getRegion(), O.getOffset().getQuantity(), k); - } + const RegionOffset &RO = R->getAsOffset(); + if (RO.hasSymbolicOffset()) + return BindingKey(R, RO.getRegion(), k); - return BindingKey(R, 0, k); + return BindingKey(RO.getRegion(), RO.getOffset(), k); } namespace llvm { static inline raw_ostream &operator<<(raw_ostream &os, BindingKey K) { - os << '(' << K.getRegion() << ',' << K.getOffset() - << ',' << (K.isDirect() ? "direct" : "default") + os << '(' << K.getRegion(); + if (!K.hasSymbolicOffset()) + os << ',' << K.getOffset(); + os << ',' << (K.isDirect() ? "direct" : "default") << ')'; return os; } } // end llvm namespace +void BindingKey::dump() const { + llvm::errs() << *this; +} + //===----------------------------------------------------------------------===// // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap<BindingKey, SVal> RegionBindings; +typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; +typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings; //===----------------------------------------------------------------------===// // Fine-grained control of RegionStoreManager. @@ -138,75 +165,15 @@ public: namespace { -class RegionStoreSubRegionMap : public SubRegionMap { -public: - typedef llvm::ImmutableSet<const MemRegion*> Set; - typedef llvm::DenseMap<const MemRegion*, Set> Map; -private: - Set::Factory F; - Map M; -public: - bool add(const MemRegion* Parent, const MemRegion* SubRegion) { - Map::iterator I = M.find(Parent); - - if (I == M.end()) { - M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion))); - return true; - } - - I->second = F.add(I->second, SubRegion); - return false; - } - - void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R); - - ~RegionStoreSubRegionMap() {} - - const Set *getSubRegions(const MemRegion *Parent) const { - Map::const_iterator I = M.find(Parent); - return I == M.end() ? NULL : &I->second; - } - - bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { - Map::const_iterator I = M.find(Parent); - - if (I == M.end()) - return true; - - Set S = I->second; - for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) { - if (!V.Visit(Parent, *SI)) - return false; - } - - return true; - } -}; - -void -RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL, - const SubRegion *R) { - const MemRegion *superR = R->getSuperRegion(); - if (add(superR, R)) - if (const SubRegion *sr = dyn_cast<SubRegion>(superR)) - WL.push_back(sr); -} - class RegionStoreManager : public StoreManager { const RegionStoreFeatures Features; RegionBindings::Factory RBFactory; + ClusterBindings::Factory CBFactory; public: RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) - : StoreManager(mgr), - Features(f), - RBFactory(mgr.getAllocator()) {} - - SubRegionMap *getSubRegionMap(Store store) { - return getRegionStoreSubRegionMap(store); - } - - RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); + : StoreManager(mgr), Features(f), + RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {} Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); /// getDefaultBinding - Returns an SVal* representing an optional default @@ -257,13 +224,15 @@ public: const Expr *E, unsigned Count, const LocationContext *LCtx, InvalidatedSymbols &IS, - const CallOrObjCMessage *Call, + const CallEvent *Call, InvalidatedRegions *Invalidated); + bool scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks); + public: // Made public for helper classes. - void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, - RegionStoreSubRegionMap &M); + RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R); RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V); @@ -282,6 +251,8 @@ public: // Made public for helper classes. BindingKey::Default); } + RegionBindings removeCluster(RegionBindings B, const MemRegion *R); + public: // Part of public interface to class. StoreRef Bind(Store store, Loc LV, SVal V); @@ -307,10 +278,14 @@ public: // Part of public interface to class. /// BindStruct - Bind a compound value to a structure. StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V); + /// BindVector - Bind a compound value to a vector. + StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V); + StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V); - /// KillStruct - Set the entire struct to unknown. - StoreRef KillStruct(Store store, const TypedRegion* R, SVal DefaultVal); + /// Clears out all bindings in the given region and assigns a new value + /// as a Default binding. + StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal); StoreRef Remove(Store store, Loc LV); @@ -377,10 +352,8 @@ public: // Part of public interface to class. /// Get the state and region whose binding this region R corresponds to. std::pair<Store, const MemRegion*> GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion); - - StoreRef CopyLazyBindings(nonloc::LazyCompoundVal V, Store store, - const TypedRegion *R); + const MemRegion *originalRegion, + bool includeSuffix = false); //===------------------------------------------------------------------===// // State pruning. @@ -390,11 +363,7 @@ public: // Part of public interface to class. /// It returns a new Store with these values removed. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper); - - StoreRef enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx); - + //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// @@ -416,14 +385,18 @@ public: // Part of public interface to class. void iterBindings(Store store, BindingsHandler& f) { RegionBindings B = GetRegionBindings(store); - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - const BindingKey &K = I.getKey(); - if (!K.isDirect()) - continue; - if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) { - // FIXME: Possibly incorporate the offset? - if (!f.HandleBinding(*this, store, R, I.getData())) - return; + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const BindingKey &K = CI.getKey(); + if (!K.isDirect()) + continue; + if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) { + // FIXME: Possibly incorporate the offset? + if (!f.HandleBinding(*this, store, R, CI.getData())) + return; + } } } } @@ -448,28 +421,6 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { } -RegionStoreSubRegionMap* -RegionStoreManager::getRegionStoreSubRegionMap(Store store) { - RegionBindings B = GetRegionBindings(store); - RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); - - SmallVector<const SubRegion*, 10> WL; - - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) - if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) - M->process(WL, R); - - // We also need to record in the subregion map "intermediate" regions that - // don't have direct bindings but are super regions of those that do. - while (!WL.empty()) { - const SubRegion *R = WL.back(); - WL.pop_back(); - M->process(WL, R); - } - - return M; -} - //===----------------------------------------------------------------------===// // Region Cluster analysis. //===----------------------------------------------------------------------===// @@ -478,14 +429,11 @@ namespace { template <typename DERIVED> class ClusterAnalysis { protected: - typedef BumpVector<BindingKey> RegionCluster; - typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap; - llvm::DenseMap<const RegionCluster*, unsigned> Visited; - typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10> - WorkList; - - BumpVectorContext BVC; - ClusterMap ClusterM; + typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap; + typedef SmallVector<const MemRegion *, 10> WorkList; + + llvm::SmallPtrSet<const ClusterBindings *, 16> Visited; + WorkList WL; RegionStoreManager &RM; @@ -496,6 +444,10 @@ protected: const bool includeGlobals; + const ClusterBindings *getCluster(const MemRegion *R) { + return B.lookup(R); + } + public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, RegionBindings b, const bool includeGlobals) @@ -505,59 +457,36 @@ public: RegionBindings getRegionBindings() const { return B; } - RegionCluster &AddToCluster(BindingKey K) { - const MemRegion *R = K.getRegion(); - const MemRegion *baseR = R->getBaseRegion(); - RegionCluster &C = getCluster(baseR); - C.push_back(K, BVC); - static_cast<DERIVED*>(this)->VisitAddedToCluster(baseR, C); - return C; - } - bool isVisited(const MemRegion *R) { - return (bool) Visited[&getCluster(R->getBaseRegion())]; - } - - RegionCluster& getCluster(const MemRegion *R) { - RegionCluster *&CRef = ClusterM[R]; - if (!CRef) { - void *Mem = BVC.getAllocator().template Allocate<RegionCluster>(); - CRef = new (Mem) RegionCluster(BVC, 10); - } - return *CRef; + return Visited.count(getCluster(R)); } void GenerateClusters() { - // Scan the entire set of bindings and make the region clusters. + // Scan the entire set of bindings and record the region clusters. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - RegionCluster &C = AddToCluster(RI.getKey()); - if (const MemRegion *R = RI.getData().getAsRegion()) { - // Generate a cluster, but don't add the region to the cluster - // if there aren't any bindings. - getCluster(R->getBaseRegion()); - } - if (includeGlobals) { - const MemRegion *R = RI.getKey().getRegion(); - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - AddToWorkList(R, C); - } + const MemRegion *Base = RI.getKey(); + + const ClusterBindings &Cluster = RI.getData(); + assert(!Cluster.isEmpty() && "Empty clusters should be removed"); + static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster); + + if (includeGlobals) + if (isa<NonStaticGlobalSpaceRegion>(Base->getMemorySpace())) + AddToWorkList(Base, &Cluster); } } - bool AddToWorkList(const MemRegion *R, RegionCluster &C) { - if (unsigned &visited = Visited[&C]) - return false; - else - visited = 1; + bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) { + if (C) { + if (Visited.count(C)) + return false; + Visited.insert(C); + } - WL.push_back(std::make_pair(R, &C)); + WL.push_back(R); return true; } - bool AddToWorkList(BindingKey K) { - return AddToWorkList(K.getRegion()); - } - bool AddToWorkList(const MemRegion *R) { const MemRegion *baseR = R->getBaseRegion(); return AddToWorkList(baseR, getCluster(baseR)); @@ -565,22 +494,20 @@ public: void RunWorkList() { while (!WL.empty()) { - const MemRegion *baseR; - RegionCluster *C; - llvm::tie(baseR, C) = WL.back(); - WL.pop_back(); + const MemRegion *baseR = WL.pop_back_val(); - // First visit the cluster. - static_cast<DERIVED*>(this)->VisitCluster(baseR, C->begin(), C->end()); + // First visit the cluster. + if (const ClusterBindings *Cluster = getCluster(baseR)) + static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster); - // Next, visit the base region. + // Next, visit the base region. static_cast<DERIVED*>(this)->VisitBaseRegion(baseR); } } public: - void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C) {} - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E) {} + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {} + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {} void VisitBaseRegion(const MemRegion *baseR) {} }; } @@ -589,16 +516,99 @@ public: // Binding invalidation. //===----------------------------------------------------------------------===// -void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, - const MemRegion *R, - RegionStoreSubRegionMap &M) { +bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks) { + assert(R == R->getBaseRegion() && "Should only be called for base regions"); + RegionBindings B = GetRegionBindings(S); + const ClusterBindings *Cluster = B.lookup(R); + + if (!Cluster) + return true; - if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R)) - for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end(); - I != E; ++I) - RemoveSubRegionBindings(B, *I, M); + for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end(); + RI != RE; ++RI) { + if (!Callbacks.scan(RI.getData())) + return false; + } - B = removeBinding(B, R); + return true; +} + +RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B, + const SubRegion *R) { + BindingKey SRKey = BindingKey::Make(R, BindingKey::Default); + const MemRegion *ClusterHead = SRKey.getBaseRegion(); + if (R == ClusterHead) { + // We can remove an entire cluster's bindings all in one go. + return RBFactory.remove(B, R); + } + + if (SRKey.hasSymbolicOffset()) { + const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion()); + B = removeSubRegionBindings(B, Base); + return addBinding(B, Base, BindingKey::Default, UnknownVal()); + } + + // This assumes the region being invalidated is char-aligned. This isn't + // true for bitfields, but since bitfields have no subregions they shouldn't + // be using this function anyway. + uint64_t Length = UINT64_MAX; + + SVal Extent = R->getExtent(svalBuilder); + if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) { + const llvm::APSInt &ExtentInt = ExtentCI->getValue(); + assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); + // Extents are in bytes but region offsets are in bits. Be careful! + Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth(); + } + + const ClusterBindings *Cluster = B.lookup(ClusterHead); + if (!Cluster) + return B; + + ClusterBindings Result = *Cluster; + + // It is safe to iterate over the bindings as they are being changed + // because they are in an ImmutableMap. + for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end(); + I != E; ++I) { + BindingKey NextKey = I.getKey(); + if (NextKey.getRegion() == SRKey.getRegion()) { + if (NextKey.getOffset() > SRKey.getOffset() && + NextKey.getOffset() - SRKey.getOffset() < Length) { + // Case 1: The next binding is inside the region we're invalidating. + // Remove it. + Result = CBFactory.remove(Result, NextKey); + } else if (NextKey.getOffset() == SRKey.getOffset()) { + // Case 2: The next binding is at the same offset as the region we're + // invalidating. In this case, we need to leave default bindings alone, + // since they may be providing a default value for a regions beyond what + // we're invalidating. + // FIXME: This is probably incorrect; consider invalidating an outer + // struct whose first field is bound to a LazyCompoundVal. + if (NextKey.isDirect()) + Result = CBFactory.remove(Result, NextKey); + } + } else if (NextKey.hasSymbolicOffset()) { + const MemRegion *Base = NextKey.getConcreteOffsetRegion(); + if (R->isSubRegionOf(Base)) { + // Case 3: The next key is symbolic and we just changed something within + // its concrete region. We don't know if the binding is still valid, so + // we'll be conservative and remove it. + if (NextKey.isDirect()) + Result = CBFactory.remove(Result, NextKey); + } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) { + // Case 4: The next key is symbolic, but we changed a known + // super-region. In this case the binding is certainly no longer valid. + if (R == Base || BaseSR->isSubRegionOf(R)) + Result = CBFactory.remove(Result, NextKey); + } + } + } + + if (Result.isEmpty()) + return RBFactory.remove(B, ClusterHead); + return RBFactory.add(B, ClusterHead, Result); } namespace { @@ -621,7 +631,7 @@ public: : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); void VisitBaseRegion(const MemRegion *baseR); private: @@ -646,26 +656,31 @@ void invalidateRegionsWorker::VisitBinding(SVal V) { const MemRegion *LazyR = LCS->getRegion(); RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + // FIXME: This should not have to walk all bindings in the old store. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion()); - if (baseR && baseR->isSubRegionOf(LazyR)) - VisitBinding(RI.getData()); + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + BindingKey K = CI.getKey(); + if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { + if (BaseR == LazyR) + VisitBinding(CI.getData()); + else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) + VisitBinding(CI.getData()); + } + } } return; } } -void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, - BindingKey *I, BindingKey *E) { - for ( ; I != E; ++I) { - // Get the old binding. Is it a region? If so, add it to the worklist. - const BindingKey &K = *I; - if (const SVal *V = RM.lookup(B, K)) - VisitBinding(*V); +void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR, + const ClusterBindings &C) { + for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) + VisitBinding(I.getData()); - B = RM.removeBinding(B, K); - } + B = RM.removeCluster(B, BaseR); } void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { @@ -681,8 +696,22 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { BI != BE; ++BI) { const VarRegion *VR = *BI; const VarDecl *VD = VR->getDecl(); - if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) + if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) { AddToWorkList(VR); + } + else if (Loc::isLocType(VR->getValueType())) { + // Map the current bindings to a Store to retrieve the value + // of the binding. If that binding itself is a region, we should + // invalidate that region. This is because a block may capture + // a pointer value, but the thing pointed by that pointer may + // get invalidated. + Store store = B.getRootWithoutRetain(); + SVal V = RM.getBinding(store, loc::MemRegionVal(VR)); + if (const Loc *L = dyn_cast<Loc>(&V)) { + if (const MemRegion *LR = L->getAsRegion()) + AddToWorkList(LR); + } + } } return; } @@ -771,7 +800,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, const Expr *Ex, unsigned Count, const LocationContext *LCtx, InvalidatedSymbols &IS, - const CallOrObjCMessage *Call, + const CallEvent *Call, InvalidatedRegions *Invalidated) { invalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), @@ -868,10 +897,22 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx)); } +// This mirrors Type::getCXXRecordDeclForPointerType(), but there doesn't +// appear to be another need for this in the rest of the codebase. +static const CXXRecordDecl *GetCXXRecordDeclForReferenceType(QualType Ty) { + if (const ReferenceType *RT = Ty->getAs<ReferenceType>()) + if (const RecordType *RCT = RT->getPointeeType()->getAs<RecordType>()) + return dyn_cast<CXXRecordDecl>(RCT->getDecl()); + return 0; +} + SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) { const CXXRecordDecl *baseDecl; + if (baseType->isPointerType()) baseDecl = baseType->getCXXRecordDeclForPointerType(); + else if (baseType->isReferenceType()) + baseDecl = GetCXXRecordDeclForReferenceType(baseType); else baseDecl = baseType->getAsCXXRecordDecl(); @@ -894,7 +935,7 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base); if (!baseRegVal) return UnknownVal(); - const MemRegion *BaseRegion = baseRegVal->stripCasts(); + const MemRegion *BaseRegion = baseRegVal->stripCasts(/*StripBases=*/false); // Assume the derived class is a pointer or a reference to a CXX record. derivedType = derivedType->getPointeeType(); @@ -917,23 +958,20 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, if (SRDecl == DerivedDecl) return loc::MemRegionVal(TSR); - // If the region type is a subclass of the derived type. - if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) { - // This occurs in two cases. - // 1) We are processing an upcast. - // 2) We are processing a downcast but we jumped directly from the - // ancestor to a child of the cast value, so conjure the - // appropriate region to represent value (the intermediate node). - return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl, - BaseRegion)); - } - - // If super region is not a parent of derived class, the cast definitely - // fails. - if (!derivedType->isVoidType() && - DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) { - Failed = true; - return UnknownVal(); + if (!derivedType->isVoidType()) { + // Static upcasts are marked as DerivedToBase casts by Sema, so this will + // only happen when multiple or virtual inheritance is involved. + CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (SRDecl->isDerivedFrom(DerivedDecl, Paths)) { + SVal Result = loc::MemRegionVal(TSR); + const CXXBasePath &Path = *Paths.begin(); + for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end(); + I != E; ++I) { + Result = evalDerivedToBase(Result, I->Base->getType()); + } + return Result; + } } if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR)) @@ -1036,8 +1074,12 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { if (RTy->isUnionType()) return UnknownVal(); - if (RTy->isArrayType()) - return getBindingForArray(store, R); + if (RTy->isArrayType()) { + if (RTy->isConstantArrayType()) + return getBindingForArray(store, R); + else + return UnknownVal(); + } // FIXME: handle Vector types. if (RTy->isVectorType()) @@ -1099,7 +1141,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { std::pair<Store, const MemRegion *> RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion) { + const MemRegion *originalRegion, + bool includeSuffix) { if (originalRegion != R) { if (Optional<SVal> OV = getDefaultBinding(B, R)) { @@ -1121,9 +1164,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, const std::pair<Store, const MemRegion *> &X = GetLazyBinding(B, FR->getSuperRegion(), originalRegion); - if (X.second) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); + if (X.second) { + if (includeSuffix) + return std::make_pair(X.first, + MRMgr.getFieldRegionWithSuper(FR, X.second)); + return X; + } + } // C++ base object region is another kind of region that we should blast // through to look for lazy compound value. It is like a field region. @@ -1132,9 +1179,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, const std::pair<Store, const MemRegion *> &X = GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion); - if (X.second) - return std::make_pair(X.first, - MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, X.second)); + if (X.second) { + if (includeSuffix) + return std::make_pair(X.first, + MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, + X.second)); + return X; + } } // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is @@ -1143,7 +1194,11 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, } SVal RegionStoreManager::getBindingForElement(Store store, - const ElementRegion* R) { + const ElementRegion* R) { + // We do not currently model bindings of the CompoundLiteralregion. + if (isa<CompoundLiteralRegion>(R->getBaseRegion())) + return UnknownVal(); + // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); if (const Optional<SVal> &V = getDirectBinding(B, R)) @@ -1274,7 +1329,16 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // At this point we have already checked in either getBindingForElement or // getBindingForField if 'R' has a direct binding. RegionBindings B = GetRegionBindings(store); + + // Lazy binding? + Store lazyBindingStore = NULL; + const MemRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R, + true); + if (lazyBindingRegion) + return getLazyBinding(lazyBindingRegion, lazyBindingStore); + // Record whether or not we see a symbolic index. That can completely // be out of scope of our lookup. bool hasSymbolicIndex = false; @@ -1299,14 +1363,6 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, break; } - // Lazy binding? - Store lazyBindingStore = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R); - - if (lazyBindingRegion) - return getLazyBinding(lazyBindingRegion, lazyBindingStore); - if (R->hasStackNonParametersStorage()) { if (isa<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check @@ -1410,15 +1466,49 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { return svalBuilder.getRegionValueSymbolVal(R); } +static bool mayHaveLazyBinding(QualType Ty) { + return Ty->isArrayType() || Ty->isStructureOrClassType(); +} + SVal RegionStoreManager::getBindingForStruct(Store store, const TypedValueRegion* R) { - assert(R->getValueType()->isStructureOrClassType()); + const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl(); + if (RD->field_empty()) + return UnknownVal(); + + // If we already have a lazy binding, don't create a new one, + // unless the first field might have a lazy binding of its own. + // (Right now we can't tell the difference.) + QualType FirstFieldType = RD->field_begin()->getType(); + if (!mayHaveLazyBinding(FirstFieldType)) { + RegionBindings B = GetRegionBindings(store); + BindingKey K = BindingKey::Make(R, BindingKey::Default); + if (const nonloc::LazyCompoundVal *V = + dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { + return *V; + } + } + return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } -SVal RegionStoreManager::getBindingForArray(Store store, +SVal RegionStoreManager::getBindingForArray(Store store, const TypedValueRegion * R) { - assert(Ctx.getAsConstantArrayType(R->getValueType())); + const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType()); + assert(Ty && "Only constant array types can have compound bindings."); + + // If we already have a lazy binding, don't create a new one, + // unless the first element might have a lazy binding of its own. + // (Right now we can't tell the difference.) + if (!mayHaveLazyBinding(Ty->getElementType())) { + RegionBindings B = GetRegionBindings(store); + BindingKey K = BindingKey::Make(R, BindingKey::Default); + if (const nonloc::LazyCompoundVal *V = + dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { + return *V; + } + } + return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } @@ -1426,16 +1516,23 @@ bool RegionStoreManager::includedInBindings(Store store, const MemRegion *region) const { RegionBindings B = GetRegionBindings(store); region = region->getBaseRegion(); - - for (RegionBindings::iterator it = B.begin(), ei = B.end(); it != ei; ++it) { - const BindingKey &K = it.getKey(); - if (region == K.getRegion()) - return true; - const SVal &D = it.getData(); - if (const MemRegion *r = D.getAsRegion()) - if (r == region) - return true; + + // Quick path: if the base is the head of a cluster, the region is live. + if (B.lookup(region)) + return true; + + // Slow path: if the region is the VALUE of any binding, it is live. + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const SVal &D = CI.getData(); + if (const MemRegion *R = D.getAsRegion()) + if (R->getBaseRegion() == region) + return true; + } } + return false; } @@ -1461,24 +1558,15 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); // Check if the region is a struct region. - if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) - if (TR->getValueType()->isStructureOrClassType()) + if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) { + QualType Ty = TR->getValueType(); + if (Ty->isStructureOrClassType()) return BindStruct(store, TR, V); - - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - if (ER->getIndex().isZeroConstant()) { - if (const TypedValueRegion *superR = - dyn_cast<TypedValueRegion>(ER->getSuperRegion())) { - QualType superTy = superR->getValueType(); - // For now, just invalidate the fields of the struct/union/class. - // This is for test rdar_test_7185607 in misc-ps-region-store.m. - // FIXME: Precisely handle the fields of the record. - if (superTy->isStructureOrClassType()) - return KillStruct(store, superR, UnknownVal()); - } - } + if (Ty->isVectorType()) + return BindVector(store, TR, V); } - else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { // Binding directly to a symbolic region should be treated as binding // to element 0. QualType T = SR->getSymbol()->getType(Ctx); @@ -1492,10 +1580,13 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { R = GetElementZeroRegion(SR, T); } + // Clear out bindings that may overlap with this binding. + // Perform the binding. RegionBindings B = GetRegionBindings(store); - return StoreRef(addBinding(B, R, BindingKey::Direct, - V).getRootWithoutRetain(), *this); + B = removeSubRegionBindings(B, cast<SubRegion>(R)); + BindingKey Key = BindingKey::Make(R, BindingKey::Direct); + return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this); } StoreRef RegionStoreManager::BindDecl(Store store, const VarRegion *VR, @@ -1566,12 +1657,12 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, nonloc::LazyCompoundVal LCV = cast<nonloc::LazyCompoundVal>(svalBuilder. makeLazyCompoundVal(StoreRef(store, *this), S)); - return CopyLazyBindings(LCV, store, R); + return BindAggregate(store, R, LCV); } // Handle lazy compound values. - if (nonloc::LazyCompoundVal *LCV = dyn_cast<nonloc::LazyCompoundVal>(&Init)) - return CopyLazyBindings(*LCV, store, R); + if (isa<nonloc::LazyCompoundVal>(Init)) + return BindAggregate(store, R, Init); // Remaining case: explicit compound values. @@ -1607,6 +1698,46 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, return newStore; } +StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, + SVal V) { + QualType T = R->getValueType(); + assert(T->isVectorType()); + const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. + + // Handle lazy compound values and symbolic values. + if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) + return BindAggregate(store, R, V); + + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding symbolic struct value. Kill the field values, and if + // the value is symbolic go and bind it as a "default" binding. + if (!isa<nonloc::CompoundVal>(V)) { + return BindAggregate(store, R, UnknownVal()); + } + + QualType ElemType = VT->getElementType(); + nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + unsigned index = 0, numElements = VT->getNumElements(); + StoreRef newStore(store, *this); + + for ( ; index != numElements ; ++index) { + if (VI == VE) + break; + + NonLoc Idx = svalBuilder.makeArrayIndex(index); + const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); + + if (ElemType->isArrayType()) + newStore = BindArray(newStore.getStore(), ER, *VI); + else if (ElemType->isStructureOrClassType()) + newStore = BindStruct(newStore.getStore(), ER, *VI); + else + newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + } + return newStore; +} + StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, SVal V) { @@ -1622,17 +1753,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, if (!RD->isCompleteDefinition()) return StoreRef(store, *this); - // Handle lazy compound values. - if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V)) - return CopyLazyBindings(*LCV, store, R); + // Handle lazy compound values and symbolic values. + if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) + return BindAggregate(store, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) { - SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal(); - return KillStruct(store, R, SV); - } + if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) + return BindAggregate(store, R, UnknownVal()); nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); @@ -1646,10 +1775,10 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, break; // Skip any unnamed bitfields to stay in sync with the initializers. - if ((*FI)->isUnnamedBitfield()) + if (FI->isUnnamedBitfield()) continue; - QualType FTy = (*FI)->getType(); + QualType FTy = FI->getType(); const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); if (FTy->isArrayType()) @@ -1671,58 +1800,16 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, return newStore; } -StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R, - SVal DefaultVal) { - BindingKey key = BindingKey::Make(R, BindingKey::Default); - - // The BindingKey may be "invalid" if we cannot handle the region binding - // explicitly. One example is something like array[index], where index - // is a symbolic value. In such cases, we want to invalidate the entire - // array, as the index assignment could have been to any element. In - // the case of nested symbolic indices, we need to march up the region - // hierarchy untile we reach a region whose binding we can reason about. - const SubRegion *subReg = R; - - while (!key.isValid()) { - if (const SubRegion *tmp = dyn_cast<SubRegion>(subReg->getSuperRegion())) { - subReg = tmp; - key = BindingKey::Make(tmp, BindingKey::Default); - } - else - break; - } - - // Remove the old bindings, using 'subReg' as the root of all regions - // we will invalidate. +StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R, + SVal Val) { + // Remove the old bindings, using 'R' as the root of all regions + // we will invalidate. Then add the new binding. RegionBindings B = GetRegionBindings(store); - OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - RemoveSubRegionBindings(B, subReg, *SubRegions); - // Set the default value of the struct region to "unknown". - if (!key.isValid()) - return StoreRef(B.getRootWithoutRetain(), *this); - - return StoreRef(addBinding(B, key, DefaultVal).getRootWithoutRetain(), *this); -} - -StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, - Store store, - const TypedRegion *R) { - - // Nuke the old bindings stemming from R. - RegionBindings B = GetRegionBindings(store); + B = removeSubRegionBindings(B, R); + B = addBinding(B, R, BindingKey::Default, Val); - OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - - // B and DVM are updated after the call to RemoveSubRegionBindings. - RemoveSubRegionBindings(B, R, *SubRegions.get()); - - // Now copy the bindings. This amounts to just binding 'V' to 'R'. This - // results in a zero-copy algorithm. - return StoreRef(addBinding(B, R, BindingKey::Default, - V).getRootWithoutRetain(), *this); + return StoreRef(B.getRootWithoutRetain(), *this); } //===----------------------------------------------------------------------===// @@ -1732,9 +1819,14 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K, SVal V) { - if (!K.isValid()) - return B; - return RBFactory.add(B, K, V); + const MemRegion *Base = K.getBaseRegion(); + + const ClusterBindings *ExistingCluster = B.lookup(Base); + ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster + : CBFactory.getEmptyMap()); + + ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); + return RBFactory.add(B, Base, NewCluster); } RegionBindings RegionStoreManager::addBinding(RegionBindings B, @@ -1744,9 +1836,11 @@ RegionBindings RegionStoreManager::addBinding(RegionBindings B, } const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) { - if (!K.isValid()) - return NULL; - return B.lookup(K); + const ClusterBindings *Cluster = B.lookup(K.getBaseRegion()); + if (!Cluster) + return 0; + + return Cluster->lookup(K); } const SVal *RegionStoreManager::lookup(RegionBindings B, @@ -1757,9 +1851,15 @@ const SVal *RegionStoreManager::lookup(RegionBindings B, RegionBindings RegionStoreManager::removeBinding(RegionBindings B, BindingKey K) { - if (!K.isValid()) + const MemRegion *Base = K.getBaseRegion(); + const ClusterBindings *Cluster = B.lookup(Base); + if (!Cluster) return B; - return RBFactory.remove(B, K); + + ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); + if (NewCluster.isEmpty()) + return RBFactory.remove(B, Base); + return RBFactory.add(B, Base, NewCluster); } RegionBindings RegionStoreManager::removeBinding(RegionBindings B, @@ -1768,6 +1868,11 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B, return removeBinding(B, BindingKey::Make(R, k)); } +RegionBindings RegionStoreManager::removeCluster(RegionBindings B, + const MemRegion *Base) { + return RBFactory.remove(B, Base); +} + //===----------------------------------------------------------------------===// // State pruning. //===----------------------------------------------------------------------===// @@ -1789,8 +1894,8 @@ public: SymReaper(symReaper), CurrentLCtx(LCtx) {} // Called by ClusterAnalysis. - void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C); - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C); + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); void VisitBindingKey(BindingKey K); bool UpdatePostponed(); @@ -1799,18 +1904,18 @@ public: } void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, - RegionCluster &C) { + const ClusterBindings &C) { if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) { if (SymReaper.isLive(VR)) - AddToWorkList(baseR, C); + AddToWorkList(baseR, &C); return; } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { if (SymReaper.isLive(SR->getSymbol())) - AddToWorkList(SR, C); + AddToWorkList(SR, &C); else Postponed.push_back(SR); @@ -1818,7 +1923,7 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } if (isa<NonStaticGlobalSpaceRegion>(baseR)) { - AddToWorkList(baseR, C); + AddToWorkList(baseR, &C); return; } @@ -1828,34 +1933,57 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); const StackFrameContext *RegCtx = StackReg->getStackFrame(); if (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx)) - AddToWorkList(TR, C); + AddToWorkList(TR, &C); } } void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR, - BindingKey *I, BindingKey *E) { - for ( ; I != E; ++I) - VisitBindingKey(*I); + const ClusterBindings &C) { + for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) { + VisitBindingKey(I.getKey()); + VisitBinding(I.getData()); + } } void removeDeadBindingsWorker::VisitBinding(SVal V) { // Is it a LazyCompoundVal? All referenced regions are live as well. if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + dyn_cast<nonloc::LazyCompoundVal>(&V)) { const MemRegion *LazyR = LCS->getRegion(); RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + + // FIXME: This should not have to walk all bindings in the old store. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion()); - if (baseR && baseR->isSubRegionOf(LazyR)) - VisitBinding(RI.getData()); + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + BindingKey K = CI.getKey(); + if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { + if (BaseR == LazyR) + VisitBinding(CI.getData()); + else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) + VisitBinding(CI.getData()); + } + } } + return; } // If V is a region, then add it to the worklist. - if (const MemRegion *R = V.getAsRegion()) + if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); + + // All regions captured by a block are also live. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); + for ( ; I != E; ++I) + AddToWorkList(I.getCapturedRegion()); + } + } + // Update the set of live symbols. for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); @@ -1874,26 +2002,7 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) { // should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr<BlocksAttr>()) - AddToWorkList(*RI); - } - - // No possible data bindings on a BlockDataRegion. - return; - } } - - // Visit the data binding for K. - if (const SVal *V = RM.lookup(B, K)) - VisitBinding(*V); } bool removeDeadBindingsWorker::UpdatePostponed() { @@ -1933,68 +2042,32 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // as live. We now remove all the regions that are dead from the store // as well as update DSymbols with the set symbols that are now dead. for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const BindingKey &K = I.getKey(); + const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. - if (W.isVisited(K.getRegion())) + if (W.isVisited(Base)) continue; // Remove the dead entry. - B = removeBinding(B, K); + B = removeCluster(B, Base); - // Mark all non-live symbols that this binding references as dead. - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(K.getRegion())) + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) SymReaper.maybeDead(SymR->getSymbol()); - SVal X = I.getData(); - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); + // Mark all non-live symbols that this binding references as dead. + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + SVal X = CI.getData(); + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); + } } return StoreRef(B.getRootWithoutRetain(), *this); } - -StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) -{ - FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl()); - FunctionDecl::param_const_iterator PI = FD->param_begin(), - PE = FD->param_end(); - StoreRef store = StoreRef(state->getStore(), *this); - - if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) { - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. We check that - // PI != PE because the actual number of arguments may be different than - // the function declaration. - for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - } else if (const CXXConstructExpr *CE = - dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) { - CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), - AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. - for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - } else - assert(isa<CXXDestructorDecl>(calleeCtx->getDecl())); - - return store; -} - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// @@ -2002,8 +2075,16 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, void RegionStoreManager::print(Store store, raw_ostream &OS, const char* nl, const char *sep) { RegionBindings B = GetRegionBindings(store); - OS << "Store (direct and default bindings):" << nl; + OS << "Store (direct and default bindings), " + << (void*) B.getRootWithoutRetain() + << " :" << nl; - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - OS << ' ' << I.getKey() << " : " << I.getData() << nl; + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; + } + OS << nl; + } } diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 9e97f5e7d12d..d1936cd3603e 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" @@ -61,7 +62,6 @@ NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { assert(lhs && rhs); - assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true); assert(!Loc::isLocType(type)); return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); } @@ -149,6 +149,18 @@ SValBuilder::getConjuredSymbolVal(const Stmt *stmt, return nonloc::SymbolVal(sym); } +DefinedOrUnknownSVal +SValBuilder::getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount) { + QualType T = E->getType(); + assert(Loc::isLocType(T)); + assert(SymbolManager::canSymbolicate(T)); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, LCtx, T, VisitCount); + return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); +} + DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, const MemRegion *region, const Expr *expr, QualType type, @@ -193,31 +205,48 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, return loc::MemRegionVal(BD); } +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D, + const StackFrameContext *SFC) { + return loc::MemRegionVal(getRegionManager(). + getCXXThisRegion(D->getThisType(getContext()), SFC)); +} + +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D, + const StackFrameContext *SFC) { + const Type *T = D->getTypeForDecl(); + QualType PT = getContext().getPointerType(QualType(T, 0)); + return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC)); +} + //===----------------------------------------------------------------------===// -SVal SValBuilder::makeGenericVal(ProgramStateRef State, - BinaryOperator::Opcode Op, - NonLoc LHS, NonLoc RHS, - QualType ResultTy) { - // If operands are tainted, create a symbol to ensure that we propagate taint. - if (State->isTainted(RHS) || State->isTainted(LHS)) { - const SymExpr *symLHS; - const SymExpr *symRHS; - - if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) { - symLHS = LHS.getAsSymExpr(); +SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, + BinaryOperator::Opcode Op, + NonLoc LHS, NonLoc RHS, + QualType ResultTy) { + if (!State->isTainted(RHS) && !State->isTainted(LHS)) + return UnknownVal(); + + const SymExpr *symLHS = LHS.getAsSymExpr(); + const SymExpr *symRHS = RHS.getAsSymExpr(); + // TODO: When the Max Complexity is reached, we should conjure a symbol + // instead of generating an Unknown value and propagate the taint info to it. + const unsigned MaxComp = 10000; // 100000 28X + + if (symLHS && symRHS && + (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) + return makeNonLoc(symLHS, Op, symRHS, ResultTy); + + if (symLHS && symLHS->computeComplexity() < MaxComp) + if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); - } - if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) { - symRHS = RHS.getAsSymExpr(); + if (symRHS && symRHS->computeComplexity() < MaxComp) + if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); - } - symLHS = LHS.getAsSymExpr(); - symRHS = RHS.getAsSymExpr(); - return makeNonLoc(symLHS, Op, symRHS, ResultTy); - } return UnknownVal(); } @@ -324,7 +353,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Are we casting from an array to a pointer? If so just pass on // the decayed value. - if (castTy->isPointerType()) + if (castTy->isPointerType() || castTy->isReferenceType()) return val; // Are we casting from an array to an integer? If so, cast the decayed @@ -340,9 +369,12 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Check for casts from a region to a specific type. if (const MemRegion *R = val.getAsRegion()) { + // Handle other casts of locations to integers. + if (castTy->isIntegerType()) + return evalCastFromLoc(loc::MemRegionVal(R), castTy); + // FIXME: We should handle the case where we strip off view layers to get // to a desugared type. - if (!Loc::isLocType(castTy)) { // FIXME: There can be gross cases where one casts the result of a function // (that returns a pointer) to some other value that happens to fit diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index b94aff449d3a..8437f50f911f 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -133,9 +133,9 @@ const MemRegion *SVal::getAsRegion() const { return 0; } -const MemRegion *loc::MemRegionVal::stripCasts() const { +const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const { const MemRegion *R = getRegion(); - return R ? R->StripCasts() : NULL; + return R ? R->StripCasts(StripBaseCasts) : NULL; } const void *nonloc::LazyCompoundVal::getStore() const { @@ -309,22 +309,6 @@ void Loc::dumpToStream(raw_ostream &os) const { case loc::MemRegionKind: os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); break; - case loc::ObjCPropRefKind: { - const ObjCPropertyRefExpr *E = cast<loc::ObjCPropRef>(this)->getPropRefExpr(); - os << "objc-prop{"; - if (E->isSuperReceiver()) - os << "super."; - else if (E->getBase()) - os << "<base>."; - - if (E->isImplicitProperty()) - os << E->getImplicitPropertyGetter()->getSelector().getAsString(); - else - os << E->getExplicitProperty()->getName(); - - os << "}"; - break; - } default: llvm_unreachable("Pretty-printing not implemented for this Loc."); } diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index a76a2da001b5..5568f1ca555d 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -71,9 +72,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, Loc Cond, bool Assumption) { - - BasicValueFactory &BasicVals = state->getBasicVals(); - switch (Cond.getSubKind()) { default: assert (false && "'Assume' not implemented for this Loc."); @@ -88,7 +86,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, while (SubR) { // FIXME: now we only find the first symbolic region. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) { - const llvm::APSInt &zero = BasicVals.getZeroWithPtrWidth(); + const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth(); if (Assumption) return assumeSymNE(state, SymR->getSymbol(), zero, zero); else @@ -134,12 +132,17 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } -ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol( - ProgramStateRef State, - SymbolRef Sym, - bool Assumption) { - QualType T = State->getSymbolManager().getType(Sym); - const llvm::APSInt &zero = State->getBasicVals().getValue(0, T); +ProgramStateRef +SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, + SymbolRef Sym, bool Assumption) { + BasicValueFactory &BVF = getBasicVals(); + QualType T = Sym->getType(BVF.getContext()); + + // None of the constraint solvers currently support non-integer types. + if (!T->isIntegerType()) + return State; + + const llvm::APSInt &zero = BVF.getValue(0, T); if (Assumption) return assumeSymNE(State, Sym, zero, zero); else @@ -158,8 +161,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); } - BasicValueFactory &BasicVals = state->getBasicVals(); - SymbolManager &SymMgr = state->getSymbolManager(); + BasicValueFactory &BasicVals = getBasicVals(); switch (Cond.getSubKind()) { default: @@ -184,7 +186,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, BinaryOperator::Opcode op = SE->getOpcode(); // Implicitly compare non-comparison expressions to 0. if (!BinaryOperator::isComparisonOp(op)) { - QualType T = SymMgr.getType(SE); + QualType T = SE->getType(BasicVals.getContext()); const llvm::APSInt &zero = BasicVals.getValue(0, T); op = (Assumption ? BO_NE : BO_EQ); return assumeSymRel(state, SE, op, zero); @@ -209,33 +211,20 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, } // end switch } -static llvm::APSInt computeAdjustment(const SymExpr *LHS, - SymbolRef &Sym) { - llvm::APSInt DefaultAdjustment; - DefaultAdjustment = 0; - - // First check if the LHS is a simple symbol reference. - if (isa<SymbolData>(LHS)) - return DefaultAdjustment; - - // Next, see if it's a "($sym+constant1)" expression. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); - - // We cannot simplify "($sym1+$sym2)". - if (!SE) - return DefaultAdjustment; - - // Get the constant out of the expression "($sym+constant1)" or - // "<expr>+constant1". - Sym = SE->getLHS(); - switch (SE->getOpcode()) { - case BO_Add: - return SE->getRHS(); - case BO_Sub: - return -SE->getRHS(); - default: - // We cannot simplify non-additive operators. - return DefaultAdjustment; +static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) { + // Is it a "($sym+constant1)" expression? + if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { + BinaryOperator::Opcode Op = SE->getOpcode(); + if (Op == BO_Add || Op == BO_Sub) { + Sym = SE->getLHS(); + Adjustment = APSIntType(Adjustment).convert(SE->getRHS()); + + // Don't forget to negate the adjustment if it's being subtracted. + // This should happen /after/ promotion, in case the value being + // subtracted is, say, CHAR_MIN, and the promoted type is 'int'. + if (Op == BO_Sub) + Adjustment = -Adjustment; + } } } @@ -246,6 +235,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, assert(BinaryOperator::isComparisonOp(op) && "Non-comparison ops should be rewritten as comparisons to zero."); + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + + // Get the type used for calculating wraparound. + APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType(Ctx)); + // We only handle simple comparisons of the form "$sym == constant" // or "($sym+constant1) == constant2". // The adjustment is "constant1" in the above expression. It's used to @@ -254,28 +249,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to // the subclasses of SimpleConstraintManager to handle the adjustment. SymbolRef Sym = LHS; - llvm::APSInt Adjustment = computeAdjustment(LHS, Sym); - - // FIXME: This next section is a hack. It silently converts the integers to - // be of the same type as the symbol, which is not always correct. Really the - // comparisons should be performed using the Int's type, then mapped back to - // the symbol's range of values. - ProgramStateManager &StateMgr = state->getStateManager(); - ASTContext &Ctx = StateMgr.getContext(); - - QualType T = Sym->getType(Ctx); - assert(T->isIntegerType() || Loc::isLocType(T)); - unsigned bitwidth = Ctx.getTypeSize(T); - bool isSymUnsigned - = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T); - - // Convert the adjustment. - Adjustment.setIsUnsigned(isSymUnsigned); - Adjustment = Adjustment.extOrTrunc(bitwidth); - - // Convert the right-hand side integer. - llvm::APSInt ConvertedInt(Int, isSymUnsigned); - ConvertedInt = ConvertedInt.extOrTrunc(bitwidth); + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + computeAdjustment(Sym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); + llvm::APSInt ConvertedInt = ComparisonType.convert(Int); switch (op) { default: diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index e082d9d801f0..088d70c2717f 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -23,8 +23,10 @@ namespace ento { class SimpleConstraintManager : public ConstraintManager { SubEngine &SU; + BasicValueFactory &BVF; public: - SimpleConstraintManager(SubEngine &subengine) : SU(subengine) {} + SimpleConstraintManager(SubEngine &subengine, BasicValueFactory &BV) + : SU(subengine), BVF(BV) {} virtual ~SimpleConstraintManager(); //===------------------------------------------------------------------===// @@ -79,6 +81,8 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// + BasicValueFactory &getBasicVals() const { return BVF; } + bool canReasonAbout(SVal X) const; ProgramStateRef assumeAux(ProgramStateRef state, diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index d0558f1af441..ad58a07c784e 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -106,9 +107,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { return UnknownVal(); llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || - Loc::isLocType(castTy)); - i = i.extOrTrunc(Context.getTypeSize(castTy)); + BasicVals.getAPSIntType(castTy).apply(i); if (isLocType) return makeIntLocVal(i); @@ -139,9 +138,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { return makeLocAsInteger(val, BitWidth); llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || - Loc::isLocType(castTy)); - i = i.extOrTrunc(BitWidth); + BasicVals.getAPSIntType(castTy).apply(i); return makeIntVal(i); } @@ -272,14 +269,40 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); // If we reach this point, the expression cannot be simplified. - // Make a SymbolVal for the entire expression. - return makeNonLoc(LHS, op, RHS, resultTy); + // Make a SymbolVal for the entire expression, after converting the RHS. + const llvm::APSInt *ConvertedRHS = &RHS; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the symbolic value + // with the given constant. + // FIXME: This is an approximation of Sema::UsualArithmeticConversions. + ASTContext &Ctx = getContext(); + QualType SymbolType = LHS->getType(Ctx); + uint64_t ValWidth = RHS.getBitWidth(); + uint64_t TypeWidth = Ctx.getTypeSize(SymbolType); + + if (ValWidth < TypeWidth) { + // If the value is too small, extend it. + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } else if (ValWidth == TypeWidth) { + // If the value is signed but the symbol is unsigned, do the comparison + // in unsigned space. [C99 6.3.1.8] + // (For the opposite case, the value is already unsigned.) + if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType()) + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } + } else + ConvertedRHS = &BasicVals.Convert(resultTy, RHS); + + return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); } SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) { + NonLoc InputLHS = lhs; + NonLoc InputRHS = rhs; + // Handle trivial case where left-side and right-side are the same. if (lhs == rhs) switch (op) { @@ -304,7 +327,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, while (1) { switch (lhs.getSubKind()) { default: - return makeGenericVal(state, op, lhs, rhs, resultTy); + return makeSymExprValNN(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); switch (rhs.getSubKind()) { @@ -315,8 +338,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue(); - i.setIsUnsigned(true); - i = i.extOrTrunc(Context.getTypeSize(Context.VoidPtrTy)); + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } default: @@ -327,86 +349,78 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, return makeTruthVal(true, resultTy); default: // This case also handles pointer arithmetic. - return makeGenericVal(state, op, lhs, rhs, resultTy); + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } } case nonloc::ConcreteIntKind: { - const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); - - // Is the RHS a symbol we can simplify? - // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case. - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt *rhs_I; - if (BinaryOperator::isRelationalOp(op)) - rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant); - else - rhs_I = &BasicVals.Convert(resultTy, *Constant); - - rhs = nonloc::ConcreteInt(*rhs_I); - } + llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue(); + + // If we're dealing with two known constants, just perform the operation. + if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { + llvm::APSInt RHSValue = *KnownRHSValue; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the two values. + // FIXME: This is not correct. char + short will result in a promotion + // to int. Unfortunately we have lost types by this point. + APSIntType CompareType = std::max(APSIntType(LHSValue), + APSIntType(RHSValue)); + CompareType.apply(LHSValue); + CompareType.apply(RHSValue); + } else if (!BinaryOperator::isShiftOp(op)) { + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + IntType.apply(LHSValue); + IntType.apply(RHSValue); } - } - if (isa<nonloc::ConcreteInt>(rhs)) { - return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs)); - } else { - const llvm::APSInt& lhsValue = lhsInt.getValue(); - - // Swap the left and right sides and flip the operator if doing so - // allows us to better reason about the expression (this is a form - // of expression canonicalization). - // While we're at it, catch some special cases for non-commutative ops. - NonLoc tmp = rhs; - rhs = lhs; - lhs = tmp; + const llvm::APSInt *Result = + BasicVals.evalAPSInt(op, LHSValue, RHSValue); + if (!Result) + return UndefinedVal(); - switch (op) { - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - op = ReverseComparison(op); - continue; - case BO_EQ: - case BO_NE: - case BO_Add: - case BO_Mul: - case BO_And: - case BO_Xor: - case BO_Or: - continue; - case BO_Shr: - if (lhsValue.isAllOnesValue() && lhsValue.isSigned()) - // At this point lhs and rhs have been swapped. - return rhs; - // FALL-THROUGH - case BO_Shl: - if (lhsValue == 0) - // At this point lhs and rhs have been swapped. - return rhs; - return makeGenericVal(state, op, rhs, lhs, resultTy); - default: - return makeGenericVal(state, op, rhs, lhs, resultTy); - } + return nonloc::ConcreteInt(*Result); + } + + // Swap the left and right sides and flip the operator if doing so + // allows us to better reason about the expression (this is a form + // of expression canonicalization). + // While we're at it, catch some special cases for non-commutative ops. + switch (op) { + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + op = ReverseComparison(op); + // FALL-THROUGH + case BO_EQ: + case BO_NE: + case BO_Add: + case BO_Mul: + case BO_And: + case BO_Xor: + case BO_Or: + std::swap(lhs, rhs); + continue; + case BO_Shr: + // (~0)>>a + if (LHSValue.isAllOnesValue() && LHSValue.isSigned()) + return evalCastFromNonLoc(lhs, resultTy); + // FALL-THROUGH + case BO_Shl: + // 0<<a and 0>>a + if (LHSValue == 0) + return evalCastFromNonLoc(lhs, resultTy); + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); + default: + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } case nonloc::SymbolValKind: { - nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs); + // We only handle LHS as simple symbols or SymIntExprs. + SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol(); // LHS is a symbolic expression. - if (selhs->isExpression()) { - - // Only handle LHS of the form "$sym op constant", at least for now. - const SymIntExpr *symIntExpr = - dyn_cast<SymIntExpr>(selhs->getSymbol()); - - if (!symIntExpr) - return makeGenericVal(state, op, lhs, rhs, resultTy); + if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) { // Is this a logical not? (!x is represented as x == 0.) if (op == BO_EQ && rhs.isZeroConstant()) { @@ -452,95 +466,57 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } // For now, only handle expressions whose RHS is a constant. - const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); - if (!rhsInt) - return makeGenericVal(state, op, lhs, rhs, resultTy); - - // If both the LHS and the current expression are additive, - // fold their constants. - if (BinaryOperator::isAdditiveOp(op)) { - BinaryOperator::Opcode lop = symIntExpr->getOpcode(); - if (BinaryOperator::isAdditiveOp(lop)) { - // resultTy may not be the best type to convert to, but it's - // probably the best choice in expressions with mixed type - // (such as x+1U+2LL). The rules for implicit conversions should - // choose a reasonable type to preserve the expression, and will - // at least match how the value is going to be used. - const llvm::APSInt &first = - BasicVals.Convert(resultTy, symIntExpr->getRHS()); - const llvm::APSInt &second = - BasicVals.Convert(resultTy, rhsInt->getValue()); - const llvm::APSInt *newRHS; - if (lop == op) - newRHS = BasicVals.evalAPSInt(BO_Add, first, second); - else - newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); - return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); - } - } - - // Otherwise, make a SymbolVal out of the expression. - return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - - // LHS is a simple symbol (not a symbolic expression). - } else { - nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); - SymbolRef Sym = slhs->getSymbol(); - QualType lhsType = Sym->getType(Context); - - // The conversion type is usually the result type, but not in the case - // of relational expressions. - QualType conversionType = resultTy; - if (BinaryOperator::isRelationalOp(op)) - conversionType = lhsType; - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (lhsType->isIntegerType()) - if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { - // The symbol evaluates to a constant. If necessary, promote the - // folded constant (LHS) to the result type. - const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, - *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it is not necessary to promote the RHS. - if (BinaryOperator::isShiftOp(op)) + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) { + // If both the LHS and the current expression are additive, + // fold their constants and try again. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // Convert the two constants to a common type, then combine them. + + // resultTy may not be the best type to convert to, but it's + // probably the best choice in expressions with mixed type + // (such as x+1U+2LL). The rules for implicit conversions should + // choose a reasonable type to preserve the expression, and will + // at least match how the value is going to be used. + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS()); + const llvm::APSInt &second = IntType.convert(*RHSValue); + + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + + assert(newRHS && "Invalid operation despite common type!"); + rhs = nonloc::ConcreteInt(*newRHS); + lhs = nonloc::SymbolVal(symIntExpr->getLHS()); + op = lop; continue; - - // Other operators: do an implicit conversion. This shouldn't be - // necessary once we support truncation/extension of symbolic values. - if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ - rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, - rhs_I->getValue())); } - - continue; } - // Is the RHS a symbol we can simplify? - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, - *Constant); - rhs = nonloc::ConcreteInt(rhs_I); - } - } + // Otherwise, make a SymIntExpr out of the expression. + return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy); } - if (isa<nonloc::ConcreteInt>(rhs)) { - return MakeSymIntVal(slhs->getSymbol(), op, - cast<nonloc::ConcreteInt>(rhs).getValue(), - resultTy); + + } else if (isa<SymbolData>(Sym)) { + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + lhs = nonloc::ConcreteInt(*Constant); + continue; } - return makeGenericVal(state, op, lhs, rhs, resultTy); + // Is the RHS a constant? + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); } + + // Give up -- this is not a symbolic expression we can handle. + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } } @@ -697,11 +673,18 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // regions, though. return UnknownVal(); - // If both values wrap regions, see if they're from different base regions. + const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace(); + const MemSpaceRegion *RightMS = RightMR->getMemorySpace(); + const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); const MemRegion *LeftBase = LeftMR->getBaseRegion(); const MemRegion *RightBase = RightMR->getBaseRegion(); - if (LeftBase != RightBase && - !isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) { + + // If the two regions are from different known memory spaces they cannot be + // equal. Also, assume that no symbolic region (whose memory space is + // unknown) is on the stack. + if (LeftMS != RightMS && + ((LeftMS != UnknownMS && RightMS != UnknownMS) || + (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) { switch (op) { default: return UnknownVal(); @@ -712,24 +695,20 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } } - // The two regions are from the same base region. See if they're both a - // type of region we know how to compare. - const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); - const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); - - // Heuristic: assume that no symbolic region (whose memory space is - // unknown) is on the stack. - // FIXME: we should be able to be more precise once we can do better - // aliasing constraints for symbolic regions, but this is a reasonable, - // albeit unsound, assumption that holds most of the time. - if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) { + // If both values wrap regions, see if they're from different base regions. + // Note, heap base symbolic regions are assumed to not alias with + // each other; for example, we assume that malloc returns different address + // on each invocation. + if (LeftBase != RightBase && + ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) || + (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){ switch (op) { - default: - break; - case BO_EQ: - return makeTruthVal(false, resultTy); - case BO_NE: - return makeTruthVal(true, resultTy); + default: + return UnknownVal(); + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); } } @@ -885,6 +864,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy); } } + return UnknownVal(); } // We are dealing with pointer arithmetic. diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 11748ae54dbc..3af60a170727 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" @@ -23,10 +24,21 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) { - return StoreRef(state->getStore(), *this); +StoreRef StoreManager::enterStackFrame(Store OldStore, + const CallEvent &Call, + const StackFrameContext *LCtx) { + StoreRef Store = StoreRef(OldStore, *this); + + SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings; + Call.getInitialStackFrameContents(LCtx, InitialBindings); + + for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(), + E = InitialBindings.end(); + I != E; ++I) { + Store = Bind(Store.getStore(), I->first, I->second); + } + + return Store; } const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, @@ -210,6 +222,17 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) llvm_unreachable("unreachable"); } +SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { + // Walk through the cast path to create nested CXXBaseRegions. + SVal Result = Derived; + for (CastExpr::path_const_iterator I = Cast->path_begin(), + E = Cast->path_end(); + I != E; ++I) { + Result = evalDerivedToBase(Result, (*I)->getType()); + } + return Result; +} + /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted @@ -357,6 +380,3 @@ bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, return true; } - -void SubRegionMap::anchor() { } -void SubRegionMap::Visitor::anchor() { } diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index adefb5858e01..0bc192d64571 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -164,6 +164,13 @@ void SymExpr::symbol_iterator::expand() { llvm_unreachable("unhandled expansion case"); } +unsigned SymExpr::computeComplexity() const { + unsigned R = 0; + for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I) + R++; + return R; +} + const SymbolRegionValue* SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; @@ -501,6 +508,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { return false; return true; } + // If no statement is provided, everything is this and parent contexts is live. + if (!Loc) + return true; return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); } @@ -510,6 +520,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); if (VarContext == CurrentContext) { + // If no statemetnt is provided, everything is live. + if (!Loc) + return true; + if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl())) return true; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index fe912dfd21f9..e5b8553aeda1 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -42,6 +42,7 @@ public: bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return true; } + virtual bool supportsCrossFileDiagnostics() const { return true; } }; } // end anonymous namespace @@ -58,7 +59,9 @@ void TextPathDiagnostics::FlushDiagnosticsImpl( for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { const PathDiagnostic *D = *it; - for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + + PathPieces FlatPath = D->path.flatten(/*ShouldFlattenMacros=*/true); + for (PathPieces::const_iterator I = FlatPath.begin(), E = FlatPath.end(); I != E; ++I) { unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 008f744957ef..fcdaaeaedf9e 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -22,6 +22,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" @@ -57,6 +58,7 @@ STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); STATISTIC(NumBlocksInAnalyzedFunctions, "The # of basic blocks in the analyzed functions."); STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); +STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); //===----------------------------------------------------------------------===// // Special PathDiagnosticConsumers. @@ -102,7 +104,7 @@ public: /// The local declaration to all declarations ratio might be very small when /// working with a PCH file. SetOfDecls LocalTUDecls; - + // PD is owned by AnalysisManager. PathDiagnosticConsumer *PD; @@ -212,7 +214,6 @@ public: Opts.AnalysisPurgeOpt, Opts.EagerlyAssume, Opts.TrimGraph, Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, - Opts.CFGAddInitializers, Opts.EagerlyTrimEGraph, Opts.IPAMode, Opts.InlineMaxStackDepth, @@ -230,7 +231,7 @@ public: /// \brief Build the call graph for all the top level decls of this TU and /// use it to define the order in which the functions should be visited. - void HandleDeclsGallGraph(); + void HandleDeclsGallGraph(const unsigned LocalTUDeclsSize); /// \brief Run analyzes(syntax or path sensitive) on the given function. /// \param Mode - determines if we are requesting syntax only or path @@ -246,6 +247,7 @@ public: SetOfConstDecls *VisitedCallees); /// Visitors for the RecursiveASTVisitor. + bool shouldWalkTypesOfTypeLocs() const { return false; } /// Handle callbacks for arbitrary Decls. bool VisitDecl(Decl *D) { @@ -306,18 +308,22 @@ void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { if (isa<ObjCMethodDecl>(*I)) continue; - LocalTUDecls.insert(*I); + LocalTUDecls.push_back(*I); } } -void AnalysisConsumer::HandleDeclsGallGraph() { +void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) { // Otherwise, use the Callgraph to derive the order. // Build the Call Graph. CallGraph CG; + // Add all the top level declarations to the graph. - for (SetOfDecls::iterator I = LocalTUDecls.begin(), - E = LocalTUDecls.end(); I != E; ++I) - CG.addToCallGraph(*I); + // Note: CallGraph can trigger deserialization of more items from a pch + // (though HandleInterestingDecl); triggering additions to LocalTUDecls. + // We rely on random access to add the initially processed Decls to CG. + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + CG.addToCallGraph(LocalTUDecls[i]); + } // Find the top level nodes - children of root + the unreachable (parentless) // nodes. @@ -338,11 +344,11 @@ void AnalysisConsumer::HandleDeclsGallGraph() { // translation unit. This step is very important for performance. It ensures // that we analyze the root functions before the externally available // subroutines. - std::queue<CallGraphNode*> BFSQueue; + std::deque<CallGraphNode*> BFSQueue; for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); TI != TE; ++TI) - BFSQueue.push(*TI); + BFSQueue.push_back(*TI); // BFS over all of the functions, while skipping the ones inlined into // the previously processed functions. Use external Visited set, which is @@ -350,7 +356,14 @@ void AnalysisConsumer::HandleDeclsGallGraph() { SmallPtrSet<CallGraphNode*,24> Visited; while(!BFSQueue.empty()) { CallGraphNode *N = BFSQueue.front(); - BFSQueue.pop(); + BFSQueue.pop_front(); + + // Push the children into the queue. + for (CallGraphNode::const_iterator CI = N->begin(), + CE = N->end(); CI != CE; ++CI) { + if (!Visited.count(*CI)) + BFSQueue.push_back(*CI); + } // Skip the functions which have been processed already or previously // inlined. @@ -365,19 +378,13 @@ void AnalysisConsumer::HandleDeclsGallGraph() { (Mgr->InliningMode == All ? 0 : &VisitedCallees)); // Add the visited callees to the global visited set. - for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(), - E = VisitedCallees.end(); I != E; ++I){ + for (SetOfConstDecls::iterator I = VisitedCallees.begin(), + E = VisitedCallees.end(); I != E; ++I) { CallGraphNode *VN = CG.getNode(*I); if (VN) Visited.insert(VN); } Visited.insert(N); - - // Push the children into the queue. - for (CallGraphNode::const_iterator CI = N->begin(), - CE = N->end(); CI != CE; ++CI) { - BFSQueue.push(*CI); - } } } @@ -402,12 +409,18 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { RecVisitorBR = &BR; // Process all the top level declarations. - for (SetOfDecls::iterator I = LocalTUDecls.begin(), - E = LocalTUDecls.end(); I != E; ++I) - TraverseDecl(*I); + // + // Note: TraverseDecl may modify LocalTUDecls, but only by appending more + // entries. Thus we don't use an iterator, but rely on LocalTUDecls + // random access. By doing so, we automatically compensate for iterators + // possibly being invalidated, although this is a bit slower. + const unsigned LocalTUDeclsSize = LocalTUDecls.size(); + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + TraverseDecl(LocalTUDecls[i]); + } if (Mgr->shouldInlineCall()) - HandleDeclsGallGraph(); + HandleDeclsGallGraph(LocalTUDeclsSize); // After all decls handled, run checkers on the entire TranslationUnit. checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); @@ -475,6 +488,12 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, return; DisplayFunction(D, Mode); + CFG *DeclCFG = Mgr->getCFG(D); + if (DeclCFG) { + unsigned CFGSize = DeclCFG->size(); + MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize; + } + // Clear the AnalysisManager of old AnalysisDeclContexts. Mgr->ClearContexts(); @@ -510,6 +529,10 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, if (!Mgr->getCFG(D)) return; + // See if the LiveVariables analysis scales. + if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) + return; + ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); // Set the graph auditor. @@ -520,7 +543,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, } // Execute the worklist algorithm. - Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0), + Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), Mgr->getMaxNodes()); // Release the auditor (if any) so that it doesn't monitor the graph diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index bbcb085f8478..06d148507874 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -1,8 +1,5 @@ set(LLVM_NO_RTTI 1) -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite - clangStaticAnalyzerCheckers) - include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../Checkers ) add_clang_library(clangStaticAnalyzerFrontend @@ -16,6 +13,18 @@ add_dependencies(clangStaticAnalyzerFrontend clangStaticAnalyzerCore ClangAttrClasses ClangAttrList + ClangCommentNodes ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend ClangStmtNodes ) + +target_link_libraries(clangStaticAnalyzerFrontend + clangBasic + clangLex + clangAST + clangFrontend + clangRewrite + clangStaticAnalyzerCheckers + ) diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index c06da0d9e4b0..0229aed6bdaa 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -118,7 +118,7 @@ CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts, for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { if (checkerOpts[i].isUnclaimed()) - diags.Report(diag::warn_unknown_analyzer_checker) + diags.Report(diag::err_unknown_analyzer_checker) << checkerOpts[i].getName(); } diff --git a/lib/Tooling/ArgumentsAdjusters.cpp b/lib/Tooling/ArgumentsAdjusters.cpp new file mode 100644 index 000000000000..31dd46599587 --- /dev/null +++ b/lib/Tooling/ArgumentsAdjusters.cpp @@ -0,0 +1,34 @@ +//===--- ArgumentsAdjusters.cpp - Command line arguments adjuster ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions of classes which implement ArgumentsAdjuster +// interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/ArgumentsAdjusters.h" + +namespace clang { +namespace tooling { + +void ArgumentsAdjuster::anchor() { +} + +/// Add -fsyntax-only option to the commnand line arguments. +CommandLineArguments +ClangSyntaxOnlyAdjuster::Adjust(const CommandLineArguments &Args) { + CommandLineArguments AdjustedArgs = Args; + // FIXME: Remove options that generate output. + AdjustedArgs.push_back("-fsyntax-only"); + return AdjustedArgs; +} + +} // end namespace tooling +} // end namespace clang + diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt index b84b21194190..49d3101f0e99 100644 --- a/lib/Tooling/CMakeLists.txt +++ b/lib/Tooling/CMakeLists.txt @@ -1,7 +1,27 @@ set(LLVM_LINK_COMPONENTS support) -SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST) add_clang_library(clangTooling + ArgumentsAdjusters.cpp + CommandLineClangTool.cpp CompilationDatabase.cpp + Refactoring.cpp + RefactoringCallbacks.cpp Tooling.cpp ) + +add_dependencies(clangTooling + ClangAttrClasses + ClangAttrList + ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangStmtNodes + ) + +target_link_libraries(clangTooling + clangBasic + clangFrontend + clangAST + clangASTMatchers + clangRewrite + ) diff --git a/lib/Tooling/CommandLineClangTool.cpp b/lib/Tooling/CommandLineClangTool.cpp new file mode 100644 index 000000000000..8da2a335a557 --- /dev/null +++ b/lib/Tooling/CommandLineClangTool.cpp @@ -0,0 +1,80 @@ +//===--- CommandLineClangTool.cpp - command-line clang tools driver -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the CommandLineClangTool class used to run clang +// tools as separate command-line applications with a consistent common +// interface for handling compilation database and input files. +// +// It provides a common subset of command-line options, common algorithm +// for locating a compilation database and source files, and help messages +// for the basic command-line interface. +// +// It creates a CompilationDatabase, initializes a ClangTool and runs a +// user-specified FrontendAction over all TUs in which the given files are +// compiled. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommandLineClangTool.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang::tooling; +using namespace llvm; + +static const char *MoreHelpText = + "\n" + "-p <build-path> is used to read a compile command database.\n" + "\n" + "\tFor example, it can be a CMake build directory in which a file named\n" + "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n" + "\tCMake option to get this output). When no build path is specified,\n" + "\tclang-check will attempt to locate it automatically using all parent\n" + "\tpaths of the first input file. See:\n" + "\thttp://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n" + "\texample of setting up Clang Tooling on a source tree.\n" + "\n" + "<source0> ... specify the paths of source files. These paths are looked\n" + "\tup in the compile command database. If the path of a file is absolute,\n" + "\tit needs to point into CMake's source tree. If the path is relative,\n" + "\tthe current working directory needs to be in the CMake source tree and\n" + "\tthe file must be in a subdirectory of the current working directory.\n" + "\t\"./\" prefixes in the relative files will be automatically removed,\n" + "\tbut the rest of a relative path must be a suffix of a path in the\n" + "\tcompile command database.\n" + "\n"; + +CommandLineClangTool::CommandLineClangTool() : + BuildPath("p", cl::desc("Build path"), cl::Optional), + SourcePaths(cl::Positional, cl::desc("<source0> [... <sourceN>]"), + cl::OneOrMore), + MoreHelp(MoreHelpText) { +} + +void CommandLineClangTool::initialize(int argc, const char **argv) { + Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc, argv)); + cl::ParseCommandLineOptions(argc, argv); + if (!Compilations) { + std::string ErrorMessage; + if (!BuildPath.empty()) { + Compilations.reset(CompilationDatabase::autoDetectFromDirectory( + BuildPath, ErrorMessage)); + } else { + Compilations.reset(CompilationDatabase::autoDetectFromSource( + SourcePaths[0], ErrorMessage)); + } + if (!Compilations) + llvm::report_fatal_error(ErrorMessage); + } +} + +int CommandLineClangTool::run(FrontendActionFactory *ActionFactory) { + ClangTool Tool(*Compilations, SourcePaths); + return Tool.run(ActionFactory); +} diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp index dd9ccc07b60b..3139cc21bb8d 100644 --- a/lib/Tooling/CompilationDatabase.cpp +++ b/lib/Tooling/CompilationDatabase.cpp @@ -12,11 +12,16 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/Path.h" #include "llvm/Support/system_error.h" +#ifdef USE_CUSTOM_COMPILATION_DATABASE +#include "CustomCompilationDatabase.h" +#endif + namespace clang { namespace tooling { @@ -121,6 +126,52 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, return Database.take(); } +static CompilationDatabase * +findCompilationDatabaseFromDirectory(StringRef Directory) { +#ifdef USE_CUSTOM_COMPILATION_DATABASE + if (CompilationDatabase *DB = + ::clang::tooling::findCompilationDatabaseForDirectory(Directory)) + return DB; +#endif + while (!Directory.empty()) { + std::string LoadErrorMessage; + + if (CompilationDatabase *DB = + CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) + return DB; + + Directory = llvm::sys::path::parent_path(Directory); + } + return NULL; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromSource(StringRef SourceFile, + std::string &ErrorMessage) { + llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); + StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(Directory); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database for file \"" + + SourceFile + "\"").str(); + return DB; +} + +CompilationDatabase * +CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, + std::string &ErrorMessage) { + llvm::SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); + + CompilationDatabase *DB = findCompilationDatabaseFromDirectory(AbsolutePath); + + if (!DB) + ErrorMessage = ("Could not auto-detect compilation database from directory \"" + + SourceDir + "\"").str(); + return DB; +} + FixedCompilationDatabase * FixedCompilationDatabase::loadFromCommandLine(int &Argc, const char **Argv, @@ -148,6 +199,11 @@ FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { return Result; } +std::vector<std::string> +FixedCompilationDatabase::getAllFiles() const { + return std::vector<std::string>(); +} + JSONCompilationDatabase * JSONCompilationDatabase::loadFromFile(StringRef FilePath, std::string &ErrorMessage) { @@ -179,8 +235,10 @@ JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString, std::vector<CompileCommand> JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { + llvm::SmallString<128> NativeFilePath; + llvm::sys::path::native(FilePath, NativeFilePath); llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator - CommandsRefI = IndexByFile.find(FilePath); + CommandsRefI = IndexByFile.find(NativeFilePath); if (CommandsRefI == IndexByFile.end()) return std::vector<CompileCommand>(); const std::vector<CompileCommandRef> &CommandsRef = CommandsRefI->getValue(); @@ -196,6 +254,21 @@ JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const { return Commands; } +std::vector<std::string> +JSONCompilationDatabase::getAllFiles() const { + std::vector<std::string> Result; + + llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefI = IndexByFile.begin(); + const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator + CommandsRefEnd = IndexByFile.end(); + for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) { + Result.push_back(CommandsRefI->first().str()); + } + + return Result; +} + bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { llvm::yaml::document_iterator I = YAMLStream.begin(); if (I == YAMLStream.end()) { @@ -222,10 +295,9 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { ErrorMessage = "Expected object."; return false; } - llvm::yaml::ScalarNode *Directory; - llvm::yaml::ScalarNode *Command; - llvm::SmallString<8> FileStorage; - llvm::StringRef File; + llvm::yaml::ScalarNode *Directory = NULL; + llvm::yaml::ScalarNode *Command = NULL; + llvm::yaml::ScalarNode *File = NULL; for (llvm::yaml::MappingNode::iterator KVI = Object->begin(), KVE = Object->end(); KVI != KVE; ++KVI) { @@ -242,20 +314,39 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { } llvm::yaml::ScalarNode *KeyString = llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey()); + if (KeyString == NULL) { + ErrorMessage = "Expected strings as key."; + return false; + } llvm::SmallString<8> KeyStorage; if (KeyString->getValue(KeyStorage) == "directory") { Directory = ValueString; } else if (KeyString->getValue(KeyStorage) == "command") { Command = ValueString; } else if (KeyString->getValue(KeyStorage) == "file") { - File = ValueString->getValue(FileStorage); + File = ValueString; } else { ErrorMessage = ("Unknown key: \"" + KeyString->getRawValue() + "\"").str(); return false; } } - IndexByFile[File].push_back( + if (!File) { + ErrorMessage = "Missing key: \"file\"."; + return false; + } + if (!Command) { + ErrorMessage = "Missing key: \"command\"."; + return false; + } + if (!Directory) { + ErrorMessage = "Missing key: \"directory\"."; + return false; + } + llvm::SmallString<8> FileStorage; + llvm::SmallString<128> NativeFilePath; + llvm::sys::path::native(File->getValue(FileStorage), NativeFilePath); + IndexByFile[NativeFilePath].push_back( CompileCommandRef(Directory, Command)); } return true; @@ -263,4 +354,3 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { } // end namespace tooling } // end namespace clang - diff --git a/lib/Tooling/CustomCompilationDatabase.h b/lib/Tooling/CustomCompilationDatabase.h new file mode 100644 index 000000000000..b375f8d25634 --- /dev/null +++ b/lib/Tooling/CustomCompilationDatabase.h @@ -0,0 +1,42 @@ +//===--- CustomCompilationDatabase.h --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a hook to supply a custom \c CompilationDatabase +// implementation. +// +// The mechanism can be used by IDEs or non-public code bases to integrate with +// their build system. Currently we support statically linking in an +// implementation of \c findCompilationDatabaseForDirectory and enabling it +// with -DUSE_CUSTOM_COMPILATION_DATABASE when compiling the Tooling library. +// +// FIXME: The strategy forward is to provide a plugin system that can load +// custom compilation databases and make enabling that a build option. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H +#define LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H + +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace tooling { +class CompilationDatabase; + +/// \brief Returns a CompilationDatabase for the given \c Directory. +/// +/// \c Directory can be any directory within a project. This methods will +/// then try to find compilation database files in \c Directory or any of its +/// parents. If a compilation database cannot be found or loaded, returns NULL. +clang::tooling::CompilationDatabase *findCompilationDatabaseForDirectory( + llvm::StringRef Directory); + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_CUSTOM_COMPILATION_DATABASE_H diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp new file mode 100644 index 000000000000..628435307c0b --- /dev/null +++ b/lib/Tooling/Refactoring.cpp @@ -0,0 +1,186 @@ +//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements tools to support refactorings. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/Support/raw_os_ostream.h" + +namespace clang { +namespace tooling { + +static const char * const InvalidLocation = ""; + +Replacement::Replacement() + : FilePath(InvalidLocation), Offset(0), Length(0) {} + +Replacement::Replacement(llvm::StringRef FilePath, unsigned Offset, + unsigned Length, llvm::StringRef ReplacementText) + : FilePath(FilePath), Offset(Offset), + Length(Length), ReplacementText(ReplacementText) {} + +Replacement::Replacement(SourceManager &Sources, SourceLocation Start, + unsigned Length, llvm::StringRef ReplacementText) { + setFromSourceLocation(Sources, Start, Length, ReplacementText); +} + +Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range, + llvm::StringRef ReplacementText) { + setFromSourceRange(Sources, Range, ReplacementText); +} + +bool Replacement::isApplicable() const { + return FilePath != InvalidLocation; +} + +bool Replacement::apply(Rewriter &Rewrite) const { + SourceManager &SM = Rewrite.getSourceMgr(); + const FileEntry *Entry = SM.getFileManager().getFile(FilePath); + if (Entry == NULL) + return false; + FileID ID; + // FIXME: Use SM.translateFile directly. + SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); + ID = Location.isValid() ? + SM.getFileID(Location) : + SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); + // FIXME: We cannot check whether Offset + Length is in the file, as + // the remapping API is not public in the RewriteBuffer. + const SourceLocation Start = + SM.getLocForStartOfFile(ID). + getLocWithOffset(Offset); + // ReplaceText returns false on success. + // ReplaceText only fails if the source location is not a file location, in + // which case we already returned false earlier. + bool RewriteSucceeded = !Rewrite.ReplaceText(Start, Length, ReplacementText); + assert(RewriteSucceeded); + return RewriteSucceeded; +} + +std::string Replacement::toString() const { + std::string result; + llvm::raw_string_ostream stream(result); + stream << FilePath << ": " << Offset << ":+" << Length + << ":\"" << ReplacementText << "\""; + return result; +} + +bool Replacement::Less::operator()(const Replacement &R1, + const Replacement &R2) const { + if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath; + if (R1.Offset != R2.Offset) return R1.Offset < R2.Offset; + if (R1.Length != R2.Length) return R1.Length < R2.Length; + return R1.ReplacementText < R2.ReplacementText; +} + +void Replacement::setFromSourceLocation(SourceManager &Sources, + SourceLocation Start, unsigned Length, + llvm::StringRef ReplacementText) { + const std::pair<FileID, unsigned> DecomposedLocation = + Sources.getDecomposedLoc(Start); + const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); + this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation; + this->Offset = DecomposedLocation.second; + this->Length = Length; + this->ReplacementText = ReplacementText; +} + +// FIXME: This should go into the Lexer, but we need to figure out how +// to handle ranges for refactoring in general first - there is no obvious +// good way how to integrate this into the Lexer yet. +static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) { + SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); + SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); + std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); + std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); + if (Start.first != End.first) return -1; + if (Range.isTokenRange()) + End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, + LangOptions()); + return End.second - Start.second; +} + +void Replacement::setFromSourceRange(SourceManager &Sources, + const CharSourceRange &Range, + llvm::StringRef ReplacementText) { + setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), + getRangeSize(Sources, Range), ReplacementText); +} + +bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) { + bool Result = true; + for (Replacements::const_iterator I = Replaces.begin(), + E = Replaces.end(); + I != E; ++I) { + if (I->isApplicable()) { + Result = I->apply(Rewrite) && Result; + } else { + Result = false; + } + } + return Result; +} + +bool saveRewrittenFiles(Rewriter &Rewrite) { + for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), + E = Rewrite.buffer_end(); + I != E; ++I) { + // FIXME: This code is copied from the FixItRewriter.cpp - I think it should + // go into directly into Rewriter (there we also have the Diagnostics to + // handle the error cases better). + const FileEntry *Entry = + Rewrite.getSourceMgr().getFileEntryForID(I->first); + std::string ErrorInfo; + llvm::raw_fd_ostream FileStream( + Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); + if (!ErrorInfo.empty()) + return false; + I->second.write(FileStream); + FileStream.flush(); + } + return true; +} + +RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, + ArrayRef<std::string> SourcePaths) + : Tool(Compilations, SourcePaths) {} + +Replacements &RefactoringTool::getReplacements() { return Replace; } + +int RefactoringTool::run(FrontendActionFactory *ActionFactory) { + int Result = Tool.run(ActionFactory); + LangOptions DefaultLangOptions; + DiagnosticOptions DefaultDiagnosticOptions; + TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), + DefaultDiagnosticOptions); + DiagnosticsEngine Diagnostics( + llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), + &DiagnosticPrinter, false); + SourceManager Sources(Diagnostics, Tool.getFiles()); + Rewriter Rewrite(Sources, DefaultLangOptions); + if (!applyAllReplacements(Replace, Rewrite)) { + llvm::errs() << "Skipped some replacements.\n"; + } + if (!saveRewrittenFiles(Rewrite)) { + llvm::errs() << "Could not save rewritten files.\n"; + return 1; + } + return Result; +} + +} // end namespace tooling +} // end namespace clang diff --git a/lib/Tooling/RefactoringCallbacks.cpp b/lib/Tooling/RefactoringCallbacks.cpp new file mode 100644 index 000000000000..4de125ec02aa --- /dev/null +++ b/lib/Tooling/RefactoringCallbacks.cpp @@ -0,0 +1,81 @@ +//===--- RefactoringCallbacks.cpp - Structural query framework ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/RefactoringCallbacks.h" + +namespace clang { +namespace tooling { + +RefactoringCallback::RefactoringCallback() {} +tooling::Replacements &RefactoringCallback::getReplacements() { + return Replace; +} + +static Replacement replaceStmtWithText(SourceManager &Sources, + const Stmt &From, + StringRef Text) { + return tooling::Replacement(Sources, CharSourceRange::getTokenRange( + From.getSourceRange()), Text); +} +static Replacement replaceStmtWithStmt(SourceManager &Sources, + const Stmt &From, + const Stmt &To) { + return replaceStmtWithText(Sources, From, Lexer::getSourceText( + CharSourceRange::getTokenRange(To.getSourceRange()), + Sources, LangOptions())); +} + +ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText) + : FromId(FromId), ToText(ToText) {} + +void ReplaceStmtWithText::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) { + Replace.insert(tooling::Replacement( + *Result.SourceManager, + CharSourceRange::getTokenRange(FromMatch->getSourceRange()), + ToText)); + } +} + +ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId) + : FromId(FromId), ToId(ToId) {} + +void ReplaceStmtWithStmt::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId); + const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId); + if (FromMatch && ToMatch) + Replace.insert(replaceStmtWithStmt( + *Result.SourceManager, *FromMatch, *ToMatch)); +} + +ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id, + bool PickTrueBranch) + : Id(Id), PickTrueBranch(PickTrueBranch) {} + +void ReplaceIfStmtWithItsBody::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) { + const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse(); + if (Body) { + Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body)); + } else if (!PickTrueBranch) { + // If we want to use the 'else'-branch, but it doesn't exist, delete + // the whole 'if'. + Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, "")); + } + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index fa2374f5e30d..e93e0c97f710 100644 --- a/lib/Tooling/Tooling.cpp +++ b/lib/Tooling/Tooling.cpp @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/Tooling.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "llvm/ADT/STLExtras.h" @@ -26,6 +26,13 @@ #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" +// For chdir, see the comment in ClangTool::run for more information. +#ifdef _WIN32 +# include <direct.h> +#else +# include <unistd.h> +#endif + namespace clang { namespace tooling { @@ -40,8 +47,8 @@ static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics, const char *BinaryName) { const std::string DefaultOutputName = "a.out"; clang::driver::Driver *CompilerDriver = new clang::driver::Driver( - BinaryName, llvm::sys::getDefaultTargetTriple(), - DefaultOutputName, false, *Diagnostics); + BinaryName, llvm::sys::getDefaultTargetTriple(), + DefaultOutputName, false, *Diagnostics); CompilerDriver->setTitle("clang_based_tool"); return CompilerDriver; } @@ -108,29 +115,26 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, return Invocation.run(); } -/// \brief Returns the absolute path of 'File', by prepending it with -/// 'BaseDirectory' if 'File' is not absolute. -/// -/// Otherwise returns 'File'. -/// If 'File' starts with "./", the returned path will not contain the "./". -/// Otherwise, the returned path will contain the literal path-concatenation of -/// 'BaseDirectory' and 'File'. -/// -/// \param File Either an absolute or relative path. -/// \param BaseDirectory An absolute path. -static std::string getAbsolutePath( - StringRef File, StringRef BaseDirectory) { - assert(llvm::sys::path::is_absolute(BaseDirectory)); +std::string getAbsolutePath(StringRef File) { + llvm::SmallString<1024> BaseDirectory; + if (const char *PWD = ::getenv("PWD")) + BaseDirectory = PWD; + else + llvm::sys::fs::current_path(BaseDirectory); + SmallString<1024> PathStorage; if (llvm::sys::path::is_absolute(File)) { - return File; + llvm::sys::path::native(File, PathStorage); + return PathStorage.str(); } StringRef RelativePath(File); + // FIXME: Should '.\\' be accepted on Win32? if (RelativePath.startswith("./")) { RelativePath = RelativePath.substr(strlen("./")); } llvm::SmallString<1024> AbsolutePath(BaseDirectory); llvm::sys::path::append(AbsolutePath, RelativePath); - return AbsolutePath.str(); + llvm::sys::path::native(Twine(AbsolutePath), PathStorage); + return PathStorage.str(); } ToolInvocation::ToolInvocation( @@ -140,7 +144,9 @@ ToolInvocation::ToolInvocation( } void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) { - MappedFileContents[FilePath] = Content; + SmallString<1024> PathStorage; + llvm::sys::path::native(FilePath, PathStorage); + MappedFileContents[PathStorage] = Content; } bool ToolInvocation::run() { @@ -167,20 +173,15 @@ bool ToolInvocation::run() { } llvm::OwningPtr<clang::CompilerInvocation> Invocation( newInvocation(&Diagnostics, *CC1Args)); - return runInvocation(BinaryName, Compilation.get(), - Invocation.take(), *CC1Args, ToolAction.take()); + return runInvocation(BinaryName, Compilation.get(), Invocation.take(), + *CC1Args); } -// Exists solely for the purpose of lookup of the resource path. -static int StaticSymbol; - bool ToolInvocation::runInvocation( const char *BinaryName, clang::driver::Compilation *Compilation, clang::CompilerInvocation *Invocation, - const clang::driver::ArgStringList &CC1Args, - clang::FrontendAction *ToolAction) { - llvm::OwningPtr<clang::FrontendAction> ScopedToolAction(ToolAction); + const clang::driver::ArgStringList &CC1Args) { // Show the invocation, with -v. if (Invocation->getHeaderSearchOpts().Verbose) { llvm::errs() << "clang Invocation:\n"; @@ -194,6 +195,11 @@ bool ToolInvocation::runInvocation( Compiler.setFileManager(Files); // FIXME: What about LangOpts? + // ToolAction can have lifetime requirements for Compiler or its members, and + // we need to ensure it's deleted earlier than Compiler. So we pass it to an + // OwningPtr declared after the Compiler variable. + llvm::OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take()); + // Create the compilers actual diagnostics engine. Compiler.createDiagnostics(CC1Args.size(), const_cast<char**>(CC1Args.data())); @@ -203,18 +209,10 @@ bool ToolInvocation::runInvocation( Compiler.createSourceManager(*Files); addFileMappingsTo(Compiler.getSourceManager()); - // Infer the builtin include path if unspecified. - if (Compiler.getHeaderSearchOpts().UseBuiltinIncludes && - Compiler.getHeaderSearchOpts().ResourceDir.empty()) { - // This just needs to be some symbol in the binary. - void *const SymbolAddr = &StaticSymbol; - Compiler.getHeaderSearchOpts().ResourceDir = - clang::CompilerInvocation::GetResourcesPath(BinaryName, SymbolAddr); - } - - const bool Success = Compiler.ExecuteAction(*ToolAction); + const bool Success = Compiler.ExecuteAction(*ScopedToolAction); Compiler.resetAndLeakFileManager(); + Files->clearStatCaches(); return Success; } @@ -228,35 +226,23 @@ void ToolInvocation::addFileMappingsTo(SourceManager &Sources) { // FIXME: figure out what '0' stands for. const FileEntry *FromFile = Files->getVirtualFile( It->getKey(), Input->getBufferSize(), 0); - // FIXME: figure out memory management ('true'). - Sources.overrideFileContents(FromFile, Input, true); + Sources.overrideFileContents(FromFile, Input); } } ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) - : Files((FileSystemOptions())) { - llvm::SmallString<1024> BaseDirectory; - if (const char *PWD = ::getenv("PWD")) - BaseDirectory = PWD; - else - llvm::sys::fs::current_path(BaseDirectory); + : Files((FileSystemOptions())), + ArgsAdjuster(new ClangSyntaxOnlyAdjuster()) { for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) { - llvm::SmallString<1024> File(getAbsolutePath( - SourcePaths[I], BaseDirectory)); + llvm::SmallString<1024> File(getAbsolutePath(SourcePaths[I])); - std::vector<CompileCommand> CompileCommands = + std::vector<CompileCommand> CompileCommandsForFile = Compilations.getCompileCommands(File.str()); - if (!CompileCommands.empty()) { - for (int I = 0, E = CompileCommands.size(); I != E; ++I) { - CompileCommand &Command = CompileCommands[I]; - if (!Command.Directory.empty()) { - // FIXME: What should happen if CommandLine includes -working-directory - // as well? - Command.CommandLine.push_back( - "-working-directory=" + Command.Directory); - } - CommandLines.push_back(std::make_pair(File.str(), Command.CommandLine)); + if (!CompileCommandsForFile.empty()) { + for (int I = 0, E = CompileCommandsForFile.size(); I != E; ++I) { + CompileCommands.push_back(std::make_pair(File.str(), + CompileCommandsForFile[I])); } } else { // FIXME: There are two use cases here: doing a fuzzy @@ -273,11 +259,39 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { MappedFileContents.push_back(std::make_pair(FilePath, Content)); } +void ClangTool::setArgumentsAdjuster(ArgumentsAdjuster *Adjuster) { + ArgsAdjuster.reset(Adjuster); +} + int ClangTool::run(FrontendActionFactory *ActionFactory) { + // Exists solely for the purpose of lookup of the resource path. + // This just needs to be some symbol in the binary. + static int StaticSymbol; + // The driver detects the builtin header path based on the path of the + // executable. + // FIXME: On linux, GetMainExecutable is independent of the value of the + // first argument, thus allowing ClangTool and runToolOnCode to just + // pass in made-up names here. Make sure this works on other platforms. + std::string MainExecutable = + llvm::sys::Path::GetMainExecutable("clang_tool", &StaticSymbol).str(); + bool ProcessingFailed = false; - for (unsigned I = 0; I < CommandLines.size(); ++I) { - std::string File = CommandLines[I].first; - std::vector<std::string> &CommandLine = CommandLines[I].second; + for (unsigned I = 0; I < CompileCommands.size(); ++I) { + std::string File = CompileCommands[I].first; + // FIXME: chdir is thread hostile; on the other hand, creating the same + // behavior as chdir is complex: chdir resolves the path once, thus + // guaranteeing that all subsequent relative path operations work + // on the same path the original chdir resulted in. This makes a difference + // for example on network filesystems, where symlinks might be switched + // during runtime of the tool. Fixing this depends on having a file system + // abstraction that allows openat() style interactions. + if (chdir(CompileCommands[I].second.Directory.c_str())) + llvm::report_fatal_error("Cannot chdir into \"" + + CompileCommands[I].second.Directory + "\n!"); + std::vector<std::string> CommandLine = + ArgsAdjuster->Adjust(CompileCommands[I].second.CommandLine); + assert(!CommandLine.empty()); + CommandLine[0] = MainExecutable; llvm::outs() << "Processing: " << File << ".\n"; ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files); for (int I = 0, E = MappedFileContents.size(); I != E; ++I) { |